Binding will work with locally set value only after initialization of target property.
The dependency property gets set to null and I lose the value I
initalized in constructor.Why is that happening?
Since InitializeComponent()
is missing in your UserControl
ctor, and you may set Txt
either before or after it, let's consider both cases with the presumption that Txt
is initialized inside InitializeComponent()
. Here initialization of Txt
means it gets assingned the value declared in XAML. If Txt
is set locally beforehand, the binding from XAML would replace this value. And if it is set afterwards, the binding would take into account this value whenever it gets its chance to be evaluated, which is the case for both TwoWay and OneWayToSource bindings. (Trigger of binding evaluation will be explained later.)
To prove my theory, I did a test with three elements each with a different mode of binding.
<TextBox Text="{Binding TwoWayStr,Mode=TwoWay}"></TextBox>
<local:UserControl1 Txt="{Binding OneWayToSourceStr, Mode=OneWay}" />
<Button Content="{Binding OneWayStr,Mode=OneWay}"></Button>
However the result shows that local value is ignored in both cases. Because unlike other elements, by the time InitializeComponent()
exits and Initialized
event fires, properties of UserControl
haven't been initialized yet, including Txt
.
- Initialization
- (Begin) Enters
InitializeComponent()
in Window
ctor
Text
initialized and TwoWay binding attempt to attach binding source
TextBox
Initialized
UserControl
Initialized
Txt
initialized and OneWayToSource binding attempt to attach binding source
Content
initialized and OneWay binding attempt to attach binding source
Button
Initialized
Window
Initialized
- (End) Exits
InitializeComponent()
in Window
ctor
- Loading/Rendering
- (Begin) Exits
Window
ctor
- TwoWay binding attempt to attach binding source if unattached
- OneWayToSource binding attempt to attach binding source if unattached
- OneWay binding attempt to attach binding source if unattached
Window
Loaded
TextBox
Loaded
UserControl
Loaded
Button
Loaded
- (End) All elements Loaded
- Post-loading
- (Begin) All elements Loaded
- TwoWay binding attempt to attach binding source if unattached
- OneWayToSource binding attempt to attach binding source if unattached
- OneWay binding initial attempt to attach binding source if unattached
- (End)
Window
displayed
This special behavior of UserControl
that properties are initialized afterwards is discussed in this question. If you use the method provided there, the calling of OnInitialized
override along with the firing of Initialized
event will be delayed until after all properties are initialized. And if you call BindingOperations.GetBindingExpression(this, MyCustomControl.TxtProperty)
in OnInitialized
override or in the handler of Initialized
, the return value will no longer be null.
At this point, it will be safe to assign the local value. But the binding evaluation will not be triggered immediately to transfer the value, because the binding source(DataContext) is still unavailable, notice the DataContext is not set until after Window
initialization. In fact if you check Status
property of the returned binding expression, the value is Unattached
.
After entering Loading stage, the second attempt to attach binding source will seize the DataContext, then an evaluation will be triggered by this first attachment of binding source where value of Txt
(in this case "123") will be transfered to source property Str
via setter. And the status of this biniding expression now changes to Active
which represents a resolved state of binding source.
If you weren't to use the method mentioned in that question, you can move the local value assignment after InitializeComponent()
of Window, into Intialized
handler of Window
or Loaded
handler of Window
/UserControl
, the result will be the same. Except if it were set in Loaded
, local assignment will trigger an immediate evaluation, since binding source is already attached. And the one triggered by first attachment will be instead transferring default value of Txt
.
OneWayToSource binding evaluation triggered by change of binding source will pass default value to source property.
What if I changed DataContext at runtime? It would again destroy the
value of the dependecy property in control.
From the previous segment, we've already seen two types of triggers for binding evaluation of OneWayToSource
binding, one is target property change(if UpdateSourceTrigger
of binding is PropertyChanged
, which is often default), and the other is first attachment of binding source.
It seems that from the discussion in the accepted answer that you have a second question about why default value is used instead of the "current" value of Txt
in the evaluation triggered by binding source changes. It turns out this is the designed behavior of this third type of evaluaiton trigger, which is also confirmed by the 2nd and 3rd answer to this question. By the way, I'm testing this in .Net 4.5, there is a change of evaluation process of OneWayToSource
from 4.0 by removing the getter call after setter, but this does not change the "default value" behavior.
As a side note, for both TwoWay and OneWay binding, the evaluation triggered by first attachment and change of binding source behaves exactly the same by calling getter.
Extra: OneWayToSource binding will ignore changes on all levels of path
Another strange behavior of OneWayToSource
binding that is perhaps pertaining to the topic is that although it is expected that changes of target property is not listened to, if the binding path contains more than one level, which means the target property is nested, changes to all levels up from the target property is also ignored. For example, if the binding is declared like this Text={Binding ChildViewModel.Str, Mode=OneWayToSource}
, a change to the ChildViewModel
property will not trigger a binding evaluation, in fact, if you trigger an evaluation by changing Text
, Str
setter is called on the previous ChildViewModel
instance. This behavior makes OneWayToSource
deviate more from the other two modes.
P.S.: I know this is an old post. But since these behaviors are still not well documented, I thought it might be helpful to anyone also trying to understand what happens.