mirror of
https://github.com/ckaczor/WorldClockStatusWindow.git
synced 2026-01-14 10:00:12 -05:00
Compare commits
7 Commits
v2024.9.8
...
v2024.10.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 50fd78a3c3 | |||
| 4751ae476d | |||
| 6d4d33c82f | |||
| fe0b0895b4 | |||
| 978873a58a | |||
| 733a3de573 | |||
| 5631bc87be |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Publish Application
|
||||
run: dotnet publish WorldClockStatusWindow.csproj -c Release -o publish
|
||||
run: dotnet publish WorldClockStatusWindow.csproj -c Debug -o publish
|
||||
|
||||
- name: Create Velopack Release
|
||||
run: |
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
<setting name="TimeZones" serializeAs="String">
|
||||
<value>[]</value>
|
||||
</setting>
|
||||
<setting name="CheckVersionAtStartup" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
</WorldClockStatusWindow.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
21
Data.cs
Normal file
21
Data.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using WorldClockStatusWindow.Properties;
|
||||
|
||||
namespace WorldClockStatusWindow;
|
||||
|
||||
internal static class Data
|
||||
{
|
||||
internal static ObservableCollection<TimeZoneEntry> TimeZoneEntries { get; set; }
|
||||
|
||||
internal static void Load()
|
||||
{
|
||||
TimeZoneEntries = JsonSerializer.Deserialize<ObservableCollection<TimeZoneEntry>>(Settings.Default.TimeZones);
|
||||
}
|
||||
|
||||
internal static void Save()
|
||||
{
|
||||
Settings.Default.TimeZones = JsonSerializer.Serialize(TimeZoneEntries);
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
42
DataErrorDictionary.cs
Normal file
42
DataErrorDictionary.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace WorldClockStatusWindow;
|
||||
|
||||
internal class DataErrorDictionary : Dictionary<string, List<string>>
|
||||
{
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
private void OnErrorsChanged(string propertyName)
|
||||
{
|
||||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public IEnumerable GetErrors(string propertyName)
|
||||
{
|
||||
return TryGetValue(propertyName, out var value) ? value : null;
|
||||
}
|
||||
|
||||
public void AddError(string propertyName, string error)
|
||||
{
|
||||
if (!ContainsKey(propertyName))
|
||||
this[propertyName] = [];
|
||||
|
||||
if (this[propertyName].Contains(error))
|
||||
return;
|
||||
|
||||
this[propertyName].Add(error);
|
||||
OnErrorsChanged(propertyName);
|
||||
}
|
||||
|
||||
public void ClearErrors(string propertyName)
|
||||
{
|
||||
if (!ContainsKey(propertyName))
|
||||
return;
|
||||
|
||||
Remove(propertyName);
|
||||
OnErrorsChanged(propertyName);
|
||||
}
|
||||
}
|
||||
313
Properties/Resources.Designer.cs
generated
313
Properties/Resources.Designer.cs
generated
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34014
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -19,10 +19,10 @@ namespace WorldClockStatusWindow.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace WorldClockStatusWindow.Properties {
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorldClockStatusWindow.Properties.Resources", typeof(Resources).Assembly);
|
||||
@@ -51,7 +51,7 @@ namespace WorldClockStatusWindow.Properties {
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -60,14 +60,315 @@ namespace WorldClockStatusWindow.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add.
|
||||
/// </summary>
|
||||
public static string AddTimeZoneLink {
|
||||
get {
|
||||
return ResourceManager.GetString("AddTimeZoneLink", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Time Zone.
|
||||
/// </summary>
|
||||
public static string AddTimeZoneToolTip {
|
||||
get {
|
||||
return ResourceManager.GetString("AddTimeZoneToolTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
internal static System.Drawing.Icon ApplicationIcon {
|
||||
public static System.Drawing.Icon ApplicationIcon {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ApplicationIcon", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to World Clock Status Window.
|
||||
/// </summary>
|
||||
public static string ApplicationName {
|
||||
get {
|
||||
return ResourceManager.GetString("ApplicationName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// </summary>
|
||||
public static string CancelButton {
|
||||
get {
|
||||
return ResourceManager.GetString("CancelButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Checking for update....
|
||||
/// </summary>
|
||||
public static string CheckingForUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("CheckingForUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Check for Update.
|
||||
/// </summary>
|
||||
public static string CheckUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("CheckUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Check _Now.
|
||||
/// </summary>
|
||||
public static string checkVersionNowButton {
|
||||
get {
|
||||
return ResourceManager.GetString("checkVersionNowButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Check for a new version on startup.
|
||||
/// </summary>
|
||||
public static string checkVersionOnStartupCheckBox {
|
||||
get {
|
||||
return ResourceManager.GetString("checkVersionOnStartupCheckBox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close.
|
||||
/// </summary>
|
||||
public static string CloseButtonText {
|
||||
get {
|
||||
return ResourceManager.GetString("CloseButtonText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to delete the selected time zones?.
|
||||
/// </summary>
|
||||
public static string ConfirmDeleteTimeZones {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfirmDeleteTimeZones", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Confirm Delete.
|
||||
/// </summary>
|
||||
public static string ConfirmDeleteTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfirmDeleteTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete.
|
||||
/// </summary>
|
||||
public static string DeleteTimeZoneLink {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteTimeZoneLink", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete Time Zone.
|
||||
/// </summary>
|
||||
public static string DeleteTimeZoneToolTip {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteTimeZoneToolTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Downloading update....
|
||||
/// </summary>
|
||||
public static string DownloadingUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("DownloadingUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit.
|
||||
/// </summary>
|
||||
public static string EditTimeZoneLink {
|
||||
get {
|
||||
return ResourceManager.GetString("EditTimeZoneLink", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Time Zone.
|
||||
/// </summary>
|
||||
public static string EditTimeZoneToolTip {
|
||||
get {
|
||||
return ResourceManager.GetString("EditTimeZoneToolTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Installing update....
|
||||
/// </summary>
|
||||
public static string InstallingUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("InstallingUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Label.
|
||||
/// </summary>
|
||||
public static string LabelColumnHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelColumnHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Loading....
|
||||
/// </summary>
|
||||
public static string Loading {
|
||||
get {
|
||||
return ResourceManager.GetString("Loading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
public static string OkayButton {
|
||||
get {
|
||||
return ResourceManager.GetString("OkayButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to About.
|
||||
/// </summary>
|
||||
public static string optionCategoryAbout {
|
||||
get {
|
||||
return ResourceManager.GetString("optionCategoryAbout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to General.
|
||||
/// </summary>
|
||||
public static string optionCategoryGeneral {
|
||||
get {
|
||||
return ResourceManager.GetString("optionCategoryGeneral", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Time Zones.
|
||||
/// </summary>
|
||||
public static string optionCategoryTimeZones {
|
||||
get {
|
||||
return ResourceManager.GetString("optionCategoryTimeZones", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update.
|
||||
/// </summary>
|
||||
public static string optionCategoryUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("optionCategoryUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Settings.
|
||||
/// </summary>
|
||||
public static string SettingsTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingsTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Start when Windows starts.
|
||||
/// </summary>
|
||||
public static string startWithWindowsCheckBox {
|
||||
get {
|
||||
return ResourceManager.GetString("startWithWindowsCheckBox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Time Zone.
|
||||
/// </summary>
|
||||
public static string TimeZoneColumnHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("TimeZoneColumnHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Time Zone.
|
||||
/// </summary>
|
||||
public static string TimeZoneWindowAdd {
|
||||
get {
|
||||
return ResourceManager.GetString("TimeZoneWindowAdd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Time Zone.
|
||||
/// </summary>
|
||||
public static string TimeZoneWindowEdit {
|
||||
get {
|
||||
return ResourceManager.GetString("TimeZoneWindowEdit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You are already running the most recent version.
|
||||
///
|
||||
///No updates are available at this time..
|
||||
/// </summary>
|
||||
public static string UpdateCheckCurrent {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateCheckCurrent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Version {0} is now available.
|
||||
///
|
||||
///Would you like to download and install it now?.
|
||||
/// </summary>
|
||||
public static string UpdateCheckNewVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateCheckNewVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} Update.
|
||||
/// </summary>
|
||||
public static string UpdateCheckTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateCheckTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Version {0}.
|
||||
/// </summary>
|
||||
public static string Version {
|
||||
get {
|
||||
return ResourceManager.GetString("Version", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +121,107 @@
|
||||
<data name="ApplicationIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Application.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="ApplicationName" xml:space="preserve">
|
||||
<value>World Clock Status Window</value>
|
||||
</data>
|
||||
<data name="optionCategoryAbout" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="CheckUpdate" xml:space="preserve">
|
||||
<value>_Check for Update</value>
|
||||
</data>
|
||||
<data name="startWithWindowsCheckBox" xml:space="preserve">
|
||||
<value>_Start when Windows starts</value>
|
||||
</data>
|
||||
<data name="optionCategoryGeneral" xml:space="preserve">
|
||||
<value>General</value>
|
||||
</data>
|
||||
<data name="SettingsTitle" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="CloseButtonText" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
<data name="Version" xml:space="preserve">
|
||||
<value>Version {0}</value>
|
||||
</data>
|
||||
<data name="optionCategoryUpdate" xml:space="preserve">
|
||||
<value>Update</value>
|
||||
</data>
|
||||
<data name="checkVersionOnStartupCheckBox" xml:space="preserve">
|
||||
<value>_Check for a new version on startup</value>
|
||||
</data>
|
||||
<data name="checkVersionNowButton" xml:space="preserve">
|
||||
<value>Check _Now</value>
|
||||
</data>
|
||||
<data name="UpdateCheckTitle" xml:space="preserve">
|
||||
<value>{0} Update</value>
|
||||
</data>
|
||||
<data name="UpdateCheckCurrent" xml:space="preserve">
|
||||
<value>You are already running the most recent version.
|
||||
|
||||
No updates are available at this time.</value>
|
||||
</data>
|
||||
<data name="UpdateCheckNewVersion" xml:space="preserve">
|
||||
<value>Version {0} is now available.
|
||||
|
||||
Would you like to download and install it now?</value>
|
||||
</data>
|
||||
<data name="Loading" xml:space="preserve">
|
||||
<value>Loading...</value>
|
||||
</data>
|
||||
<data name="CheckingForUpdate" xml:space="preserve">
|
||||
<value>Checking for update...</value>
|
||||
</data>
|
||||
<data name="DownloadingUpdate" xml:space="preserve">
|
||||
<value>Downloading update...</value>
|
||||
</data>
|
||||
<data name="InstallingUpdate" xml:space="preserve">
|
||||
<value>Installing update...</value>
|
||||
</data>
|
||||
<data name="optionCategoryTimeZones" xml:space="preserve">
|
||||
<value>Time Zones</value>
|
||||
</data>
|
||||
<data name="LabelColumnHeader" xml:space="preserve">
|
||||
<value>Label</value>
|
||||
</data>
|
||||
<data name="TimeZoneColumnHeader" xml:space="preserve">
|
||||
<value>Time Zone</value>
|
||||
</data>
|
||||
<data name="AddTimeZoneLink" xml:space="preserve">
|
||||
<value>Add</value>
|
||||
</data>
|
||||
<data name="EditTimeZoneLink" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="DeleteTimeZoneLink" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="AddTimeZoneToolTip" xml:space="preserve">
|
||||
<value>Add Time Zone</value>
|
||||
</data>
|
||||
<data name="EditTimeZoneToolTip" xml:space="preserve">
|
||||
<value>Edit Time Zone</value>
|
||||
</data>
|
||||
<data name="DeleteTimeZoneToolTip" xml:space="preserve">
|
||||
<value>Delete Time Zone</value>
|
||||
</data>
|
||||
<data name="ConfirmDeleteTitle" xml:space="preserve">
|
||||
<value>Confirm Delete</value>
|
||||
</data>
|
||||
<data name="ConfirmDeleteTimeZones" xml:space="preserve">
|
||||
<value>Are you sure you want to delete the selected time zones?</value>
|
||||
</data>
|
||||
<data name="TimeZoneWindowAdd" xml:space="preserve">
|
||||
<value>Add Time Zone</value>
|
||||
</data>
|
||||
<data name="TimeZoneWindowEdit" xml:space="preserve">
|
||||
<value>Edit Time Zone</value>
|
||||
</data>
|
||||
<data name="OkayButton" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
<data name="CancelButton" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
</root>
|
||||
12
Properties/Settings.Designer.cs
generated
12
Properties/Settings.Designer.cs
generated
@@ -70,5 +70,17 @@ namespace WorldClockStatusWindow.Properties {
|
||||
this["TimeZones"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool CheckVersionAtStartup {
|
||||
get {
|
||||
return ((bool)(this["CheckVersionAtStartup"]));
|
||||
}
|
||||
set {
|
||||
this["CheckVersionAtStartup"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,8 @@
|
||||
<Setting Name="TimeZones" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">[]</Value>
|
||||
</Setting>
|
||||
<Setting Name="CheckVersionAtStartup" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
21
SettingsWindow/AboutSettingsPanel.xaml
Normal file
21
SettingsWindow/AboutSettingsPanel.xaml
Normal file
@@ -0,0 +1,21 @@
|
||||
<windows:CategoryPanelBase x:Class="WorldClockStatusWindow.SettingsWindow.AboutSettingsPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:properties="clr-namespace:WorldClockStatusWindow.Properties"
|
||||
xmlns:worldClockStatusWindow="clr-namespace:WorldClockStatusWindow"
|
||||
xmlns:windows="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.CategoryWindow"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="150"
|
||||
d:DesignWidth="300">
|
||||
<Grid>
|
||||
<StackPanel windows:Spacing.Vertical="10">
|
||||
<TextBlock Text="{x:Static properties:Resources.ApplicationName}"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding Source={x:Static worldClockStatusWindow:UpdateCheck.LocalVersion}, StringFormat={x:Static properties:Resources.Version}}"
|
||||
Name="VersionLabel" />
|
||||
<TextBlock Text="Chris Kaczor" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:CategoryPanelBase>
|
||||
12
SettingsWindow/AboutSettingsPanel.xaml.cs
Normal file
12
SettingsWindow/AboutSettingsPanel.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace WorldClockStatusWindow.SettingsWindow
|
||||
{
|
||||
public partial class AboutSettingsPanel
|
||||
{
|
||||
public AboutSettingsPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string CategoryName => Properties.Resources.optionCategoryAbout;
|
||||
}
|
||||
}
|
||||
16
SettingsWindow/GeneralSettingsPanel.xaml
Normal file
16
SettingsWindow/GeneralSettingsPanel.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<windows:CategoryPanelBase x:Class="WorldClockStatusWindow.SettingsWindow.GeneralSettingsPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:properties="clr-namespace:WorldClockStatusWindow.Properties"
|
||||
xmlns:windows="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.CategoryWindow"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300">
|
||||
<StackPanel windows:Spacing.Vertical="10">
|
||||
<CheckBox Content="{x:Static properties:Resources.startWithWindowsCheckBox}"
|
||||
IsChecked="{Binding Source={x:Static properties:Settings.Default}, Path=AutoStart}"
|
||||
Click="OnSaveSettings" />
|
||||
</StackPanel>
|
||||
</windows:CategoryPanelBase>
|
||||
36
SettingsWindow/GeneralSettingsPanel.xaml.cs
Normal file
36
SettingsWindow/GeneralSettingsPanel.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using ChrisKaczor.Wpf.Application;
|
||||
using System.Windows;
|
||||
using WorldClockStatusWindow.Properties;
|
||||
|
||||
namespace WorldClockStatusWindow.SettingsWindow;
|
||||
|
||||
public partial class GeneralSettingsPanel
|
||||
{
|
||||
public GeneralSettingsPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string CategoryName => Properties.Resources.optionCategoryGeneral;
|
||||
|
||||
public override void LoadPanel(Window parentWindow)
|
||||
{
|
||||
base.LoadPanel(parentWindow);
|
||||
|
||||
MarkLoaded();
|
||||
}
|
||||
|
||||
private void OnSaveSettings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
if (!HasLoaded) return;
|
||||
|
||||
Settings.Default.Save();
|
||||
|
||||
Application.Current.SetStartWithWindows(Settings.Default.AutoStart);
|
||||
}
|
||||
}
|
||||
83
SettingsWindow/TimeZoneWindow.xaml
Normal file
83
SettingsWindow/TimeZoneWindow.xaml
Normal file
@@ -0,0 +1,83 @@
|
||||
<Window x:Class="WorldClockStatusWindow.SettingsWindow.TimeZoneWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:properties="clr-namespace:WorldClockStatusWindow.Properties"
|
||||
xmlns:worldClockStatusWindow="clr-namespace:WorldClockStatusWindow"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
d:DataContext="{d:DesignInstance Type=worldClockStatusWindow:TimeZoneEntry}"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||
xmlns:windows="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.CategoryWindow"
|
||||
mc:Ignorable="d"
|
||||
Title="TimeZoneWindow"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="Height"
|
||||
Width="450"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/WorldClockStatusWindow;component/Resources/Application.ico"
|
||||
FocusManager.FocusedElement="{Binding ElementName=LabelTextBox}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid Margin="6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Margin="0,4"
|
||||
windows:Spacing.Vertical="8">
|
||||
<TextBox Name="LabelTextBox"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.LabelColumnHeader}"
|
||||
mah:TextBoxHelper.SelectAllOnFocus="True"
|
||||
Text="{Binding Path=Label, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}" />
|
||||
<ComboBox Name="TimeZoneComboBox"
|
||||
DisplayMemberPath="DisplayName"
|
||||
SelectedValuePath="Id"
|
||||
SelectedValue="{Binding Path=TimeZoneId}"
|
||||
VirtualizingPanel.IsVirtualizing="False"
|
||||
mah:TextBoxHelper.UseFloatingWatermark="True"
|
||||
mah:TextBoxHelper.Watermark="{x:Static properties:Resources.TimeZoneColumnHeader}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal"
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Content="{x:Static properties:Resources.OkayButton}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="75"
|
||||
Margin="0,0,5,0"
|
||||
IsDefault="True"
|
||||
Click="HandleOkayButtonClick">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text.Length, ElementName=LabelTextBox}"
|
||||
Value="0">
|
||||
<Setter Property="IsEnabled"
|
||||
Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
<Button Content="{x:Static properties:Resources.CancelButton}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="75"
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
43
SettingsWindow/TimeZoneWindow.xaml.cs
Normal file
43
SettingsWindow/TimeZoneWindow.xaml.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using ChrisKaczor.Wpf.Validation;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace WorldClockStatusWindow.SettingsWindow;
|
||||
|
||||
public partial class TimeZoneWindow
|
||||
{
|
||||
public TimeZoneWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool? Display(TimeZoneEntry timeZoneEntry, Window owner)
|
||||
{
|
||||
DataContext = timeZoneEntry;
|
||||
|
||||
TimeZoneComboBox.ItemsSource = TimeZoneInfo.GetSystemTimeZones();
|
||||
|
||||
Title = string.IsNullOrWhiteSpace(timeZoneEntry.Label) ? Properties.Resources.TimeZoneWindowAdd : Properties.Resources.TimeZoneWindowEdit;
|
||||
|
||||
Owner = owner;
|
||||
|
||||
return ShowDialog();
|
||||
}
|
||||
|
||||
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!this.IsValid())
|
||||
return;
|
||||
|
||||
var timeZoneEntry = (TimeZoneEntry)DataContext;
|
||||
|
||||
if (!Data.TimeZoneEntries.Contains(timeZoneEntry))
|
||||
Data.TimeZoneEntries.Add(timeZoneEntry);
|
||||
|
||||
Data.Save();
|
||||
|
||||
DialogResult = true;
|
||||
|
||||
Close();
|
||||
}
|
||||
}
|
||||
90
SettingsWindow/TimeZonesSettingsPanel.xaml
Normal file
90
SettingsWindow/TimeZonesSettingsPanel.xaml
Normal file
@@ -0,0 +1,90 @@
|
||||
<windows:CategoryPanelBase x:Class="WorldClockStatusWindow.SettingsWindow.TimeZonesSettingsPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:properties="clr-namespace:WorldClockStatusWindow.Properties"
|
||||
xmlns:worldClockStatusWindow="clr-namespace:WorldClockStatusWindow"
|
||||
xmlns:windows="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.CategoryWindow"
|
||||
xmlns:controls="clr-namespace:ChrisKaczor.Wpf.Controls;assembly=ChrisKaczor.Wpf.Controls.Link"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="150"
|
||||
d:DesignWidth="300">
|
||||
<windows:CategoryPanelBase.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.FlatButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/light.cobalt.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</windows:CategoryPanelBase.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<DataGrid Name="TimeZoneDataGrid"
|
||||
SelectionMode="Extended"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
AutoGenerateColumns="False"
|
||||
GridLinesVisibility="None"
|
||||
CanUserResizeRows="False"
|
||||
IsReadOnly="True"
|
||||
SelectionUnit="FullRow"
|
||||
HeadersVisibility="Column"
|
||||
BorderThickness="1,1,1,1"
|
||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
|
||||
Background="{x:Null}"
|
||||
SelectionChanged="HandleTimeZoneDataGridSelectionChanged"
|
||||
d:DataContext="{d:DesignInstance worldClockStatusWindow:TimeZoneEntry }">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Path=Label}"
|
||||
Header="{x:Static properties:Resources.LabelColumnHeader}"
|
||||
SortDirection="Ascending"
|
||||
Width="*" />
|
||||
<DataGridTextColumn Binding="{Binding TimeZoneId}"
|
||||
Header="{x:Static properties:Resources.TimeZoneColumnHeader}"
|
||||
Width="*" />
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow"
|
||||
BasedOn="{StaticResource MahApps.Styles.DataGridRow}">
|
||||
<EventSetter Event="MouseDoubleClick"
|
||||
Handler="HandleTimeZoneDataGridRowMouseDoubleClick" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
<Border Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
BorderThickness="1,0,1,1"
|
||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||
<controls:Link Name="AddTimeZoneButton"
|
||||
Margin="2"
|
||||
Click="HandleAddTimeZoneButtonClick"
|
||||
Text="{x:Static properties:Resources.AddTimeZoneLink}"
|
||||
ToolTip="{x:Static properties:Resources.AddTimeZoneToolTip}">
|
||||
</controls:Link>
|
||||
<controls:Link Name="EditTimeZoneButton"
|
||||
Margin="2"
|
||||
Click="HandleEditTimeZoneButtonClick"
|
||||
Text="{x:Static properties:Resources.EditTimeZoneLink}"
|
||||
ToolTip="{x:Static properties:Resources.EditTimeZoneToolTip}">
|
||||
</controls:Link>
|
||||
<controls:Link Name="DeleteTimeZoneButton"
|
||||
Margin="2"
|
||||
Click="HandleDeleteTimeZoneButtonClick"
|
||||
Text="{x:Static properties:Resources.DeleteTimeZoneLink}"
|
||||
ToolTip="{x:Static properties:Resources.DeleteTimeZoneToolTip}">
|
||||
</controls:Link>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</windows:CategoryPanelBase>
|
||||
115
SettingsWindow/TimeZonesSettingsPanel.xaml.cs
Normal file
115
SettingsWindow/TimeZonesSettingsPanel.xaml.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace WorldClockStatusWindow.SettingsWindow;
|
||||
|
||||
public partial class TimeZonesSettingsPanel
|
||||
{
|
||||
private CollectionViewSource _collectionViewSource;
|
||||
|
||||
public TimeZonesSettingsPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string CategoryName => Properties.Resources.optionCategoryTimeZones;
|
||||
|
||||
public override void LoadPanel(Window parentWindow)
|
||||
{
|
||||
base.LoadPanel(parentWindow);
|
||||
|
||||
if (_collectionViewSource == null)
|
||||
{
|
||||
_collectionViewSource = new CollectionViewSource { Source = Data.TimeZoneEntries };
|
||||
_collectionViewSource.SortDescriptions.Add(new SortDescription("Label", ListSortDirection.Ascending));
|
||||
|
||||
TimeZoneDataGrid.ItemsSource = _collectionViewSource.View;
|
||||
}
|
||||
|
||||
_collectionViewSource.View.Refresh();
|
||||
|
||||
if (TimeZoneDataGrid.Items.Count > 0)
|
||||
TimeZoneDataGrid.SelectedIndex = 0;
|
||||
|
||||
SetTimeZoneButtonStates();
|
||||
}
|
||||
|
||||
private void HandleTimeZoneDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
SetTimeZoneButtonStates();
|
||||
}
|
||||
|
||||
private void SetTimeZoneButtonStates()
|
||||
{
|
||||
AddTimeZoneButton.IsEnabled = true;
|
||||
EditTimeZoneButton.IsEnabled = TimeZoneDataGrid.SelectedItems.Count == 1;
|
||||
DeleteTimeZoneButton.IsEnabled = TimeZoneDataGrid.SelectedItems.Count > 0;
|
||||
}
|
||||
|
||||
private void HandleAddTimeZoneButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddTimeZone();
|
||||
}
|
||||
|
||||
private void HandleEditTimeZoneButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EditSelectedTimeZone();
|
||||
}
|
||||
|
||||
private void HandleDeleteTimeZoneButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DeleteSelectedTimeZones();
|
||||
}
|
||||
|
||||
private void HandleTimeZoneDataGridRowMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
EditSelectedTimeZone();
|
||||
}
|
||||
|
||||
private void AddTimeZone()
|
||||
{
|
||||
var timeZoneEntry = new TimeZoneEntry { TimeZoneId = TimeZoneInfo.Local.Id };
|
||||
|
||||
var timeZoneWindow = new TimeZoneWindow();
|
||||
|
||||
var result = timeZoneWindow.Display(timeZoneEntry, Window.GetWindow(this));
|
||||
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
TimeZoneDataGrid.SelectedItem = timeZoneEntry;
|
||||
|
||||
SetTimeZoneButtonStates();
|
||||
}
|
||||
|
||||
private void EditSelectedTimeZone()
|
||||
{
|
||||
if (TimeZoneDataGrid.SelectedItem == null)
|
||||
return;
|
||||
|
||||
var timeZoneEntry = (TimeZoneEntry) TimeZoneDataGrid.SelectedItem;
|
||||
|
||||
var timeZoneWindow = new TimeZoneWindow();
|
||||
|
||||
timeZoneWindow.Display(timeZoneEntry, Window.GetWindow(this));
|
||||
}
|
||||
|
||||
private void DeleteSelectedTimeZones()
|
||||
{
|
||||
if (MessageBox.Show(ParentWindow!, Properties.Resources.ConfirmDeleteTimeZones, Properties.Resources.ConfirmDeleteTitle, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
|
||||
return;
|
||||
|
||||
var selectedItems = new TimeZoneEntry[TimeZoneDataGrid.SelectedItems.Count];
|
||||
|
||||
TimeZoneDataGrid.SelectedItems.CopyTo(selectedItems, 0);
|
||||
|
||||
foreach (var timeZoneEntry in selectedItems)
|
||||
Data.TimeZoneEntries.Remove(timeZoneEntry);
|
||||
|
||||
SetTimeZoneButtonStates();
|
||||
}
|
||||
}
|
||||
22
SettingsWindow/UpdateSettingsPanel.xaml
Normal file
22
SettingsWindow/UpdateSettingsPanel.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<windows:CategoryPanelBase x:Class="WorldClockStatusWindow.SettingsWindow.UpdateSettingsPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:properties="clr-namespace:WorldClockStatusWindow.Properties"
|
||||
xmlns:windows="clr-namespace:ChrisKaczor.Wpf.Windows;assembly=ChrisKaczor.Wpf.Windows.CategoryWindow"
|
||||
xmlns:worldClockStatusWindow="clr-namespace:WorldClockStatusWindow"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="150"
|
||||
d:DesignWidth="250">
|
||||
<StackPanel windows:Spacing.Vertical="10">
|
||||
<CheckBox Content="{x:Static properties:Resources.checkVersionOnStartupCheckBox}"
|
||||
Name="CheckVersionOnStartupCheckBox"
|
||||
IsChecked="{Binding Source={x:Static properties:Settings.Default}, Path=CheckVersionAtStartup}"
|
||||
Click="OnSaveSettings" />
|
||||
<Button Content="{x:Static properties:Resources.checkVersionNowButton}"
|
||||
IsEnabled="{Binding Source={x:Static worldClockStatusWindow:UpdateCheck.IsInstalled}}"
|
||||
HorizontalAlignment="Left"
|
||||
Click="HandleCheckVersionNowButtonClick" />
|
||||
</StackPanel>
|
||||
</windows:CategoryPanelBase>
|
||||
38
SettingsWindow/UpdateSettingsPanel.xaml.cs
Normal file
38
SettingsWindow/UpdateSettingsPanel.xaml.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using WorldClockStatusWindow.Properties;
|
||||
|
||||
namespace WorldClockStatusWindow.SettingsWindow;
|
||||
|
||||
public partial class UpdateSettingsPanel
|
||||
{
|
||||
public UpdateSettingsPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public override string CategoryName => Properties.Resources.optionCategoryUpdate;
|
||||
|
||||
private async void HandleCheckVersionNowButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var cursor = Cursor;
|
||||
|
||||
Cursor = Cursors.Wait;
|
||||
|
||||
await UpdateCheck.DisplayUpdateInformation(true);
|
||||
|
||||
Cursor = cursor;
|
||||
}
|
||||
|
||||
private void OnSaveSettings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
if (!HasLoaded) return;
|
||||
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,61 @@
|
||||
namespace WorldClockStatusWindow;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class TimeZoneEntry
|
||||
namespace WorldClockStatusWindow;
|
||||
|
||||
public class TimeZoneEntry : INotifyDataErrorInfo
|
||||
{
|
||||
public string Label { get; set; }
|
||||
private readonly DataErrorDictionary _dataErrorDictionary;
|
||||
|
||||
public TimeZoneEntry()
|
||||
{
|
||||
_dataErrorDictionary = new DataErrorDictionary();
|
||||
_dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged;
|
||||
}
|
||||
|
||||
private string _label;
|
||||
|
||||
public string Label
|
||||
{
|
||||
get => _label;
|
||||
set
|
||||
{
|
||||
if (!ValidateLabel(value))
|
||||
return;
|
||||
|
||||
_label = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string TimeZoneId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasErrors => _dataErrorDictionary.Any();
|
||||
|
||||
public IEnumerable GetErrors(string propertyName)
|
||||
{
|
||||
return _dataErrorDictionary.GetErrors(propertyName);
|
||||
}
|
||||
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
private void DataErrorDictionaryErrorsChanged(object sender, DataErrorsChangedEventArgs e)
|
||||
{
|
||||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(e.PropertyName));
|
||||
}
|
||||
|
||||
private bool ValidateLabel(string newValue)
|
||||
{
|
||||
_dataErrorDictionary.ClearErrors(nameof(Label));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(newValue))
|
||||
return true;
|
||||
|
||||
_dataErrorDictionary.AddError(nameof(Label), "Label cannot be empty");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
50
UpdateCheck.cs
Normal file
50
UpdateCheck.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using NuGet.Versioning;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Velopack;
|
||||
using Velopack.Sources;
|
||||
|
||||
namespace WorldClockStatusWindow;
|
||||
|
||||
internal static class UpdateCheck
|
||||
{
|
||||
private static UpdateManager _updateManager;
|
||||
|
||||
public static UpdateManager UpdateManager => _updateManager ??= new UpdateManager(new GithubSource("https://github.com/ckaczor/WorldClockStatusWindow", null, false));
|
||||
|
||||
public static string LocalVersion => (UpdateManager.CurrentVersion ?? new SemanticVersion(0, 0, 0)).ToString();
|
||||
|
||||
public static bool IsInstalled => UpdateManager.IsInstalled;
|
||||
|
||||
public static async Task DisplayUpdateInformation(bool showIfCurrent)
|
||||
{
|
||||
var newVersion = IsInstalled ? await UpdateManager.CheckForUpdatesAsync() : null;
|
||||
|
||||
if (newVersion != null)
|
||||
{
|
||||
var updateCheckTitle = string.Format(Properties.Resources.UpdateCheckTitle, Properties.Resources.ApplicationName);
|
||||
|
||||
var updateCheckMessage = string.Format(Properties.Resources.UpdateCheckNewVersion, newVersion.TargetFullRelease.Version);
|
||||
|
||||
if (MessageBox.Show(updateCheckMessage, updateCheckTitle, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
Log.Logger.Information("Downloading update");
|
||||
|
||||
await UpdateManager.DownloadUpdatesAsync(newVersion);
|
||||
|
||||
Log.Logger.Information("Installing update");
|
||||
|
||||
UpdateManager.ApplyUpdatesAndRestart(newVersion);
|
||||
}
|
||||
else if (showIfCurrent)
|
||||
{
|
||||
var updateCheckTitle = string.Format(Properties.Resources.UpdateCheckTitle, Properties.Resources.ApplicationName);
|
||||
|
||||
var updateCheckMessage = string.Format(Properties.Resources.UpdateCheckCurrent, Properties.Resources.ApplicationName);
|
||||
|
||||
MessageBox.Show(updateCheckMessage, updateCheckTitle, MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
WindowSource.cs
103
WindowSource.cs
@@ -1,16 +1,15 @@
|
||||
using ChrisKaczor.Wpf.Windows.FloatingStatusWindow;
|
||||
using ChrisKaczor.Wpf.Windows;
|
||||
using ChrisKaczor.Wpf.Windows.FloatingStatusWindow;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows.Threading;
|
||||
using Velopack;
|
||||
using Velopack.Sources;
|
||||
using WorldClockStatusWindow.Properties;
|
||||
using WorldClockStatusWindow.SettingsWindow;
|
||||
|
||||
namespace WorldClockStatusWindow;
|
||||
|
||||
@@ -20,19 +19,13 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
private readonly Timer _timer;
|
||||
private readonly Dispatcher _dispatcher;
|
||||
|
||||
private readonly UpdateManager _updateManager;
|
||||
|
||||
private List<TimeZoneEntry> _timeZoneEntries;
|
||||
|
||||
internal WindowSource()
|
||||
{
|
||||
_floatingStatusWindow = new FloatingStatusWindow(this);
|
||||
_floatingStatusWindow.SetText("Loading...");
|
||||
_floatingStatusWindow.SetText(Resources.Loading);
|
||||
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
|
||||
_updateManager = new UpdateManager(new GithubSource("https://github.com/ckaczor/WorldClockStatusWindow", null, false));
|
||||
|
||||
_timer = new Timer(1000);
|
||||
|
||||
Task.Factory.StartNew(UpdateApp).ContinueWith(task => Start(task.Result.Result));
|
||||
@@ -42,29 +35,32 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_updateManager.IsInstalled)
|
||||
if (!UpdateCheck.IsInstalled)
|
||||
return false;
|
||||
|
||||
if (!Settings.Default.CheckVersionAtStartup)
|
||||
return false;
|
||||
|
||||
Log.Logger.Information("Checking for update");
|
||||
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText("Checking for update..."));
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText(Resources.CheckingForUpdate));
|
||||
|
||||
var newVersion = await _updateManager.CheckForUpdatesAsync();
|
||||
var newVersion = await UpdateCheck.UpdateManager.CheckForUpdatesAsync();
|
||||
|
||||
if (newVersion == null)
|
||||
return false;
|
||||
|
||||
Log.Logger.Information("Downloading update");
|
||||
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText("Downloading update..."));
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText(Resources.DownloadingUpdate));
|
||||
|
||||
await _updateManager.DownloadUpdatesAsync(newVersion);
|
||||
await UpdateCheck.UpdateManager.DownloadUpdatesAsync(newVersion);
|
||||
|
||||
Log.Logger.Information("Installing update");
|
||||
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText("Installing update..."));
|
||||
await _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText(Resources.InstallingUpdate));
|
||||
|
||||
_updateManager.ApplyUpdatesAndRestart(newVersion);
|
||||
UpdateCheck.UpdateManager.ApplyUpdatesAndRestart(newVersion);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -91,49 +87,37 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
_timer.Enabled = true;
|
||||
}
|
||||
|
||||
private void Load()
|
||||
private static void Load()
|
||||
{
|
||||
_timeZoneEntries = JsonSerializer.Deserialize<List<TimeZoneEntry>>(Properties.Settings.Default.TimeZones);
|
||||
|
||||
if (_timeZoneEntries.Any())
|
||||
return;
|
||||
|
||||
_timeZoneEntries.Add(new TimeZoneEntry { Label = "UTC", TimeZoneId = "UTC" });
|
||||
_timeZoneEntries.Add(new TimeZoneEntry { Label = "IST", TimeZoneId = "India Standard Time" });
|
||||
_timeZoneEntries.Add(new TimeZoneEntry { Label = "CET", TimeZoneId = "Central Europe Standard Time" });
|
||||
_timeZoneEntries.Add(new TimeZoneEntry { Label = "Local", TimeZoneId = string.Empty });
|
||||
|
||||
Save();
|
||||
Data.Load();
|
||||
}
|
||||
|
||||
private void Save()
|
||||
private static void Save()
|
||||
{
|
||||
Properties.Settings.Default.TimeZones = JsonSerializer.Serialize(_timeZoneEntries);
|
||||
Properties.Settings.Default.Save();
|
||||
Data.Save();
|
||||
}
|
||||
|
||||
private void HandleTimerElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
|
||||
var now = DateTimeOffset.Now;
|
||||
|
||||
var labelLength = _timeZoneEntries.Max(x => x.Label.Length);
|
||||
|
||||
foreach (var timeZoneEntry in _timeZoneEntries)
|
||||
if (Data.TimeZoneEntries.Any())
|
||||
{
|
||||
var timeZone = timeZoneEntry.TimeZoneId == string.Empty ? TimeZoneInfo.Local : TimeZoneInfo.FindSystemTimeZoneById(timeZoneEntry.TimeZoneId);
|
||||
var now = DateTimeOffset.Now;
|
||||
|
||||
if (text.Length > 0)
|
||||
text.AppendLine();
|
||||
var labelLength = Data.TimeZoneEntries.Max(x => x.Label.Length);
|
||||
|
||||
text.Append($"{timeZoneEntry.Label.PadLeft(labelLength)}: {TimeZoneInfo.ConvertTime(now, timeZone).ToString(Properties.Settings.Default.TimeFormat)}");
|
||||
foreach (var timeZoneEntry in Data.TimeZoneEntries)
|
||||
{
|
||||
var timeZone = timeZoneEntry.TimeZoneId == string.Empty ? TimeZoneInfo.Local : TimeZoneInfo.FindSystemTimeZoneById(timeZoneEntry.TimeZoneId);
|
||||
|
||||
if (text.Length > 0)
|
||||
text.AppendLine();
|
||||
|
||||
text.Append($"{timeZoneEntry.Label.PadLeft(labelLength)}: {TimeZoneInfo.ConvertTime(now, timeZone).ToString(Settings.Default.TimeFormat)}");
|
||||
}
|
||||
}
|
||||
|
||||
text.AppendLine();
|
||||
text.AppendLine();
|
||||
text.Append(_updateManager.CurrentVersion);
|
||||
|
||||
_dispatcher.Invoke(() => _floatingStatusWindow.SetText(text.ToString()));
|
||||
}
|
||||
|
||||
@@ -148,9 +132,9 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
|
||||
public Guid Id => Guid.Parse("29DF6CFD-6783-406F-AE12-4723EB7741EA");
|
||||
|
||||
public string Name => "World Clock";
|
||||
public string Name => Resources.ApplicationName;
|
||||
|
||||
public System.Drawing.Icon Icon => Properties.Resources.ApplicationIcon;
|
||||
public System.Drawing.Icon Icon => Resources.ApplicationIcon;
|
||||
|
||||
public bool HasSettingsMenu => true;
|
||||
|
||||
@@ -158,11 +142,24 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
|
||||
public void ShowAbout()
|
||||
{
|
||||
_floatingStatusWindow.SetText(Assembly.GetEntryAssembly()!.GetName().Version!.ToString());
|
||||
}
|
||||
|
||||
public void ShowSettings()
|
||||
{
|
||||
var categoryPanels = new List<CategoryPanelBase>
|
||||
{
|
||||
new GeneralSettingsPanel(),
|
||||
new TimeZonesSettingsPanel(),
|
||||
new UpdateSettingsPanel(),
|
||||
new AboutSettingsPanel()
|
||||
};
|
||||
|
||||
var settingsWindow = new CategoryWindow(categoryPanels, Resources.SettingsTitle, Resources.CloseButtonText);
|
||||
|
||||
var dialogResult = settingsWindow.ShowDialog();
|
||||
|
||||
if (!dialogResult.GetValueOrDefault(false))
|
||||
return;
|
||||
|
||||
Save();
|
||||
}
|
||||
@@ -175,11 +172,11 @@ internal class WindowSource : IWindowSource, IDisposable
|
||||
|
||||
public string WindowSettings
|
||||
{
|
||||
get => Properties.Settings.Default.WindowSettings;
|
||||
get => Settings.Default.WindowSettings;
|
||||
set
|
||||
{
|
||||
Properties.Settings.Default.WindowSettings = value;
|
||||
Properties.Settings.Default.Save();
|
||||
Settings.Default.WindowSettings = value;
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,58 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
||||
<StartupObject>WorldClockStatusWindow.Program</StartupObject>
|
||||
<ApplicationIcon>Resources\Application.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Application.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Application.StartWithWindows" Version="1.0.5" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Windows.FloatingStatusWindow" Version="2.0.0.5" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
<PackageReference Include="Velopack" Version="0.0.626" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include=".github\workflows\main.yml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
||||
<StartupObject>WorldClockStatusWindow.Program</StartupObject>
|
||||
<ApplicationIcon>Resources\Application.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Application.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Application.StartWithWindows" Version="1.0.5" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Controls.Link" Version="1.0.4" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Validation" Version="1.0.4" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Windows.CategoryWindow" Version="1.0.2" />
|
||||
<PackageReference Include="ChrisKaczor.Wpf.Windows.FloatingStatusWindow" Version="2.0.0.5" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Serilog" Version="4.0.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
<PackageReference Include="Velopack" Version="0.0.626" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include=".github\workflows\main.yml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
2
WorldClockStatusWindow.sln.DotSettings
Normal file
2
WorldClockStatusWindow.sln.DotSettings
Normal file
@@ -0,0 +1,2 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kaczor/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
Reference in New Issue
Block a user