WPF:将列表动态绑定到(某些)对象的属性

Posted

技术标签:

【中文标题】WPF:将列表动态绑定到(某些)对象的属性【英文标题】:WPF: Dynamically binding a list to (some of) an object's properties 【发布时间】:2011-03-10 07:34:09 【问题描述】:

我有一个对象集合存储在CollectionViewSource 中并绑定到DataGrid我想显示DataGrid中当前选定对象的“详细视图”。我可以使用CollectionViewSource.View.CurrentItem获取当前对象。

MyClass

    [IsImportant]   
    AProperty

    AnotherProperty

    [IsImportant]
    YetAnotherProperty

我想做的是在列表框中为每个标记有IsImportant 属性的属性显示一个标签(带有属性名称)和一个控件(用于编辑)。绑定必须在所做的编辑、DataGrid 和支持对象之间起作用。显示的控件应根据属性的类型而有所不同,可以是booleanstringIEnumerable<string>(我写了一个IValueConverter 来在可枚举字符串和换行符分隔的字符串之间进行转换)。

有没有人知道实现这个的方法?我目前可以通过以下方式显示每个属性的值,但编辑它们不会更新支持对象:

listBox.ItemsSource = from p in typeof(MyClass).GetProperties()
                      where p.IsDefined(typeof(IsImportant), false)
                      select p.GetValue(_collectionViewSource.View.CurrentItem, null);

澄清一下,我希望这会“自动”发生,而不需要在 XAML 中手动指定属性名称。如果我可以在运行时根据哪些属性标记有属性来动态添加到 XAML ,那也不错。

【问题讨论】:

【参考方案1】:

您想要一个具有带有属性名称的标签的控件以及用于编辑属性值的控件,因此首先创建一个类,该类包装特定对象的属性以充当该控件的 DataContext:

public class PropertyValue

    private PropertyInfo propertyInfo;
    private object baseObject;

    public PropertyValue(PropertyInfo propertyInfo, object baseObject)
    
        this.propertyInfo = propertyInfo;
        this.baseObject = baseObject;
    

    public string Name  get  return propertyInfo.Name;  

    public Type PropertyType  get  return propertyInfo.PropertyType;  

    public object Value
    
        get  return propertyInfo.GetValue(baseObject, null); 
        set  propertyInfo.SetValue(baseObject, value, null); 
    

您希望将 ListBox 的 ItemsSource 绑定到一个对象,以便使用这些控件填充它,因此创建一个 IValueConverter 它将一个对象转换为 PropertyValue 对象列表以获取其重要属性:

public class PropertyValueConverter
    : IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    
        return
            from p in value.GetType().GetProperties()
            where p.IsDefined(typeof(IsImportant), false)
            select new PropertyValue(p, value);
    

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    
        return Binding.DoNothing;
    

最后一个技巧是您希望编辑控件根据属性的类型而变化。您可以通过使用 ContentControl 并将 ContentTemplate 设置为基于属性类型的各种编辑器模板之一来做到这一点。如果属性是布尔值,则此示例使用 CheckBox,否则使用 TextBox:

<DataTemplate x:Key="CheckBoxTemplate">
    <CheckBox IsChecked="Binding Value"/>
</DataTemplate>
<DataTemplate x:Key="TextBoxTemplate">
    <TextBox Text="Binding Value"/>
</DataTemplate>
<Style x:Key="EditControlStyle" TargetType="ContentControl">
    <Setter Property="ContentTemplate" Value="StaticResource TextBoxTemplate"/>
    <Style.Triggers>
        <DataTrigger Binding="Binding PropertyType" Value="x:Type sys:Boolean">
            <Setter Property="ContentTemplate" Value="StaticResource CheckBoxTemplate"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
<DataTemplate DataType="x:Type local:PropertyValue">
    <StackPanel Orientation="Horizontal">
        <Label Content="Binding Name"/>
        <ContentControl Style="StaticResource EditControlStyle" Content="Binding"/>
    </StackPanel>
</DataTemplate>

然后,您可以将 ListBox 创建为:

<ItemsControl ItemsSource="Binding Converter=StaticResource PropertyValueConverter"/>

【讨论】:

看起来棒极了;我会尽快实施它,并让你知道它是如何进行的。谢谢! 这非常有效。我刚刚向 PropertyValueConverter 添加了一个空检查,因为它在首次设置绑定时接收到一个空对象。唯一的问题是,目前,如果我更改此控件上的值,它不会传播到数据网格。推测支持对象正在更改,因为更改的值仍然由 itemscontrol 显示,但网格没有被通知。这与 Binding.DoNothing 有关吗? @Daniel:如果您希望一个控件所做的更新反映在绑定到同一属性的其他控件中,您的对象将需要实现 INotifyPropertyChanged。您还需要在 PropertyValue 上实现 INotifyPropertyChanged。 PropertyValueConverter 用于 ItemsSource 绑定本身,它是单向的,因此不会调用 ConvertBack 并且 Binding.DoNothing 无关紧要。

以上是关于WPF:将列表动态绑定到(某些)对象的属性的主要内容,如果未能解决你的问题,请参考以下文章

WPF Datagrid 动态生成列 并绑定数据

WPF DataGrid 将单元格绑定到具有动态列的数据模型

将 qml 中的值动态绑定到转发器创建的对象

wpf checkbox 如何绑定变量

WPF 动态生成对象属性 (dynamic)

将对象列表数据绑定到 WinForms DataGridView,但不显示某些公共属性