WPF: динамическая привязка списка к (некоторым) свойствам объекта - PullRequest
8 голосов
/ 30 июня 2010

У меня есть коллекция объектов, хранящихся в CollectionViewSource и привязанных к DataGrid. Я хочу отобразить «подробный вид» объекта, выбранного в данный момент в DataGrid.Я могу получить текущий объект, используя CollectionViewSource.View.CurrentItem.

MyClass
{
    [IsImportant]   
    AProperty{}

    AnotherProperty{}

    [IsImportant]
    YetAnotherProperty{}
}

Что я хотел бы сделать, это отобразить метку (с именем свойства) и элемент управления (для редактирования) в списке,для каждого из этих свойств, помеченных атрибутом IsImportant .Привязка должна работать между внесенными изменениями, DataGrid и вспомогательным объектом.Отображаемый элемент управления должен различаться в зависимости от типа свойства, которое может быть boolean, string или IEnumerable<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 Ответ

12 голосов
/ 01 июля 2010

Вы хотите, чтобы элемент управления, имеющий метку с именем свойства и элемент управления, редактировали значение свойства, поэтому начните с создания класса, который обернет свойство определенного объекта, чтобы действовать как 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); }
    }
}

Вы хотите связать ItemsSource объекта ListBox с объектом, чтобы заполнить его этими элементами управления, поэтому создайте 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}}"/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...