After some more research (and after posting an irrelevant answer and then deleting it again) -- and being motivated by some discussion with Eren Ersönmez and Dev Hedgehog -- the reason of why the two-way binding IsChecked="{Binding Path=., Mode=TwoWay}"
cannot work became more clear.
(Side note: This answer does not contain a solution to the problem. Pushpraj's answer already provides a nice solution.)
{Binding Path=., Source=SomeSource}
denotes a binding which binds to the binding source directly. An equivalent binding syntax is {Binding Source=SomeSource}
.
However, bindings to the binding source itself cannot be two-way. Trying so by specifying {Binding Source=SomeSource, Mode=TwoWay}
will cause an InvalidOperationException during run-time.
On the other hand, {Binding Path=., Source=SomeSource, Mode=TwoWay}
will not produce an exception - but it still does not enable a two-way binding for such a data binding. Rather, it only fools the binding validation/error handling mechanism of the binding engine.
This has been already found out and discussed in answers to other binding-related questions, such as the answers by Allon Guralnek or by H.B. (and were it not for their answers and my discussion with Eren Ersönmez, i would probably still be clueless of what is going on here...).
This also means that the issue is not caused by a boxed value type being a binding source per se, but rather due to trying to use a two-way binding with the binding path .
.
Why does it make no sense to have two-way bindings with binding path .
?
Below is a small example demonstrating the issue. It also demonstrates that the problem is not restricted to situations where a boxed value type is being the binding source, nor that it is restricted to bindings involving DataContext.
public class Demo
{
public static string SourceString
{
get { return _ss; }
set { _ss = value; }
}
private static string _ss = "Hello World!";
}
The string (a reference type) provided by static property Demo.SourceString will be used as the binding source in the following XAML snippet:
<TextBox Text="{Binding Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
(Note that the object provided by the My:Demo.SourceString property is the binding source; the property itself is not the binding source.)
This XAML above will produce an InvalidOperationException at run-time.
However, using the equivalent XAML:
<TextBox Text="{Binding Path=., Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
will not produce an exception during run-time, but the binding is nevertheless not a working two-way binding. Text entered into the text box will not be promoted back to the Demo.SourceString property. It can't -- all the binding knows is that it has a string object as source and a binding path which is .
.
...but it would just need to update Demo.SourceString, that can't be too difficult, or?
Assume for a moment that a two-way binding with a path .
as shown in the XAML above would try to work as a two-way binding - would it result in meaningful behavior? What would happen when the TextBox.Text property has changed its value due to the user inputting text?
The binding would theoretically try to promote the new string value back to the binding source by applying the binding path .
onto the object that serves as the binding source (the string "Hello World!"). That does not make much sense... Well, it could perhaps replace the original binding source (the "Hello World!" string) with the string from the TextBox.Text property - but this would be rather meaningless as this change would be local and internal to the binding only. The binding would not be able to assign the new string to Demo.SourceString -- unless someone knows a way of how to obtain a reference to Demo.SourceString by applying the binding path .
to a string object containing "Hello World!". Hence, two-way binding mode is not a feasible option for bindings which bind to the binding source itself.
(If somebody is confused about how the example above would apply to a binding using the DataContext like {Binding Path=., Mode=TwoWay}
, just ignore the class Demo and substitute any occurences of Demo.SourceString in the text with DataContext.)