tags:

views:

1029

answers:

1
+2  Q: 

WPF Force rebind

I have an object that can't inherit DependencyObject OR use NotifyPropertyChanged, and I've binded it to quite a few controls, so when the properties change, I don't want to go to each control and change it's value on the code, so I'm thinking there must be a way to tell the XAML to "Rebind" all that it's bound to with one or two lines of code, instead of going:

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

So on, so forth...

This is an oversimplified example:

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

I found a way to do it, but it's not elegant at all, and unfortunatelly, I can't just set the DataContext to a new instance of weather, it needs to be the SAME reference (that's why I set it to null so it changes):

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

Thanks in advance!

+2  A: 

If you have access to the element that you want to update the binding on then you can explicitly update the binding. You can retrieve the Binding Expression on the element and then use UpdateTarget() to refresh the UI, or UpdateSource to refresh the backing property (if you want to bind to something editable like a TextBox).

Here's a simple example that demonstrates it:

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

(I would recommend using INotifyPropertyChanged though if at all possible. That way you can extract the logic from the code behind.)

rmoore
Ok this works, the only problem is, do I have to do it for every control that I need to update?
Carlo
If you can't implement INotifyPropertyChanged, then yes. You could create a collection of the BindingExpressions you need, and then use the linq extension .ForEach(b => b.UpdateTarget()). If you have to update a lot of things though, or only have access to some of the controls at a time, then it may be simpler to 'cycle' the DataContext like you've already found.
rmoore
I think updating every control is feasible, the only thing I don't like about that approach is that if I add a new control I'm going to have to add it in the code to update its target too. But not a very big deal. Thanks!
Carlo