.Net 中的 WPF、协变、逆变和绑定
WPF, covariance, contravariance, and binding in .Net
我需要创建一个设置管理器,但我不能使用 appSettings 或 applicationSettings,因为我需要特定的行为(adding/removing 运行时设置,在多个文件中加载 from/saving...)。
我遇到的问题是找到我想要的最终结果的解决方案。这是一个用例中的示例:
public partial class MyWindow : Window
{
private SettingsManager _settingsManager;
public MyClass()
{
_settingsManager.Add(new Option(){Name = "Option1", DisplayName = "Option 1", DefaultValue = "SomeText", Value = "SomeText"});
_settingsManager.Add(new Option(){Name = "Option2", DisplayName = "Option 2", DefaultValue = 5});
_settingsManager.Add(new Option(){Name = "Option3", DisplayName = "Option 3"});
_settingsManager["Option2"] = 10;
_settingsManager["Option3"].DefaultValue = Brushes.Black;
_settingsManager["Option3"] = Brushes.White;
DataContext = _settingsManager;
}
}
与其对应的 XAML 文件 :
<Window x:Class="AnotherProject.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type String}">
<Label Content="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type int}">
<Label Content="{Binding}" Foreground="Red"/>
</DataTemplate>
<DataTemplate DataType="{x:Type Brush}">
<Label Content="{Binding ToString}" Foreground="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
我知道 WPF 不支持 int 和 string 类型并且它会产生错误,但这只是用作说明性示例,真正的类型不会产生这些错误。
我尝试从 SettingsProperty 继承,由 appSettings 使用,但对于我想要获得的东西来说它似乎太复杂了,我无法在没有转换的情况下分配值。
我尝试使用泛型,但类型识别存在问题。除非将对象转换为适当的类型,否则以下行不起作用:
_settingsManager["Option2"] = 10;
我知道单独实现一些用例部分的方法,但没有一种方法可以完成上述所有操作。
有可能实现吗?
更新:
这是 SettingManager 的样子
public class SettingsManager : ObservableCollection<Option>
{
public SettingsManager()
{
}
public SettingsManager(IEnumerable<Option> options)
{
if (options != null)
{
foreach(var option in options)
{
this.Add(option);
}
}
}
public Option this[String key]
{
get
{
return this.Where(x => x.Name == key).FirstOrDefault();
}
set
{
var res = this.First(x => x.Name == key);
if (res != null)
{
this.SetItem(this.IndexOf(res), value);
}
else
this.Add(value);
}
}
public SettingsManager AddRange(IEnumerable<Option> options)
{
if (options != null)
{
foreach(var option in options)
{
this.Add(option);
}
}
return this;
}
public void ResetAll()
{
this.ToList().ForEach(x => x.Reset());
}
}
ItemsControl
的 ItemsSource
设置为 Option
的集合,这意味着每个数据项的类型都是 Option
。因此 DataTemplate
with DataType 无法过滤不同类型的 Value
(Option
的 属性)以应用不同的模板。在这种情况下,您应该使用 DataTemplateSelector
,为方便起见,将您的 DataTemplates 定义为 XAML 中的资源(然后我们可以在 SelectTemplate
方法后面的代码中找到它们):
public class OptionTemplateSelector : DataTemplateSelector {
public override DataTemplate SelectTemplate(object item, DependencyObject container){
var option = item as Option;
if(option != null){
var itemContainer = container as FrameworkElement;
if(option.Value is int){
return itemContainer.FindResource("intOption") as DataTemplate;
}
else if(option.Value is string){
return itemContainer.FindResource("stringOption") as DataTemplate;
}
else if(option.Value is Brush){
return itemContainer.FindResource("brushOption") as DataTemplate;
}
}
return null;
}
}
将模板选择器定义为 XAML 中的某些资源:
<local:OptionTemplateSelector x:Key="ot">
剩余XAML:
<ItemsControl ItemsSource="{Binding}"
ItemTemplateSelector="{StaticResource ot}">
<ItemsControl.Resources>
<DataTemplate x:Key="stringOption">
<Label Content="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="intOption">
<Label Content="{Binding Value}" Foreground="Red"/>
</DataTemplate>
<DataTemplate x:Key="brushOption">
<Label Content="{Binding Value}" Foreground="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
我需要创建一个设置管理器,但我不能使用 appSettings 或 applicationSettings,因为我需要特定的行为(adding/removing 运行时设置,在多个文件中加载 from/saving...)。
我遇到的问题是找到我想要的最终结果的解决方案。这是一个用例中的示例:
public partial class MyWindow : Window
{
private SettingsManager _settingsManager;
public MyClass()
{
_settingsManager.Add(new Option(){Name = "Option1", DisplayName = "Option 1", DefaultValue = "SomeText", Value = "SomeText"});
_settingsManager.Add(new Option(){Name = "Option2", DisplayName = "Option 2", DefaultValue = 5});
_settingsManager.Add(new Option(){Name = "Option3", DisplayName = "Option 3"});
_settingsManager["Option2"] = 10;
_settingsManager["Option3"].DefaultValue = Brushes.Black;
_settingsManager["Option3"] = Brushes.White;
DataContext = _settingsManager;
}
}
与其对应的 XAML 文件 :
<Window x:Class="AnotherProject.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type String}">
<Label Content="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type int}">
<Label Content="{Binding}" Foreground="Red"/>
</DataTemplate>
<DataTemplate DataType="{x:Type Brush}">
<Label Content="{Binding ToString}" Foreground="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
我知道 WPF 不支持 int 和 string 类型并且它会产生错误,但这只是用作说明性示例,真正的类型不会产生这些错误。
我尝试从 SettingsProperty 继承,由 appSettings 使用,但对于我想要获得的东西来说它似乎太复杂了,我无法在没有转换的情况下分配值。
我尝试使用泛型,但类型识别存在问题。除非将对象转换为适当的类型,否则以下行不起作用:
_settingsManager["Option2"] = 10;
我知道单独实现一些用例部分的方法,但没有一种方法可以完成上述所有操作。
有可能实现吗?
更新: 这是 SettingManager 的样子
public class SettingsManager : ObservableCollection<Option>
{
public SettingsManager()
{
}
public SettingsManager(IEnumerable<Option> options)
{
if (options != null)
{
foreach(var option in options)
{
this.Add(option);
}
}
}
public Option this[String key]
{
get
{
return this.Where(x => x.Name == key).FirstOrDefault();
}
set
{
var res = this.First(x => x.Name == key);
if (res != null)
{
this.SetItem(this.IndexOf(res), value);
}
else
this.Add(value);
}
}
public SettingsManager AddRange(IEnumerable<Option> options)
{
if (options != null)
{
foreach(var option in options)
{
this.Add(option);
}
}
return this;
}
public void ResetAll()
{
this.ToList().ForEach(x => x.Reset());
}
}
ItemsControl
的 ItemsSource
设置为 Option
的集合,这意味着每个数据项的类型都是 Option
。因此 DataTemplate
with DataType 无法过滤不同类型的 Value
(Option
的 属性)以应用不同的模板。在这种情况下,您应该使用 DataTemplateSelector
,为方便起见,将您的 DataTemplates 定义为 XAML 中的资源(然后我们可以在 SelectTemplate
方法后面的代码中找到它们):
public class OptionTemplateSelector : DataTemplateSelector {
public override DataTemplate SelectTemplate(object item, DependencyObject container){
var option = item as Option;
if(option != null){
var itemContainer = container as FrameworkElement;
if(option.Value is int){
return itemContainer.FindResource("intOption") as DataTemplate;
}
else if(option.Value is string){
return itemContainer.FindResource("stringOption") as DataTemplate;
}
else if(option.Value is Brush){
return itemContainer.FindResource("brushOption") as DataTemplate;
}
}
return null;
}
}
将模板选择器定义为 XAML 中的某些资源:
<local:OptionTemplateSelector x:Key="ot">
剩余XAML:
<ItemsControl ItemsSource="{Binding}"
ItemTemplateSelector="{StaticResource ot}">
<ItemsControl.Resources>
<DataTemplate x:Key="stringOption">
<Label Content="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="intOption">
<Label Content="{Binding Value}" Foreground="Red"/>
</DataTemplate>
<DataTemplate x:Key="brushOption">
<Label Content="{Binding Value}" Foreground="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>