Why do I get Inconsistent Binding results - silverlight

I have a control template with a toggle button. This ToggleButton has it's IsChecked property one way bound to a dependancy property. If i set the dependancy property explicitly the binding works.
The problem is that after I interact with the toggle button in the UI, the bindings don't update the IsChecked property if I set the dependency property explicitly.
I do have a work arround using TwoWay binding which works fine. My question is, why does it behave this way? Am I missing something? Is there a bug in the binding mechanism of Silverlight?
EDIT TO INCLUDE SNIPPET:
The binding in the ControlTemplate looks something like (could be replaced with TemplateBinding)
<ToggleButton x:Name="PlayPause" Grid.Column="0"
IsChecked="{Binding Paused, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
Width="50" Height="50"/>
The explicit setting of the dependency property is the fairly bog standard:
myComponent.Paused = true;

WPF deletes one way bindings when the target property (IsChecked in this case) is modified. Silverlight used to keep the binding when IsChecked was modified. If Paused was later set, this value would overwrite IsChecked as well.
According to you, it seems Silverlight reverted to WPF behavior. Oh well. Personally, I consider modifying a binded property a bug. If the properties are not meant to be in sync commanding may be a better solution.

You should use TwoWay binding
Make sure that the object that contains your Paused property supports INotifyPropertyChanged.
Make sure that the setter for Paused triggers the PropertyChanged event

Related

Is WPF UpdateSourceTrigger always needed to notify model of changes in collection?

Question: Was UpdateSourceTrigger always necessary to have properties update the source? I seem to recall that Mode=TwoWay was enough a long time ago. But now, I have to UpdateSourceTrigger=PropertyChanged? Maybe I'm losing it...
<DataGridTemplateColumn Header="Hub" Width="SizeToHeader">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="XHub" IsChecked="{Binding Hub, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
When I omit that part of the binding the model is never called when a value in the ObservableCollection changes.
Both Mode and UpdateSourceTrigger properties of the binding depends on the dependency property you are binding to. According to MSDN:
The default is Default, which returns the default UpdateSourceTrigger value of the target dependency property. However, the default value for most dependency properties is PropertyChanged, while the Text property has a default value of LostFocus.
A programmatic way to determine the default UpdateSourceTrigger value of a dependency property is to get the property metadata of the property using GetMetadata and then check the value of the DefaultUpdateSourceTrigger property.
So in your case you are binding to IsChecked that is defined in ToogleButton class as following:
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof (bool?), typeof (ToggleButton),
(PropertyMetadata) new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(ToggleButton.OnIsCheckedChanged)));
So BindsTwoWayByDefault makes Mode=TwoWayredundant, while
querying metadata:
var def = ((FrameworkPropertyMetadata)CheckBox.IsCheckedProperty.GetMetadata(typeof(CheckBox))).DefaultUpdateSourceTrigger;
results in PropertyChanged, that makes the UpdateSourceTrigger=PropertyChanged part also redundant.
Edit: about this part:
When I omit that part of the binding the model is never called when a value in the ObservableCollection changes.
Sincerely, I cannot explain myself the behavior that you've noticed (but it's quite easy to reproduce). I was expecting the binding to update viewmodel according to dependency property default UpdateSourceTrigger. But I've noticed that the ViewModel is updated not even on focus lost, but when you either move focus to next row or press enter. So the explanation that Colin Eberhardt gives in his blog seems the best I can find. Obviously this behavior is strongly related to DataGrid and if you would have the same checkbox outside of the grid, then the ViewModel would update as expected without explicit UpdateSourceTrigger set to PropertyChanged.
When you bind to a DataTable, you are actually binding to your DataTable's DefaultView, which is of type DataView. As a result, each row of your table will be bound to a DataRowView. If you look at the documentation for DataRowView you will find that it implements the IEditableObject interface which is the significant factor here. This interface allows you to perform transactional changes to your object, i.e. you can change the object's properties within a 'transaction', then commit then all in a single atomic action. By default, when you bind to a DataGrid this occurs when the user finishes editing a row, either by moving focus or hitting Enter.
Was UpdateSourceTrigger always necessary to have properties update the source?
No. The UpdateSourceTrigger property of a binding specifies what triggers the update of a source property whereas the Mode property lets you control the direction of the data flow. These are two different things.
Even if you don't explicitly set the UpdateSourceTrigger property your Hub source property will indeed still be set, but not until the you step out of the cell of the DataGrid.

