2009-04-22 3 views
6

Ich versuche, die Text-Eigenschaft eines TextBlocks sehr dynamisch zu binden. Ich muss den Pfad von einem zugrunde liegenden Objekt erhalten.Wie binde ich die Binding.Path-Eigenschaft an die zugrunde liegenden Daten?

Hier ist die Datatemplate:

<DataTemplate DataType={x:Type local:DummyClass}> 
    <TextBlock Text={Binding Path=???} /> 
</DataTemplate> 

Das DummyClass Objekt verfügt über eine Eigenschaft mit dem Namen „FieldValuePath“ - der Weg, der gesetzt werden muss, wo das ??? ist.

Die Idee dahinter ist, dass die Datenvorlage eine GUI zum Anzeigen/Bearbeiten einer beliebigen Eigenschaft eines Objekts sein soll. Es ist also besser, XAML deklarieren zu können, das einige Steuerelemente (Textfelder, Textblöcke, Datumsauswahl usw.) an eine bestimmte Eigenschaft bindet.

Vielleicht hat jemand irgendwelche Vorschläge, wie man so etwas implementieren?

Antwort

7

Wenn Sie die Bindung in den Code hinter erstellen, dann könnten Sie es zum Laufen bringen. Zum Beispiel ist ein einfacher Code erzeugte Bindung:

Binding binding = new Binding("BindingPath"); 
binding.Mode = BindingMode.TwoWay; 
BindingOperations.SetBinding(textBoxName, TextBox.TextProperty, binding); 

Da der Weg in dieser Bindung („BindingPath“) ist nur eine Zeichenfolge, könnte diese Zeichenfolge kommt aus jedem beliebigen Objekt.

Sie müssen jedoch in die Erstellung Ihrer Datenelemente eingreifen, um diese Bindung festzulegen.


Eine weitere Möglichkeit, basierend auf Ihre Kommentare:

This blog post beschreibt eine Möglichkeit, eine benutzerdefinierte Bindung Klasse durch Erben von Markupextension zu erstellen. Sie können dies möglicherweise als Ausgangspunkt verwenden, um meinen Vorschlag in ein wiederverwendbares Xaml-Markup für Ihren speziellen Bindungsfall einzubetten.


Weitere Gedanken:

Okay, das war ein interessantes Problem, so habe ich beschlossen, zu sehen, eine wenig Zeit zu verbringen, wenn ich mit einer Arbeitslösung kommen könnte. Ich entschuldige mich für die Länge der folgenden Codebeispiele im Voraus ...

Gründend meine Lösung auf der Blog-Post ich oben I verknüpfen diese Klasse erstellt:

public class IndirectBinder : MarkupExtension 
    { 
     public string IndirectProperty { get; set; } 

     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
      //try to get bound items for our custom work 
      DependencyObject targetObject; 
      DependencyProperty targetProperty; 
      bool status = TryGetTargetItems(serviceProvider, out targetObject, out targetProperty); 

      if (status) 
      { 
       Control targetControl = targetObject as Control; 
       if (targetControl == null) return null; 

       //Find the object to take the binding from 
       object dataContext = targetControl.DataContext; 
       if (dataContext == null) return null; 

       //Reflect out the indirect property and get the value 
       PropertyInfo pi = dataContext.GetType().GetProperty(IndirectProperty); 
       if (pi == null) return null; 

       string realProperty = pi.GetValue(dataContext, null) as string; 
       if (realProperty == null) return null; 

       //Create the binding against the inner property 
       Binding binding = new Binding(realProperty); 
       binding.Mode = BindingMode.TwoWay; 
       BindingOperations.SetBinding(targetObject, targetProperty, binding); 

       //Return the initial value of the binding 
       PropertyInfo realPi = dataContext.GetType().GetProperty(realProperty); 
       if (realPi == null) return null; 

       return realPi.GetValue(dataContext, null); 

      } 

      return null; 

     } 

     protected virtual bool TryGetTargetItems(IServiceProvider provider, out DependencyObject target, out DependencyProperty dp) 
     { 
      target = null; 
      dp = null; 
      if (provider == null) return false; 

      //create a binding and assign it to the target 
      IProvideValueTarget service = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget)); 
      if (service == null) return false; 

      //we need dependency objects/properties 
      target = service.TargetObject as DependencyObject; 
      dp = service.TargetProperty as DependencyProperty; 
      return target != null && dp != null; 
     } 

Sie können diese neue Markup mit dem Einsatz folgende xAML:

<TextBox Text="{local:IndirectBinder IndirectProperty=FieldValuePath}"/> 

Wo TextBox jede Klasse sein kann, die von der Kontrolle und Text erbt kann jede Abhängigkeitseigenschaft sein.

Offensichtlich müssen Sie der Klasse weitere Eigenschaften hinzufügen, wenn Sie andere Datenbindungsoptionen verfügbar machen müssen (z. B. ein- oder zweiseitige Bindung).

Während dies eine komplizierte Lösung ist, hat ein Vorteil gegenüber einem Konverter, dass die Bindung, die schließlich erstellt wird, gegen die tatsächliche innere Eigenschaft und nicht gegen das Objekt gerichtet ist. Dies bedeutet, dass es korrekt auf PropertyChanged-Ereignisse reagiert.

+0

Nun, dies ist ein Ansatz, der funktionieren könnte, aber ich würde wirklich gerne Bindungen in XAML erstellen können, weil ich verschiedene Schreibeigenschaften schreiben möchte, abhängig vom Zustand der zugrunde liegenden Daten. Aber möglicherweise könnte ich tatsächlich versuchen, all diese Logik in den Code-hinter zu schreiben ... – arconaut

+0

und außerdem, es ist nicht nur TextBlock, den ich für verbindlich festlegen möchte. Es sollte ein Editor/Viewer für jede Eigenschaft eines Objekts sein. Ich denke, ich sollte zum Top Post hinzufügen. – arconaut

+0

danke für die antwort, und danke für den link. Ich werde das ausprobieren – arconaut

-1
<DataTemplate DataType={x:Type local:DummyClass}> 
    <TextBlock Text={Binding Path=FieldValuePath} /> 
</DataTemplate> 

Sollte die richtige Methode sein.Wenn Sie auf Änderungen in FieldValuePath achten, müssen Sie sicherstellen, dass DummyClass von INotifyPropertyChanged erbt und dass das geänderte Ereignisereignis ausgelöst wird, wenn sich FieldValuePath ändert.

+0

Entschuldigung, aber dies würde den Text auf den Wert FieldValuePath gleich setzen. Wenn Sie also dummyObject.FieldValuePath == "Property1" haben, erhalten Sie TextBlock.Text == "Property1" – arconaut

+0

Sind in diesem Fall alle Ihre Eigenschaften als DependencyProperties deklariert? Sie sollten in der Lage sein, an die Eigenschaft itsel zu binden, wenn dies der Fall ist. Dies könnte helfen ... http://geekswithblogs.net/thibbard/archive/2008/04/22/wpf-custom-control-dependency-property-gotcha.aspx –

2

Ich würde empfehlen, einen Konverter mit:

<DataTemplate DataType={x:Type local:DummyClass}> 
    <TextBlock Text={Binding Converter={StaticResource PropertyNameToValueConverter, ConverterParameter=FieldValuePath}} /> 
</DataTemplate> 

Der Konverter die Klasse und den Namen der Eigenschaft von dort würde es den Wert mit Reflexion bekommen würde und zurück.

+0

Aber es würde es nur einmal zurückgeben. – arconaut