2009-06-05 12 views
13

Ich habe ein Objekt, das DependencyObject nicht erben kann ODER NotifyPropertyChanged verwenden, und ich habe es mit ziemlich vielen Steuerelementen verknüpft. Wenn sich die Eigenschaften ändern, möchte ich nicht zu jedem Steuerelement wechseln und ändern es ist Wert auf dem Code, so dass ich denke, es eine Möglichkeit sein muss, die XAML zu sagen, „Rebind“ alles, was es mit einer oder zwei Zeilen Code gebunden ist zu, anstatt zu gehen:WPF-Force-Rebind

label1.Content = myObject.DontNotifyThis; 
label2.Content = myObject.DontNotifyThisEither; 
label3.Content = myObject.DontEvenThinkOfNotifyingThis; 
label4.Content = myObject.NotSoFastPal; 

so auf , usw ...

Dies ist ein stark vereinfachten Beispiel:

XAML:

<Window x:Class="StackOverflowTests.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" x:Name="window1" Height="300" Width="300" Loaded="window1_Loaded"> 
    <Grid x:Name="gridMain"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <Label Grid.Row="0" Content="{Binding Status}" ContentStringFormat="Today's weather: {0}" /> 
     <Label Grid.Row="2" Content="{Binding Temperature}" ContentStringFormat="Today's temperature: {0}" /> 
     <Label Grid.Row="1" Content="{Binding Humidity}" ContentStringFormat="Today's humidity: {0}" /> 
    </Grid> 
</Window> 

C#:

using System.Windows; 

namespace StackOverflowTests 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     Weather weather = new Weather("Cloudy", "60F", "25%"); 

     public Window1() 
     { 
      InitializeComponent(); 
      this.DataContext = weather; 
     } 

     private void window1_Loaded(object sender, RoutedEventArgs e) 
     { 
      weather.Status = "Sunny"; 
      weather.Temperature = "80F"; 
      weather.Humidity = "3%"; 
     }  
    } 

    class Weather 
    { 
     public string Status { get; set; } 
     public string Temperature { get; set; } 
     public string Humidity { get; set; } 

     public Weather(string status, string temperature, string humidity) 
     { 
      this.Status = status; 
      this.Temperature = temperature; 
      this.Humidity = humidity; 
     } 
    } 
} 

fand ich einen Weg, es zu tun, aber es ist durchaus nicht elegant, und leider kann ich nicht nur die Datacontext auf eine neue Instanz von Wetter eingestellt, es muss die SAME-Referenz sein (deshalb setze ich sie auf Null, damit sie sich ändert):

private void window1_Loaded(object sender, RoutedEventArgs e) 
{ 
    weather.Status = "Sunny"; 
    weather.Temperature = "80F"; 
    weather.Humidity = "3%"; 

    // bad way to do it 
    Weather w = (Weather)this.DataContext; 
    this.DataContext = null; 
    this.DataContext = w; 
} 

Vielen Dank im Voraus!

+0

Kurios: warum Kannst du INPC nicht implementieren? –

+0

Wir verwenden in unserer App Undo/Redo, INotifyPropertyChanging serialisiert den vorherigen Status des Objekts, INotifyPropertyChanged ermöglicht das Speichern des Objekts in einer neuen XmlSerialized-Datei. Diese spezifischen Eigenschaften, die ich ändern muss, ändern jedoch nicht den Speicherstatus des Objekts (Schriftart, Farbe, Hintergrund, Rahmen) oder alles, was der Benutzer speichern möchte. Wenn ich NotifyPropertyChanging/Change mit diesen Eigenschaften anwähle, denkt das System, dass das Objekt geändert wurde, aber für den Benutzer nicht. Deshalb kann ich das nicht benutzen. – Carlo

+0

Verstanden, aber das klingt für mich wie ein fehlerhaftes Design. Sie wären besser mit INPC als generische Eigenschaft geänderte Benachrichtigung und dann ein anderer Mechanismus zum Verfolgen von Statusänderungen, die Sie für das Rückgängigmachen/Wiederherstellen interessieren. Vielleicht ist es zu spät, um Ihr Design zu ändern, also Punkt gemacht. –

Antwort

20

Wenn Sie Zugriff auf das Element haben, für das Sie die Bindung aktualisieren möchten, können Sie die Bindung explizit aktualisieren. Sie können den Bindungsausdruck für das Element abrufen und dann UpdateTarget() verwenden, um die UI zu aktualisieren, oder UpdateSource, um die Backing-Eigenschaft zu aktualisieren (wenn Sie an eine bearbeitbare Komponente wie eine TextBox binden möchten).

Hier ist ein einfaches Beispiel, das es zeigt:

<StackPanel> 
    <TextBlock x:Name="uiTextBlock" Text="{Binding MyString}" /> 
    <Button Click="Button_Click" 
      Content="Rebind" /> 
</StackPanel> 

public partial class Window1 : Window 
{ 
    public string MyString { get; set; } 

    public Window1() 
    { 
     MyString = "New Value"; 

     InitializeComponent(); 
     this.DataContext = this; 
    } 
    int count = 0; 
    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     MyString = "Rebound " + ++count + " times"; 

     var bindingExpression = uiTextBlock.GetBindingExpression(TextBlock.TextProperty); 
     bindingExpression.UpdateTarget(); 
    } 
} 

(.. Ich INotifyPropertyChanged wenn möglich, wenn überhaupt verwenden würde empfehlen, diese Weise können Sie die Logik aus dem Code hinter extrahieren kann)

+0

Ok, das funktioniert, das einzige Problem ist, muss ich es für jedes Steuerelement tun, das ich aktualisieren muss? – Carlo

+0

Wenn Sie INotifyPropertyChanged nicht implementieren können, dann ja. Sie können eine Sammlung der benötigten BindingExpressions erstellen und dann die linq-Erweiterung .ForEach (b => b.UpdateTarget()) verwenden. Wenn Sie jedoch viele Dinge aktualisieren müssen oder nur auf einige Steuerelemente gleichzeitig zugreifen können, ist es möglicherweise einfacher, den DataContext so zu steuern, wie Sie ihn bereits gefunden haben. – rmoore

+0

Ich denke, jedes Update ist machbar, das einzige, was ich an diesem Ansatz nicht mag, ist, dass ich, wenn ich ein neues Steuerelement hinzufüge, es in den Code hinzufügen muss, um sein Ziel zu aktualisieren. Aber keine große Sache. Vielen Dank! – Carlo