Binding UI-element to UI-element like Textbox to Textbox

I have UI-elements like a textbox and I want to bind them to a ViewModel. I need to access many properties of the textbox like the text- or the IsEnabled-property.
Is it possible that I bind the textbox directly to another textbox in the ViewModel with all their properties instead of binding every single property to properties?
Yes, using an ElementName, but you still bind all properties though.
<!-- Bound to ViewModel -->
<TextBox Name="tbOne" IsEnabled="{Binding OneIsEnabled}" Text={Binding TextOne}/>
<TextBox Name="tbTwo" IsEnabled="{Binding ElementName=tbOne, Path=IsEnabled}" Text={Binding ElementName=tbOne, Path=Text}/>
There is no built-in way to bind all dependency properties of the TextBox to another. Personally, I would prefer binding directly to the ViewModel.
An alternative solution would be to create a UserControl that internally clones the TextBox with all bindings:
<CloneControl Target="{Binding ElementName=tbOne}"/>
Here, the ControlControl would inspect the target, and have code that create a new TextBox, and sets the bindings in code. This is only useful if you are doing this very often, and there is a slightly performance price to pay, as you are adding another level of controls to the UI tree.

In WPF, why doesn't TemplateBinding work where Binding does?

Ok... this is leaving me scratching my head. I have two WPF controls--one's a user control and the other's a custom control. Let's call them UserFoo and CustomFoo. In the control template for CustomFoo, I use an instance of UserFoo which is a named part so I can get to it after the template is applied. That works fine.
Now both UserFoo and CustomFoo have a Text property defined on them (independently, i.e. not a shared DP using AddOwner. Don't ask...) that are both declared like this...
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(UserFoo), // The other is CustomFoo
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
null,
null,
true,
UpdateSourceTrigger.PropertyChanged
)
);
Notice specifically that the mode is set to TwoWay and the UpdateSourceTrigger is set to PropertyChanged, again for both.
So in the style template for CustomFoo, I want to bind CustomFoo's Text property as the source to the internal UserFoo's Text property. Normally, this is easy. You just set UserFoo's text property to "{TemplateBinding Text}" but for some reason it's only going one way (i.e. UserFoo is properly set from CustomFoo, but not the reverse), even though again, both DPs are set for two-way! However, when using a relative source binding instead of a template binding, it works great! Um... wha??
// This one works
Text="{Binding Text, RelativeSource={RelativeSource AncestorType={local:CustomFoo}}, Mode=TwoWay}"
// As does this too...
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
// But not this one!
Text="{TemplateBinding Text}"
So what gives? What am I missing?
Found this forum post on MSDN: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0bb3858c-30d6-4c3d-93bd-35ad0bb36bb4/
It says this:
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with
{Binding RelativeSource={RelativeSource TemplatedParent}}
Note from OP: Contrary to what it says in the documentation, in actuality, it should be this...
{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}
I filed a complaint against the docs, and while they did add a sentence now stating they are always one-way, the code example still doesn't list the mode, but I guess it's better than nothing.)
The TemplateBinding transfers data from the templated parent to the property that is template bound. If you need to transfer data in the opposite direction or both ways, create a Binding with RelativeSource of TemplatedParent with the Mode property set to OneWayToSource or TwoWay.
More in: http://msdn.microsoft.com/en-us/library/ms742882.aspx
Looks like Mode=OneWay is one of the "Optimizations" of using a TemplateBinding
TemplateBinding does not support two-way binding, only Binding does that. Even with your BindsTwoWayBeDefault option, it won't support two-way binding.
More info can be found here, but to summarize:
However, a TemplateBinding can only
transfer data in one direction: from
the templated parent to the element
with the TemplateBinding. If you need
to transfer data in the opposite
direction or both ways, a Binding with
RelativeSource of TemplatedParent is
your only option. For example,
interaction with a TextBox or Slider
within a template will only change a
property on the templated parent if
you use a two-way Binding.

How to set ItemsSource?

