.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());
    }
}

ItemsControlItemsSource 设置为 Option 的集合,这意味着每个数据项的类型都是 Option。因此 DataTemplate with DataType 无法过滤不同类型的 ValueOption 的 属性)以应用不同的模板。在这种情况下,您应该使用 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>