Any of you have any experience and resources for addressing a situation similar to this:
Define a DoubleTextBox:
- User control that wraps a normal TextBox
- Exposes a double Value Dependency property that the user can bind to a source object
- Real target of the Value binding is the Text property of the encapsulated TextBox, but need some custom logic in the loop when values are set to convert to special text representations for some values
- Source object can validate Values set with IDataErrorInfo or exceptions, need to pass those through and indicate invalid inputs on the TextBox
- Need to convert the input string to double and set the Value property’s source object property
I can easily handle such a control where a user binding for the exposed custom propery is not involved, but not sure how to push a value through a binding programmatically to the source object for the binding. And of course the real requirement I am trying to address is not a single encapsulated control, it is a little grouping of several controls that will be shown in several screens and I don’t want to have to repeat the MVVM hook up I know very well how to do to address it in a single screen.
Thanks
Brian
-----------------------------------------
Brian Noyes
Chief Architect, IDesign Inc
Microsoft Regional Director / MVP
http://www.idesign.net
+1 703-447-3712
-----------------------------------------
Because if the user of my control has set the exposed Value property with a {Binding} to some other object’s property, setting the dependency property in my control’s code behind replaces their binding with the new value. It also doesn’t address how to reflect validation errors.
I still cannot quite visualise the issue here. When the consumer of
the control binds to the Value of your UserControl, the DoubleTextBox
is the target of the binding. However, if you use binding to wire-up
the UI of your DoubleTextBox internally, for example binding the
DoubleTextBox.Value property to a TextBlox, in this case
DoubleTextBox.Value is the source and your TextBox within your
control's UI is the target
The two should not cancel each other out, they are different targets.
The only thing you have to do is ensure that you do not change the
DataContext of DoubleTextBox in order to wire-up your UI. I typically
set the 'LayoutRoot' of my UserControl to this, allowing the
DataContext of the UserControl to inherit properly.
Perhaps I have misunderstood (as well!), but this sounds relatively
simple to me!
Colin E.
On Fri, Aug 19, 2011 at 1:30 PM, Brian Noyes
--
Regards,
Colin E.
<Window x:Class="EncapsulatedBindableControls.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:EncapsulatedBindableControls" Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:EncapsulatedBindableControls"> <Window.Resources> <local:SomeDataObject x:Key="SomeObj" InterestRate="23.1" /> </Window.Resources> <Grid> <my:DoubleTextBox HorizontalAlignment="Left" Width="100" Value="{Binding Source={StaticResource SomeObj}, Path=InterestRate, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,0,0" x:Name="doubleTextBox1" VerticalAlignment="Top" /> <TextBox Margin="10,50,0,0" /> </Grid> </Window>
The code behind tries to set the ValueProperty dependency property through its wrapper property when the Text of the encapsulated TextBox changes (so that it can intercept and handle special values). But setting Value kills the binding:
private void TrySetValue()
{
if (string.IsNullOrWhiteSpace(myTextBox.Text)) return;
double convertedValue = double.Parse(myTextBox.Text);
var binding = GetBindingExpression(ValueProperty);
Debug.Assert(binding != null);
Value = convertedValue;
binding = GetBindingExpression(ValueProperty);
Debug.Assert(binding == null);
}
Again I am was simplifying the scenario the try to find an answer to the one part I can’t solve. The real scenario is more complicated with a cluster of several controls with some semi-complex value transformations between what the user inputs and what the value produced is, and this is needed on several screens. It is not a single control single property scenario I am trying to address.
Thanks anyway, guess it just doesn’t translate by email.
I tested this out, and it works the way I would have expected, see the
attached project.
In my example I have a user control with a TextBox that is bound to
the property that the user control exposes to the control's clients.
There is also a button that sets the value directly. Clicking this
button sets the exposed value, but binding still works afterwards.
Again, hope I understood correctly!
Regards,
Colin E.
Wow. OK, this is a subtle one that I can get working now, but I can't fully explain why.
I finally isolated it to this: If the binding on Value in the containing form is TwoWay, life is good. If it is not, setting Value programmatically inside the control kills the binding.
In other words, this works:
<local:DoubleTextBox Value="{Binding Path=MyValue, Mode=TwoWay}"/>
This does not:
<local:DoubleTextBox Value="{Binding Path=MyValue}"/>
I modified your sample slightly to bind the contained TextBox to a string property in the control itself and did the setting of the Value property and binding checking from there:
<TextBox Text="{Binding StringSource}"/>
private string _StringSource; public string StringSource { get { return _StringSource; } set { _StringSource = value; var binding = GetBindingExpression(ValueProperty); Debug.Assert(binding != null); Value = 77.0; binding = GetBindingExpression(ValueProperty); Debug.Assert(binding == null); } }And sure enough, the binding was intact after setting Value if the Mode=TwoWay in the users binding, but it dies on setting Value if Mode=OneWay (the default).