This dialog makes no sense to me
http://img576.imageshack.us/img576/4223/50709706.gif
And I'm having trouble finding good tutorials on it. Most of the examples aren't detailed enough, or do stuff via code, but I'd like to take advantage of the IDE as much as possible.
Whats the difference between ItemsSource and DataContext?
I'd like to bind it to just a List for starters. I don't need SQL or databases or anything fancy. Where would I declare my list? In MainWindow.xaml.cs? How do I get it to appear in that dialog?
Think of "DataContext" as the default value for "Source" in a binding.
When you create a binding, you can specify the path and source, like this (I'll use TextBox as an example):
<TextBox Text="{Binding Path=Foo,Source={StaticResource Bar}}" />
So my TextBox.Text property is bound to a Foo property on an object called Bar (a resource somewhere in the application).
However, if you have a whole bunch of things that you want to bind to properties on Bar, it's easier to set Bar as the DataContext of the parent container. A Binding without a Source will just use the DataContext by default, and DataContext flows through to child controls from the parent. So:
<StackPanel DataContext="{StaticResource Bar}">
<TextBox Text="{Binding Path=Foo}" />
<TextBox Text="{Binding Path=Fizz}" />
<TextBox Text="{Binding Path=Buzz}" />
</StackPanel>
All of the TextBoxes are still binding to properties on Bar, but they're doing it without setting it as a Source explicitly.
So let's have another look at the dialog you posted. It's giving you several options for the "source" of the ItemsSource binding. When you choose "DataContext", you're telling Visual Studio that the ItemsControl doesn't need to know the source - it'll pick it up from the DataContext of the parent container (maybe even the Window itself).
If you chose one of the other options (ElementName, RelativeSource or StaticResource) then you'd be setting the binding's source explicitly for that ItemsControl.
Once you've told it that it's binding to the DataContext, you'll need to drop into the "Path" section of the dialog and tell it which property to bind the items of the control to. In the end, the markup would look something like this (assuming it's a ListBox):
<ListBox ItemsSource="{Binding Path=Foos}" />
So the items in the ListBox are coming from a property called "Foos", and that property is on an object that's set in the DataContext somewhere higher in the logical tree (perhaps on the Window itself).
You rarely need to use the data context of a control outside of the control. The most common use case for setting DataContext(DataContext = this;) is within UserControl's code-behind to make all controls within the UserControl to bind to the control's properties.
When you use a ListBox, setting ItemsSource is sufficient, unless you are doing something funky.
This is a pretty good walkthrough: http://windowsclient.net/learn/video.aspx?v=315275
Specifically, you need to set the DataContext first to tell it where to look for the ItemsSource. The easiest way is to set this on the Window through the XAML:
<Window.DataContext>
<controllers:DownloadManager />
</Window.DataContext>

WPF Update Binding when Bound directly to DataContext w/ Converter

Normally when you want a databound control to 'update,' you use the "PropertyChanged" event to signal to the interface that the data has changed behind the scenes.
For instance, you could have a textblock that is bound to the datacontext with a property "DisplayText"
<TextBlock Text="{Binding Path=DisplayText}"/>
From here, if the DataContext raises the PropertyChanged event with PropertyName "DisplayText," then this textblock's text should update (assuming you didn't change the Mode of the binding).
However, I have a more complicated binding that uses many properties off of the datacontext to determine the final look and feel of the control. To accomplish this, I bind directly to the datacontext and use a converter. In this case I am working with an image source.
<Image Source="{Binding Converter={StaticResource ImageConverter}}"/>
As you can see, I use a {Binding} with no path to bind directly to the datacontext, and I use an ImageConverter to select the image I'm looking for. But now I have no way (that I know of) to tell that binding to update. I tried raising the propertychanged event with "." as the propertyname, which did not work.
Is this possible? Do I have to wrap up the converting logic into a property that the binding can attach to, or is there a way to tell the binding to refresh (without explicitly refreshing the binding)?
Any help would be greatly appreciated.
Thanks!
-Adam
The workaround here was to add a property to my object (to be used as the datacontext) called "Self" , which simply returned
public Object Self { get { return this; }}
Then in the binding I used this property:
<Image Source="{Binding Path=Self, Converter={StaticResource ImageConverter}}"/>
Then when I call
PropertyChanged(this, new PropertyChangedEventArgs("Self"))
it works like a charm.
Thanks all.
I don't believe there is a way of accomplishing exactly what you need with your current converter. As you mentioned, you could do the calculation in your ViewModel, or you could change your converter into an IMulitValueConverter.
From your specific scenario (the converter tied to a ViewModel class, and a few of its properties), I would lean towards implementing the logic in the ViewModel.
Hmm, you don't show the full implementation. But I think it should update, if the value bound to the GUI provides the PropertyChanged-Event.
Regards

Resources