2010-06-30 111 views
8

我有一个对象集合,存储在CollectionViewSource中并绑定到DataGrid我想在DataGrid中显示当前所选对象的'详细视图'。我可以使用CollectionViewSource.View.CurrentItem获得当前对象。WPF:将列表动态绑定到(某些)对象的属性

MyClass 
{ 
    [IsImportant] 
    AProperty{} 

    AnotherProperty{} 

    [IsImportant] 
    YetAnotherProperty{} 
} 

我想这样做是在一个列表框显示一个标签(与属性名)和控制(编辑),对每个属性的标有IsImportant属性。绑定必须在所做的编辑,DataGrid和后台对象之间起作用。显示的控件应根据属性的类型而有所不同,可以是boolean,stringIEnumerable<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中,那也可以。

回答

12

你要具有与属性名和控件来编辑属性值标签的控制,所以通过创建一个包装了特定对象的属性作为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设置为各种编辑器模板之一来完成此操作。这个例子使用一个复选框,如果该属性是一个布尔否则一个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> 

然后,你可以创建你的列表框为:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyValueConverter}}"/> 
+0

这看起来太棒了;我会尽快实施它,并让你知道它是如何发生的。谢谢! – 2010-07-01 16:53:26

+0

这个工程大多很棒。我刚刚向PropertyValueConverter添加了一个空检查,因为它在第一次设置绑定时正在接收一个空对象。唯一的问题是,目前,如果我更改此控件的值,它不会传播到数据网格。据推测支持对象正在改变,因为改变的值仍然由itemscontrol显示,但网格没有被通知。这是否与Binding.DoNothing? – 2010-07-01 19:27:08

+1

@Daniel:如果您想让一个控件所做的更新反映到绑定到同一个属性的其他控件中,您的对象将需要实现INotifyPropertyChanged。您还需要在PropertyValue上实现INotifyPropertyChanged。 PropertyValueConverter是用于ItemsSource绑定本身的,这是单向的,所以ConvertBack不会被调用,而Binding.DoNothing也不会有问题。 – Quartermeister 2010-07-02 02:54:49