mirror of
https://github.com/ckaczor/WorldClockStatusWindow.git
synced 2026-01-13 17:23:18 -05:00
Add time zones settings panel
This commit is contained in:
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
137
Properties/Resources.Designer.cs
generated
137
Properties/Resources.Designer.cs
generated
@@ -60,6 +60,24 @@ 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>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -79,6 +97,15 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Cancel.
|
||||||
|
/// </summary>
|
||||||
|
public static string CancelButton {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CancelButton", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Checking for update....
|
/// Looks up a localized string similar to Checking for update....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -124,6 +151,42 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Downloading update....
|
/// Looks up a localized string similar to Downloading update....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -133,6 +196,24 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Installing update....
|
/// Looks up a localized string similar to Installing update....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -142,6 +223,15 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Label.
|
||||||
|
/// </summary>
|
||||||
|
public static string LabelColumnHeader {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LabelColumnHeader", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Loading....
|
/// Looks up a localized string similar to Loading....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -151,6 +241,15 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to OK.
|
||||||
|
/// </summary>
|
||||||
|
public static string OkayButton {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("OkayButton", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to About.
|
/// Looks up a localized string similar to About.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -169,6 +268,15 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Time Zones.
|
||||||
|
/// </summary>
|
||||||
|
public static string optionCategoryTimeZones {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("optionCategoryTimeZones", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Update.
|
/// Looks up a localized string similar to Update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -196,6 +304,33 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You are already running the most recent version.
|
/// Looks up a localized string similar to You are already running the most recent version.
|
||||||
///
|
///
|
||||||
@@ -208,7 +343,7 @@ namespace WorldClockStatusWindow.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Version {1} of {0} is now available.
|
/// Looks up a localized string similar to Version {0} is now available.
|
||||||
///
|
///
|
||||||
///Would you like to download and install it now?.
|
///Would you like to download and install it now?.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -179,4 +179,49 @@ Would you like to download and install it now?</value>
|
|||||||
<data name="InstallingUpdate" xml:space="preserve">
|
<data name="InstallingUpdate" xml:space="preserve">
|
||||||
<value>Installing update...</value>
|
<value>Installing update...</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
@@ -20,8 +19,6 @@ internal class WindowSource : IWindowSource, IDisposable
|
|||||||
private readonly Timer _timer;
|
private readonly Timer _timer;
|
||||||
private readonly Dispatcher _dispatcher;
|
private readonly Dispatcher _dispatcher;
|
||||||
|
|
||||||
private List<TimeZoneEntry> _timeZoneEntries;
|
|
||||||
|
|
||||||
internal WindowSource()
|
internal WindowSource()
|
||||||
{
|
{
|
||||||
_floatingStatusWindow = new FloatingStatusWindow(this);
|
_floatingStatusWindow = new FloatingStatusWindow(this);
|
||||||
@@ -90,36 +87,27 @@ internal class WindowSource : IWindowSource, IDisposable
|
|||||||
_timer.Enabled = true;
|
_timer.Enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Load()
|
private static void Load()
|
||||||
{
|
{
|
||||||
_timeZoneEntries = JsonSerializer.Deserialize<List<TimeZoneEntry>>(Settings.Default.TimeZones);
|
Data.Load();
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Save()
|
private static void Save()
|
||||||
{
|
{
|
||||||
Settings.Default.TimeZones = JsonSerializer.Serialize(_timeZoneEntries);
|
Data.Save();
|
||||||
Settings.Default.Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleTimerElapsed(object sender, ElapsedEventArgs e)
|
private void HandleTimerElapsed(object sender, ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
var text = new StringBuilder();
|
var text = new StringBuilder();
|
||||||
|
|
||||||
|
if (Data.TimeZoneEntries.Any())
|
||||||
|
{
|
||||||
var now = DateTimeOffset.Now;
|
var now = DateTimeOffset.Now;
|
||||||
|
|
||||||
var labelLength = _timeZoneEntries.Max(x => x.Label.Length);
|
var labelLength = Data.TimeZoneEntries.Max(x => x.Label.Length);
|
||||||
|
|
||||||
foreach (var timeZoneEntry in _timeZoneEntries)
|
foreach (var timeZoneEntry in Data.TimeZoneEntries)
|
||||||
{
|
{
|
||||||
var timeZone = timeZoneEntry.TimeZoneId == string.Empty ? TimeZoneInfo.Local : TimeZoneInfo.FindSystemTimeZoneById(timeZoneEntry.TimeZoneId);
|
var timeZone = timeZoneEntry.TimeZoneId == string.Empty ? TimeZoneInfo.Local : TimeZoneInfo.FindSystemTimeZoneById(timeZoneEntry.TimeZoneId);
|
||||||
|
|
||||||
@@ -128,6 +116,7 @@ internal class WindowSource : IWindowSource, IDisposable
|
|||||||
|
|
||||||
text.Append($"{timeZoneEntry.Label.PadLeft(labelLength)}: {TimeZoneInfo.ConvertTime(now, timeZone).ToString(Settings.Default.TimeFormat)}");
|
text.Append($"{timeZoneEntry.Label.PadLeft(labelLength)}: {TimeZoneInfo.ConvertTime(now, timeZone).ToString(Settings.Default.TimeFormat)}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_dispatcher.Invoke(() => _floatingStatusWindow.SetText(text.ToString()));
|
_dispatcher.Invoke(() => _floatingStatusWindow.SetText(text.ToString()));
|
||||||
}
|
}
|
||||||
@@ -160,6 +149,7 @@ internal class WindowSource : IWindowSource, IDisposable
|
|||||||
var categoryPanels = new List<CategoryPanelBase>
|
var categoryPanels = new List<CategoryPanelBase>
|
||||||
{
|
{
|
||||||
new GeneralSettingsPanel(),
|
new GeneralSettingsPanel(),
|
||||||
|
new TimeZonesSettingsPanel(),
|
||||||
new UpdateSettingsPanel(),
|
new UpdateSettingsPanel(),
|
||||||
new AboutSettingsPanel()
|
new AboutSettingsPanel()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
<AppDesigner Include="Properties\" />
|
<AppDesigner Include="Properties\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Resources\Application.ico" />
|
<Resource Include="Resources\Application.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ChrisKaczor.Wpf.Application.StartWithWindows" Version="1.0.5" />
|
<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.CategoryWindow" Version="1.0.2" />
|
||||||
<PackageReference Include="ChrisKaczor.Wpf.Windows.FloatingStatusWindow" Version="2.0.0.5" />
|
<PackageReference Include="ChrisKaczor.Wpf.Windows.FloatingStatusWindow" Version="2.0.0.5" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user