将 ItemsControl 绑定到单个模型上的各个属性?

Bind an ItemsControl to individual properties on a single model?

我有一个具有许多 bool 属性的模型,例如:-

public BasicPreferences
{
    public bool ShowLegend {get;set;}
    public bool ShowLabels {get;set;}
    public bool ShowTooltips {get;set;}
}

(简化 - 真正的 class 实现 INotifyPropertyChanged)。

我目前将它们呈现为网格中的一系列复选框,例如:-

<Grid>
   <Grid.ColumnDefinitions>
       <ColumnDefinition />
       <ColumnDefinition />
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
       <RowDefinition />
       <RowDefinition />
       <RowDefinition />
   </Grid.RowDefinitions>

   <!-- First option -->
   <TextBlock Grid.Column="0" Grid.Row="0" Text="Show legend?" />
   <CheckBox Grid.Column="1" Grid.Row="0" IsChecked="{Binding Preferences.ShowLegend" />
   <!-- Second option -->
   <TextBlock Grid.Column="0" Grid.Row="1" Text="Show labels?" />
   <CheckBox etc...
</Grid>

(再次简化以删除样式等)

我计划创建更多模型 classes 继承上述 class 并添加 bool 它们自己的属性,例如

public AdvancedPreferences : BasicPreferences
{
    public bool IsFooEnabled {get;set;}
    ..etc..
}

很明显,我需要找到 "hardcoded" XAML 网格的替代方案,所以有什么方法可以利用数据绑定来显示(比如)ItemsControl 中的选项,同时保持模型原样?我意识到标准的 WPF 解决方案是将选项公开为可观察的集合,但我想保持模型原样,将选项作为单独的属性(在应用程序代码的其他地方引用)。

DataForm 控件可以满足您的需要。它给出了模型的视觉表示。您需要将模型绑定到控件,控件将根据 属性 类型呈现相应的 UI 元素。像字符串类型属性的 TextBox,布尔类型属性的复选框等等。它是高度可定制的。

https://wpfdataform.codeplex.com/

通常的做法是通过转换器。转换器获取您的 BasicPreferences class,并使用反射生成您的属性的 ObservableCollection<object>(绑定所需的任何内容)。后面如果要区分各种属性类型,可以在ItemsControl.

中使用DataTemplate

这也是一个示例实现:https://wpftoolkit.codeplex.com/wikipage?title=PropertyGrid

这是一个可能的解决方案:

在您的视图模型中,创建对象的可观察集合:

public ObservableCollection<object> Preferences { get; set; }

然后,将您的偏好添加到这个可观察的集合中。

创建一个转换器,它将测试首选项列表中对象的类型,并return基于对象类型的可见性:

public class IsAdvancedPreferencesConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Type t = value.GetType();

        if (t == typeof(AdvancedPreferences))
            return Visibility.Visible;
        else return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

根据您的看法,在 window 资源中创建转换器:

<Window.Resources>
    <ViewModels:IsAdvancedPreferencesConverter x:Key="IsAdvancedPreferencesConverter"/>
</Window.Resources>

现在,使用以下 ItemsControl:

<ItemsControl ItemsSource="{Binding Preferences}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <CheckBox Grid.Row="0" IsChecked="{Binding ShowLegend}" Content="Show Legend?"/>
                    <CheckBox Grid.Row="1" IsChecked="{Binding ShowLabels}" Content="Show Labels?"/>
                    <CheckBox Grid.Row="2" IsChecked="{Binding ShowTooltips}" Content="Show Tooltips?"/>

                    <Grid Grid.Row="3" Visibility="{Binding Converter={StaticResource IsAdvancedPreferencesConverter}}">
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            ...
                        </Grid.RowDefinitions>

                        <CheckBox Grid.Row="0" IsChecked="{Binding IsFooEnabled}" Content="Is Foo Enabled?"/>
                        ...
                    </Grid>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

在这种情况下,如果列表中项目的类型是高级首选项,则包含高级首选项复选框的网格将可见。