Reputation: 7692
We are converting an app from Silverlight to WPF. It's a fairly complex app, but the code sharing is about 95% +. The XAML is pretty much all the same except for XML namespace definitions etc. About 90% of the app now works but there are a few glaring issues that are puzzling me. One is this binding issue.
We have a model object called TaskInfo. It has a property called TaskNo. in Silverlight and WPF we bind to this property like this
<TextBox IsReadOnly="True" Grid.Column="0" Grid.Row="0" Margin="1" Text="{Binding Path=TaskNo}" Height="28" Background="#CAECF4" VerticalAlignment="Center" VerticalContentAlignment="Center" />
In both WPF and Silverlight the TaskNo is correctly displayed when the TaskInfo model is first set as the DataContext. In Silverlight, if we create a new TaskInfo, send it to the server for saving, and return the model with a new TaskNo, the TaskNo is successfully displayed. But, in WPF, it just displays 0 when the saved TaskInfo is returned from the server. There is some issue with binding. This is the binding error I see in the output window:
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=TaskNo; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I inspected the visual tree and the TextBox's DataContext is set to the TaskInfo as expected.
So, I turned off binding and tried this code. It's the event handler for the DataContextChanging on the TextBox. This code works fine. When a new task is saved and returned, the TaskNo successfully displays here:
private void TaskNoBox_DataContextChanging(object sender, DependencyPropertyChangedEventArgs e)
{
var task = TaskNoBox.DataContext as TaskInfo;
if (task == null)
{
throw new Exception("Ouch!");
}
TaskNoBox.Text = task.TaskNo.ToString();
}
To further debug this problem, I added this event handler for the GotFocus event on the text box. So, after the task has been saved on the server side and has been returned and set as the DataContext, I click inside the control to fire this event handler. When I step through this code, I can see that the DataContext is correct, and has the correct TaskNo. Calling this code still doesn't cause the binding to occur.
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var textBox = (TextBox)sender;
var be = textBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
be.UpdateTarget();
}
TextBox Text binding property:
DataContext of TextBox's properties:
How do I make sense of this binding error? What are the binding gotchas between Silverlight and WPF? Do I need some kind of workaround? Why is binding not working?
Upvotes: 0
Views: 1428
Reputation: 7692
Binding in WPF never updates if the previous DataContext is equivalent to the new DataContext according to the Equals method.
The difference between Silverlight and WPF seems to be that when the DataContext changes, WPF seems to use the Equals method to evaluate difference between objects while Silverlight uses the reference. That means that WPF is the same as Xamarin.Forms.
I tried this code, and it causes the TaskNo to display correctly. I think what is happening is that because the previous DataContext was equivalent to the new DataContext when Equals is called. So, this works around the problem.
private async void TaskPageHeader_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
TaskNoBox.DataContext = new object();
TaskNoBox.DataContext = CurrentTask;
}
Upvotes: 2