2009-06-05 17 views
13

Mam obiekt, który nie może odziedziczyć DependencyObject LUB użyć NotifyPropertyChanged, a ja powiązałem go z wieloma kontrolkami, więc gdy właściwości się zmieniają, nie chcę iść do każdej kontrolki i zmieniać jego wartość na kodzie, więc myślę, że musi być jakiś sposób, aby powiedzieć XAML do „Przypisz” wszystko to wiąże się z jednej lub dwóch linii kodu, zamiast iść:Ponowne wiązanie WPF

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

więc na , itd ...

jest to uproszczony przykład:

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

znalazłem sposób, aby to zrobić, ale to nie jest eleganckie w ogóle, i niestety, nie mogę po prostu ustawić DataContext do nowej instancji pogody, to musi być referencją SAME (dlatego ustawiłem ją na wartość null, aby uległa zmianie):

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

Z góry dziękuję!

+0

Curious: dlaczego nie możesz wdrożyć INPC? –

+0

Używamy Cofnij/Ponów w naszej aplikacji, INotifyPropertyChanging serializuje poprzedni stan Obiektu, INotifyPropertyChanged umożliwia zapisanie obiektu do nowego pliku XmlSerialized. Te konkretne cechy, które muszę zmienić, nie zmieniają stanu zachowania obiektu (nie zmieniają czcionki, koloru, tła, obramowania) ani niczego, co użytkownik chciałby zapisać. Jeśli I NotifyPropertyChanging/Changed z tymi właściwościami, system pomyśli, że obiekt został zmieniony, ale dla użytkownika nie był. Dlatego nie mogę tego użyć. – Carlo

+0

Zrozumiałem, ale to brzmi jak wadliwy projekt dla mnie. Lepiej radzić sobie z INPC jako powiadomieniem o zmianie właściwości ogólnej, a następnie innym mechanizmem do śledzenia zmian stanu, które zależy Ci na cofnięciu/ponowieniu. Może być jednak za późno, aby zmienić projekt, więc punkt zaczerpnięty. –

Odpowiedz

20

Jeśli masz dostęp do elementu, który chcesz zaktualizować powiązanie, możesz jawnie zaktualizować powiązanie. Możesz pobrać wyrażenie wiązania na elemencie, a następnie użyć UpdateTarget(), aby odświeżyć interfejs użytkownika lub UpdateSource, aby odświeżyć właściwość backing (jeśli chcesz powiązać coś edytowalnego jak TextBox).

Oto prosty przykład, który pokazuje, że:

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

(.. Polecam przy użyciu INotifyPropertyChanged chociaż jeśli to w ogóle możliwe, że sposób można wyodrębnić logiki z kodu tyłu)

+0

Ok to działa, jedynym problemem jest to, czy muszę to zrobić dla każdej kontroli, którą muszę zaktualizować? – Carlo

+0

Jeśli nie możesz zaimplementować INotifyPropertyChanged, to tak. Możesz utworzyć kolekcję potrzebnych BindingExpressions, a następnie użyć rozszerzenia linq. ForEach (b => b.UpdateTarget()). Jeśli jednak musisz aktualizować wiele rzeczy lub mieć dostęp tylko do niektórych kontrolek na raz, może być łatwiejsze "ukrycie" DataContext, jak już znalazłeś. – rmoore

+0

Wydaje mi się, że uaktualnienie każdej kontroli jest wykonalne, jedyną rzeczą, której nie podoba mi się to podejście, jest to, że jeśli dodaję nową kontrolę, będę musiał dodać ją do kodu, aby również zaktualizować swój cel. Ale nie jest to wielka sprawa. Dzięki! – Carlo