2009-06-05 7 views
13

J'ai un objet qui ne peut pas hériter de DependencyObject OU utiliser NotifyPropertyChanged, et je l'ai lié à un certain nombre de contrôles, donc quand les propriétés changent, je ne veux pas aller à chaque contrôle et changer sa valeur sur le code, donc je pense qu'il doit y avoir un moyen de dire XAML à « Rebind » tout ce qu'il est lié à une ou deux lignes de code, au lieu d'aller:WPF Force rebind

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

donc, , ainsi de suite ...

Voici un exemple simplificatrice:

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

J'ai trouvé une façon de le faire, mais ce n'est pas élégant du tout, et unfortunatelly, je ne peux pas régler le DataContext à une nouvelle instance de temps, il doit être la même référence (c'est pourquoi je l'ai mis à null afin qu'il change):

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

Merci d'avance!

+0

curieux: pourquoi ne pouvez-vous pas mettre en œuvre l'INPC? –

+0

Nous utilisons Undo/Redo sur notre application, INotifyPropertyChanging sérialise l'état précédent de l'objet, INotifyPropertyChanged permet d'enregistrer l'objet dans un nouveau fichier XmlSerialized. Cependant, ces propriétés spécifiques que je dois modifier ne modifient pas l'état de sauvegarde de l'objet (ne change pas la police, la couleur, l'arrière-plan, la bordure) ou tout ce que l'utilisateur voudrait sauvegarder. Si je NotifyPropertyChanging/Changed avec ces propriétés, le système pense que l'objet a été changé, mais pour l'utilisateur, ce n'était pas le cas. C'est pourquoi je ne peux pas l'utiliser. – Carlo

+0

Compris, mais cela me semble un design imparfait. Vous seriez mieux avec INPC comme une propriété générique modifiée notification, puis un autre mécanisme pour suivre les changements d'état que vous vous souciez de défaire/refaire. Peut-être trop tard pour changer votre design, donc le point pris. –

Répondre

20

Si vous avez accès à l'élément sur lequel vous souhaitez mettre à jour la liaison, vous pouvez explicitement mettre à jour la liaison. Vous pouvez récupérer l'expression de liaison sur l'élément, puis utiliser UpdateTarget() pour actualiser l'interface utilisateur ou UpdateSource pour actualiser la propriété de sauvegarde (si vous souhaitez lier à quelque chose de modifiable comme un TextBox).

Voici un exemple simple qui le démontre:

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

(.. Je recommanderais d'utiliser INotifyPropertyChanged bien si possible De cette façon, vous pouvez extraire la logique du code derrière)

+0

Ok cela fonctionne, le seul problème est, dois-je le faire pour chaque contrôle que j'ai besoin de mettre à jour? – Carlo

+0

Si vous ne pouvez pas implémenter INotifyPropertyChanged, alors oui. Vous pouvez créer une collection de BindingExpressions dont vous avez besoin, puis utiliser l'extension linq .ForEach (b => b.UpdateTarget()). Cependant, si vous devez mettre à jour beaucoup de choses, ou seulement avoir accès à certains contrôles à la fois, il peut être plus simple de "boucler" le DataContext comme vous l'avez déjà trouvé. – rmoore

+0

Je pense que la mise à jour de chaque contrôle est possible, la seule chose que je n'aime pas à propos de cette approche est que si j'ajoute un nouveau contrôle, je vais devoir l'ajouter dans le code pour mettre à jour sa cible. Mais pas un très gros problème. Merci! – Carlo