Refactor theme code to support different types
This commit is contained in:
parent
d0bc0b6864
commit
8ba3433dcf
|
@ -63,7 +63,7 @@ namespace Filtration.ObjectModel.BlockItemBaseTypes
|
||||||
|
|
||||||
private void OnThemeComponentUpdated(object sender, EventArgs e)
|
private void OnThemeComponentUpdated(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Color = ((ThemeComponent) sender).Color;
|
Color = ((ColorThemeComponent) sender).Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnThemeComponentDeleted(object sender, EventArgs e)
|
private void OnThemeComponentDeleted(object sender, EventArgs e)
|
||||||
|
|
|
@ -131,6 +131,7 @@
|
||||||
<Compile Include="Socket.cs" />
|
<Compile Include="Socket.cs" />
|
||||||
<Compile Include="SocketGroup.cs" />
|
<Compile Include="SocketGroup.cs" />
|
||||||
<Compile Include="ThemeEditor\Theme.cs" />
|
<Compile Include="ThemeEditor\Theme.cs" />
|
||||||
|
<Compile Include="ThemeEditor\ColorThemeComponent.cs" />
|
||||||
<Compile Include="ThemeEditor\ThemeComponent.cs" />
|
<Compile Include="ThemeEditor\ThemeComponent.cs" />
|
||||||
<Compile Include="ThemeEditor\ThemeComponentCollection.cs" />
|
<Compile Include="ThemeEditor\ThemeComponentCollection.cs" />
|
||||||
<Compile Include="WindsorInstallers\CommandsInstaller.cs" />
|
<Compile Include="WindsorInstallers\CommandsInstaller.cs" />
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Filtration.ObjectModel.Annotations;
|
||||||
|
using Filtration.ObjectModel.Enums;
|
||||||
|
|
||||||
|
namespace Filtration.ObjectModel.ThemeEditor
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class ColorThemeComponent : ThemeComponent
|
||||||
|
{
|
||||||
|
private Color _color;
|
||||||
|
private readonly object _eventLock = new object();
|
||||||
|
|
||||||
|
public ColorThemeComponent(ThemeComponentType componentType, string componentName, Color componentColor)
|
||||||
|
{
|
||||||
|
if (componentName == null || componentColor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Null parameters not allowed in ColorThemeComponent constructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentType = componentType;
|
||||||
|
Color = componentColor;
|
||||||
|
ComponentName = componentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color Color
|
||||||
|
{
|
||||||
|
get { return _color; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_color = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
_themeComponentUpdatedEventHandler?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ namespace Filtration.ObjectModel.ThemeEditor
|
||||||
|
|
||||||
public void AddComponent(ThemeComponentType componentType, string componentName, Color componentColor)
|
public void AddComponent(ThemeComponentType componentType, string componentName, Color componentColor)
|
||||||
{
|
{
|
||||||
_components.Add(new ThemeComponent(componentType, componentName, componentColor));
|
_components.Add(new ColorThemeComponent(componentType, componentName, componentColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ namespace Filtration.ObjectModel.ThemeEditor
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ThemeComponent : INotifyPropertyChanged
|
public class ThemeComponent : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private Color _color;
|
protected EventHandler _themeComponentUpdatedEventHandler;
|
||||||
private EventHandler _themeComponentUpdatedEventHandler;
|
|
||||||
private readonly object _eventLock = new object();
|
private readonly object _eventLock = new object();
|
||||||
|
|
||||||
public ThemeComponent()
|
public ThemeComponent()
|
||||||
|
@ -19,18 +18,6 @@ namespace Filtration.ObjectModel.ThemeEditor
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThemeComponent(ThemeComponentType componentType, string componentName, Color componentColor)
|
|
||||||
{
|
|
||||||
if (componentName == null || componentColor == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Null parameters not allowed in ThemeComponent constructor");
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentType = componentType;
|
|
||||||
Color = componentColor;
|
|
||||||
ComponentName = componentName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// By implementing a custom event accessor here we can keep the UsageCount up to date.
|
// By implementing a custom event accessor here we can keep the UsageCount up to date.
|
||||||
public event EventHandler ThemeComponentUpdated
|
public event EventHandler ThemeComponentUpdated
|
||||||
{
|
{
|
||||||
|
@ -58,17 +45,6 @@ namespace Filtration.ObjectModel.ThemeEditor
|
||||||
public string ComponentName { get; set; }
|
public string ComponentName { get; set; }
|
||||||
public ThemeComponentType ComponentType{ get; set; }
|
public ThemeComponentType ComponentType{ get; set; }
|
||||||
|
|
||||||
public Color Color
|
|
||||||
{
|
|
||||||
get { return _color; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_color = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
_themeComponentUpdatedEventHandler?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int UsageCount
|
public int UsageCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
|
@ -16,7 +16,15 @@ namespace Filtration.ObjectModel.ThemeEditor
|
||||||
return Items.FirstOrDefault(t => t.ComponentName == componentName && t.ComponentType == componentType);
|
return Items.FirstOrDefault(t => t.ComponentName == componentName && t.ComponentType == componentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
var component = new ThemeComponent(componentType, componentName, componentColor);
|
ThemeComponent component = null;
|
||||||
|
switch(componentType)
|
||||||
|
{
|
||||||
|
case ThemeComponentType.BackgroundColor:
|
||||||
|
case ThemeComponentType.BorderColor:
|
||||||
|
case ThemeComponentType.TextColor:
|
||||||
|
component = new ColorThemeComponent(componentType, componentName, componentColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
Items.Add(component);
|
Items.Add(component);
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
|
|
|
@ -773,7 +773,7 @@ namespace Filtration.Parser.Tests.Services
|
||||||
// Arrange
|
// Arrange
|
||||||
var inputString = "Show" + Environment.NewLine +
|
var inputString = "Show" + Environment.NewLine +
|
||||||
" SetTextColor 255 20 100 # Rare Item Text";
|
" SetTextColor 255 20 100 # Rare Item Text";
|
||||||
var testComponent = new ThemeComponent(ThemeComponentType.TextColor, "Rare Item Text", new Color { R = 255, G = 20, B = 100});
|
var testComponent = new ColorThemeComponent(ThemeComponentType.TextColor, "Rare Item Text", new Color { R = 255, G = 20, B = 100});
|
||||||
var testInputThemeComponentCollection = new ThemeComponentCollection { testComponent };
|
var testInputThemeComponentCollection = new ThemeComponentCollection { testComponent };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
@ -1751,7 +1751,7 @@ namespace Filtration.Parser.Tests.Services
|
||||||
|
|
||||||
var blockItem = new TextColorBlockItem(new Color {A = 255, R = 54, G = 102, B = 255})
|
var blockItem = new TextColorBlockItem(new Color {A = 255, R = 54, G = 102, B = 255})
|
||||||
{
|
{
|
||||||
ThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Test Theme Component", new Color())
|
ThemeComponent = new ColorThemeComponent(ThemeComponentType.TextColor, "Test Theme Component", new Color())
|
||||||
};
|
};
|
||||||
|
|
||||||
_testUtility.TestBlock.BlockItems.Add(blockItem);
|
_testUtility.TestBlock.BlockItems.Add(blockItem);
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Filtration.ThemeEditor.Tests.Services
|
||||||
|
|
||||||
var testInputTheme = new Theme();
|
var testInputTheme = new Theme();
|
||||||
var testInputThemeComponentColor = new Color{ R = 255, G = 0, B = 1 };
|
var testInputThemeComponentColor = new Color{ R = 255, G = 0, B = 1 };
|
||||||
var testInputThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
|
var testInputThemeComponent = new ColorThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
|
||||||
testInputTheme.Components.Add(testInputThemeComponent);
|
testInputTheme.Components.Add(testInputThemeComponent);
|
||||||
testInputBlockItem.ThemeComponent = testInputThemeComponent;
|
testInputBlockItem.ThemeComponent = testInputThemeComponent;
|
||||||
var mockMessageBoxService = new Mock<IMessageBoxService>();
|
var mockMessageBoxService = new Mock<IMessageBoxService>();
|
||||||
|
@ -53,8 +53,8 @@ namespace Filtration.ThemeEditor.Tests.Services
|
||||||
|
|
||||||
var testInputTheme = new Theme();
|
var testInputTheme = new Theme();
|
||||||
var testInputThemeComponentColor = new Color { R = 255, G = 0, B = 1 };
|
var testInputThemeComponentColor = new Color { R = 255, G = 0, B = 1 };
|
||||||
var testInputThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
|
var testInputThemeComponent = new ColorThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
|
||||||
var testInputBlockItemThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Different Component", testInputThemeComponentColor);
|
var testInputBlockItemThemeComponent = new ColorThemeComponent(ThemeComponentType.TextColor, "Different Component", testInputThemeComponentColor);
|
||||||
testInputTheme.Components.Add(testInputThemeComponent);
|
testInputTheme.Components.Add(testInputThemeComponent);
|
||||||
testInputBlockItem.ThemeComponent = testInputBlockItemThemeComponent;
|
testInputBlockItem.ThemeComponent = testInputBlockItemThemeComponent;
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@
|
||||||
<Compile Include="Providers\ThemeProvider.cs" />
|
<Compile Include="Providers\ThemeProvider.cs" />
|
||||||
<Compile Include="Services\ThemePersistenceService.cs" />
|
<Compile Include="Services\ThemePersistenceService.cs" />
|
||||||
<Compile Include="Services\ThemeService.cs" />
|
<Compile Include="Services\ThemeService.cs" />
|
||||||
|
<Compile Include="ViewModels\ColorThemeComponentViewModel.cs" />
|
||||||
<Compile Include="ViewModels\IThemeViewModelFactory.cs" />
|
<Compile Include="ViewModels\IThemeViewModelFactory.cs" />
|
||||||
<Compile Include="ViewModels\ThemeComponentViewModel.cs" />
|
<Compile Include="ViewModels\ThemeComponentViewModel.cs" />
|
||||||
<Compile Include="ViewModels\ThemeEditorViewModel.cs" />
|
<Compile Include="ViewModels\ThemeEditorViewModel.cs" />
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Filtration.ObjectModel;
|
using Filtration.ObjectModel;
|
||||||
|
using Filtration.ObjectModel.Enums;
|
||||||
using Filtration.ObjectModel.ThemeEditor;
|
using Filtration.ObjectModel.ThemeEditor;
|
||||||
using Filtration.ThemeEditor.Services;
|
using Filtration.ThemeEditor.Services;
|
||||||
using Filtration.ThemeEditor.ViewModels;
|
using Filtration.ThemeEditor.ViewModels;
|
||||||
|
@ -34,7 +35,14 @@ namespace Filtration.ThemeEditor.Providers
|
||||||
var themeComponentCollection = script.ThemeComponents.Aggregate(new ThemeComponentCollection(),
|
var themeComponentCollection = script.ThemeComponents.Aggregate(new ThemeComponentCollection(),
|
||||||
(c, component) =>
|
(c, component) =>
|
||||||
{
|
{
|
||||||
c.Add(new ThemeComponent(component.ComponentType, component.ComponentName, component.Color));
|
switch(component.ComponentType)
|
||||||
|
{
|
||||||
|
case ThemeComponentType.BackgroundColor:
|
||||||
|
case ThemeComponentType.BorderColor:
|
||||||
|
case ThemeComponentType.TextColor:
|
||||||
|
c.Add(new ColorThemeComponent(component.ComponentType, component.ComponentName, ((ColorThemeComponent)component).Color));
|
||||||
|
break;
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Filtration.Common.Services;
|
using Filtration.Common.Services;
|
||||||
|
@ -29,39 +30,19 @@ namespace Filtration.ThemeEditor.Services
|
||||||
var mismatchedComponents = false;
|
var mismatchedComponents = false;
|
||||||
foreach (var component in theme.Components)
|
foreach (var component in theme.Components)
|
||||||
{
|
{
|
||||||
var componentMatched = false;
|
var blocks = script.ItemFilterBlocks.OfType<ItemFilterBlock>();
|
||||||
Type targetType = null;
|
|
||||||
switch (component.ComponentType)
|
switch (component.ComponentType)
|
||||||
{
|
{
|
||||||
case ThemeComponentType.BackgroundColor:
|
case ThemeComponentType.BackgroundColor:
|
||||||
targetType = typeof (BackgroundColorBlockItem);
|
mismatchedComponents = ApplyColorTheme(blocks, typeof(BackgroundColorBlockItem), component);
|
||||||
break;
|
break;
|
||||||
case ThemeComponentType.TextColor:
|
case ThemeComponentType.TextColor:
|
||||||
targetType = typeof (TextColorBlockItem);
|
mismatchedComponents = ApplyColorTheme(blocks, typeof(TextColorBlockItem), component);
|
||||||
break;
|
break;
|
||||||
case ThemeComponentType.BorderColor:
|
case ThemeComponentType.BorderColor:
|
||||||
targetType = typeof (BorderColorBlockItem);
|
mismatchedComponents = ApplyColorTheme(blocks, typeof(BorderColorBlockItem), component);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var block in script.ItemFilterBlocks.OfType<ItemFilterBlock>())
|
|
||||||
{
|
|
||||||
foreach (var blockItem in block.BlockItems.Where(i => i.GetType() == targetType))
|
|
||||||
{
|
|
||||||
var colorBlockItem = (ColorBlockItem) blockItem;
|
|
||||||
if (colorBlockItem.ThemeComponent != null &&
|
|
||||||
colorBlockItem.ThemeComponent.ComponentName == component.ComponentName)
|
|
||||||
{
|
|
||||||
colorBlockItem.Color = component.Color;
|
|
||||||
componentMatched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!componentMatched)
|
|
||||||
{
|
|
||||||
mismatchedComponents = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mismatchedComponents)
|
if (mismatchedComponents)
|
||||||
|
@ -71,5 +52,25 @@ namespace Filtration.ThemeEditor.Services
|
||||||
MessageBoxButton.OK, MessageBoxImage.Exclamation);
|
MessageBoxButton.OK, MessageBoxImage.Exclamation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ApplyColorTheme(IEnumerable<ItemFilterBlock> blocks, Type type, ThemeComponent component)
|
||||||
|
{
|
||||||
|
var componentMatched = false;
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
foreach (var blockItem in block.BlockItems.Where(i => i.GetType() == type))
|
||||||
|
{
|
||||||
|
var colorBlockItem = (ColorBlockItem)blockItem;
|
||||||
|
if (colorBlockItem.ThemeComponent != null &&
|
||||||
|
colorBlockItem.ThemeComponent.ComponentName == component.ComponentName)
|
||||||
|
{
|
||||||
|
colorBlockItem.Color = ((ColorThemeComponent)component).Color;
|
||||||
|
componentMatched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !componentMatched;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Filtration.ObjectModel.Enums;
|
||||||
|
|
||||||
|
namespace Filtration.ThemeEditor.ViewModels
|
||||||
|
{
|
||||||
|
public class ColorThemeComponentViewModel : ThemeComponentViewModel
|
||||||
|
{
|
||||||
|
public Color Color { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,11 @@ namespace Filtration.ThemeEditor.ViewModels
|
||||||
{
|
{
|
||||||
string ComponentName { get; set; }
|
string ComponentName { get; set; }
|
||||||
ThemeComponentType ComponentType { get; set; }
|
ThemeComponentType ComponentType { get; set; }
|
||||||
Color Color { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ThemeComponentViewModel : IThemeComponentViewModel
|
public class ThemeComponentViewModel : IThemeComponentViewModel
|
||||||
{
|
{
|
||||||
public string ComponentName { get; set; }
|
public string ComponentName { get; set; }
|
||||||
public ThemeComponentType ComponentType { get; set; }
|
public ThemeComponentType ComponentType { get; set; }
|
||||||
public Color Color { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,8 +194,15 @@ namespace Filtration.ThemeEditor.ViewModels
|
||||||
|
|
||||||
private void OnAddThemeComponentCommand(ThemeComponentType themeComponentType)
|
private void OnAddThemeComponentCommand(ThemeComponentType themeComponentType)
|
||||||
{
|
{
|
||||||
Components.Add(new ThemeComponent(themeComponentType, "Untitled Component",
|
switch (themeComponentType)
|
||||||
new Color {A = 255, R = 255, G = 255, B = 255}));
|
{
|
||||||
|
case ThemeComponentType.BackgroundColor:
|
||||||
|
case ThemeComponentType.BorderColor:
|
||||||
|
case ThemeComponentType.TextColor:
|
||||||
|
Components.Add(new ColorThemeComponent(themeComponentType, "Untitled Component",
|
||||||
|
new Color { A = 255, R = 255, G = 255, B = 255 }));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDeleteThemeComponentCommand(ThemeComponent themeComponent)
|
private void OnDeleteThemeComponentCommand(ThemeComponent themeComponent)
|
||||||
|
|
|
@ -58,6 +58,13 @@
|
||||||
</Style>
|
</Style>
|
||||||
</ContentControl.Style>
|
</ContentControl.Style>
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
<xctk:ColorPicker Grid.Row="2" SelectedColor="{Binding Color}" />
|
<ContentControl Grid.Row="2" Content="{Binding Mode=OneWay}">
|
||||||
|
<ContentControl.Resources>
|
||||||
|
<!-- Color Theme Template -->
|
||||||
|
<DataTemplate DataType="{x:Type themeEditor:ColorThemeComponent}">
|
||||||
|
<xctk:ColorPicker SelectedColor="{Binding Color}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.Resources>
|
||||||
|
</ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace Filtration
|
||||||
cfg.ConstructServicesUsing(_container.Resolve);
|
cfg.ConstructServicesUsing(_container.Resolve);
|
||||||
cfg.CreateMap<Theme, IThemeEditorViewModel>().ConstructUsingServiceLocator();
|
cfg.CreateMap<Theme, IThemeEditorViewModel>().ConstructUsingServiceLocator();
|
||||||
cfg.CreateMap<ThemeComponent, ThemeComponentViewModel>().ReverseMap();
|
cfg.CreateMap<ThemeComponent, ThemeComponentViewModel>().ReverseMap();
|
||||||
|
cfg.CreateMap<ColorThemeComponent, ColorThemeComponentViewModel>().ReverseMap();
|
||||||
cfg.CreateMap<IThemeEditorViewModel, Theme>();
|
cfg.CreateMap<IThemeEditorViewModel, Theme>();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ using System.Windows;
|
||||||
using Filtration.Annotations;
|
using Filtration.Annotations;
|
||||||
using Filtration.ObjectModel;
|
using Filtration.ObjectModel;
|
||||||
using Filtration.ObjectModel.BlockItemBaseTypes;
|
using Filtration.ObjectModel.BlockItemBaseTypes;
|
||||||
|
using Filtration.ObjectModel.ThemeEditor;
|
||||||
using Filtration.Views;
|
using Filtration.Views;
|
||||||
using GalaSoft.MvvmLight.CommandWpf;
|
using GalaSoft.MvvmLight.CommandWpf;
|
||||||
using Xceed.Wpf.Toolkit;
|
using Xceed.Wpf.Toolkit;
|
||||||
|
@ -97,7 +98,7 @@ namespace Filtration.UserControls
|
||||||
var blockItem = BlockItem as ColorBlockItem;
|
var blockItem = BlockItem as ColorBlockItem;
|
||||||
if (blockItem?.ThemeComponent == null) return;
|
if (blockItem?.ThemeComponent == null) return;
|
||||||
|
|
||||||
blockItem.Color = blockItem.ThemeComponent.Color;
|
blockItem.Color = ((ColorThemeComponent)blockItem.ThemeComponent).Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
Loading…
Reference in New Issue