2010-06-30 18 views
8

Ich habe eine Sammlung von Objekten in einem CollectionViewSource gespeichert und an eine DataGrid gebunden. Ich möchte eine 'Detailansicht' des aktuell ausgewählten Objekts im DataGrid anzeigen. Ich kann das aktuelle Objekt mit CollectionViewSource.View.CurrentItem erhalten.WPF: Dynamische Bindung einer Liste an (einige) Eigenschaften eines Objekts

MyClass 
{ 
    [IsImportant] 
    AProperty{} 

    AnotherProperty{} 

    [IsImportant] 
    YetAnotherProperty{} 
} 

Was würde ich tun möchte, ist Display ein Label (mit dem Eigenschaftsnamen) und eine Steuerung (für die Bearbeitung) in einer Listbox, für jede dieser Eigenschaften mit dem IsImportant Attribute gekennzeichnet. Die Bindung muss zwischen den vorgenommenen Änderungen, dem DataGrid und dem Sicherungsobjekt funktionieren. Das angezeigte Steuerelement sollte basierend auf dem Typ der Eigenschaft variieren, der entweder boolean, string oder IEnumerable<string> sein kann (Ich habe eine IValueConverter geschrieben, um zwischen aufzählbarer und durch Zeilentrennzeichen getrennter Zeichenfolge zu konvertieren).

Kennt jemand eine Methode, um dies zu erreichen? Ich kann zur Zeit der Werte der Eigenschaften durch die folgende Anzeige, aber bearbeite sie würde die Unterstützung Objekt nicht aktualisieren:

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

Um zu klären, ich diese ‚automagically‘, passieren möchte, ohne manuell angeben Eigenschaftsnamen in das XAML. Wenn ich dynamisch zur Laufzeit XAML hinzufügen kann, basierend darauf, welche Eigenschaften mit Attributen markiert sind, wäre das auch in Ordnung.

Antwort

12

Sie wollen ein Steuerelement, das ein Etikett mit dem Eigenschaftsnamen und Kontrolle hat die Eigenschaft Wert zu bearbeiten, so starten, indem Sie eine Klasse erstellen, die eine Eigenschaft eines bestimmten Objekts wickelt für das Steuerelement als Datacontext zu handeln:

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

Sie wollen die Itemssource einer List-Box auf ein Objekt zu binden, um es mit diesen Kontrollen zu füllen, so einen IValueConverter erstellen, die ein Objekt in eine Liste von Property Objekten für seine wichtigen Eigenschaften umwandeln:

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

Der letzte Trick Sie möchten, dass das Bearbeitungssteuerelement basierend auf dem Typ der Eigenschaft variiert. Sie können dies tun, indem Sie ein ContentControl verwenden und das ContentTemplate basierend auf dem Eigenschaftstyp auf eine der verschiedenen Editorvorlagen setzen. In diesem Beispiel wird eine CheckBox, wenn die Eigenschaft ein Boolean ist und ein TextBox anders:

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

Dann können Sie einfach Ihre List-Box erstellen, wie:

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

Das sieht fantastisch; Ich werde es umsetzen, sobald ich eine Chance habe und Sie wissen lassen, wie es geht. Vielen Dank! –

+0

Das funktioniert meistens super. Ich habe dem PropertyValueConverter gerade einen Null-Check hinzugefügt, da er beim ersten Setzen der Bindung ein Null-Objekt erhalten hat. Das einzige Problem ist, dass derzeit, wenn ich einen Wert für dieses Steuerelement ändere, es nicht zum Datagrid weitergeleitet wird. Vermutlich wird das Backing-Objekt geändert, da der geänderte Wert immer noch von itemscontrol angezeigt wird, aber das Grid nicht benachrichtigt wird. Hat das mit Binding zu tun? –

+1

@Daniel: Ihr Objekt muss INotifyPropertyChanged implementieren, wenn Aktualisierungen, die von einem Steuerelement vorgenommen werden, in anderen Steuerelementen widergespiegelt werden sollen, die an dieselbe Eigenschaft gebunden sind. Sie müssen auch INotifyPropertyChanged für PropertyValue implementieren. Der PropertyValueConverter ist für die ItemsSource-Bindung selbst gedacht, was in einer Richtung erfolgt, so dass ConvertBack nicht aufgerufen wird und Binding.DoNothing keine Rolle spielt. – Quartermeister

Verwandte Themen