2010-06-30 10 views
8

Mam kolekcję obiektów przechowywanych w CollectionViewSource i związanych z DataGrid. Chcę wyświetlić "szczegółowy widok" obiektu aktualnie wybranego w DataGrid. Mogę uzyskać bieżący obiekt za pomocą CollectionViewSource.View.CurrentItem.WPF: Dynamiczne wiązanie listy do (niektórych) właściwości obiektu

MyClass 
{ 
    [IsImportant] 
    AProperty{} 

    AnotherProperty{} 

    [IsImportant] 
    YetAnotherProperty{} 
} 

Co chciałbym zrobić, to wyświetlacz etykieta (z nazwą własności) oraz sterowanie (do edycji) w listbox, dla każdej z tych właściwości oznaczone atrybutemIsImportant. Powiązanie musi działać między wprowadzonymi zmianami, DataGrid i obiektem podkładu. Wyświetlona kontrola powinna być różna w zależności od typu właściwości, która może być boolean, string lub IEnumerable<string> (napisałem IValueConverter, aby przekonwertować ciąg wyliczalny i znakowany nowymi liniami).

Czy ktoś wie o metody realizacji tego? Mogę obecnie wyświetla wartości każdej nieruchomości przez następujące, ale edytowanie ich nie aktualizuje obiekt Podłoże:

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

do wyjaśnienia, chciałbym aby tak się stało „automagicznie”, bez konieczności ręcznego określania nazwy właściwości w XAML. Jeśli mogę dynamicznie dodawać do XAML w czasie wykonywania w oparciu o które właściwości są oznaczone atrybutami, byłoby to również w porządku.

Odpowiedz

12

Chcesz sterowania, który ma etykietę z nazwą własności i kontroli, aby zmienić wartość właściwości, więc zacząć od stworzenia klasy, która otacza właściwość określonego obiektu działać jako DataContext dla tej kontroli:

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

chcesz wiązać ItemsSource z ListBox do obiektu w celu wypełnienia go z tych kontroli, więc utworzyć IValueConverter że będzie konwertować obiekt do listy obiektów WartośćWłaściwości dla swoich istotnych właściwości:

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; 
    } 
} 

Ostatnia sztuczka jest to, że chcesz, aby kontrola edycji była zależna od typu właściwości. Możesz to zrobić, używając ContentControl i ustawiając ContentTemplate na jeden z różnych szablonów edytora opartych na typie właściwości. Ten przykład wykorzystuje pole, jeśli nieruchomość jest logiczna i TextBox inaczej:

<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> 

Następnie można po prostu stworzyć swój ListBox jako:

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

To wygląda fantastycznie; Zaimplementuję to tak szybko, jak tylko będę miał szansę i dam ci znać, jak to działa. Dzięki! –

+0

Działa to głównie świetnie. Właśnie dodałem zerowy czek do PropertyValueConverter, ponieważ otrzymywał obiekt zerowy po pierwszym ustawieniu wiązania. Jedynym problemem jest to, że obecnie, jeśli zmienię wartość tej kontrolki, nie zostanie ona przekazana do datagridu. Prawdopodobnie zmienia się obiekt podkładu, ponieważ zmieniona wartość jest nadal pokazywana przez sterowanie elementami, ale siatka nie jest powiadamiana. Czy ma to związek z Binding.DoNothing? –

+1

@Daniel: Twój obiekt będzie musiał zaimplementować INotifyPropertyChanged, jeśli chcesz, aby aktualizacje wykonane przez jedną kontrolkę znalazły odzwierciedlenie w innych kontrolkach powiązanych z tą samą usługą. Będziesz także musiał wdrożyć INotifyPropertyChanged na PropertyValue. Właściwość PropertyValueConverter dotyczy samego powiązania ItemsSource, które jest jednokierunkowe, więc ConvertBack nie zostanie wywołane, a funkcja Binding.DoNothing nie będzie miała znaczenia. – Quartermeister