Binding to a UserControl's property which is bound to VM
Hi folks, I'm investigating bindings mechanics rn and don't understand why it doesn't play nicely with UserControls. Here's my simplified code:
My View:
LabeledInputBox UserControl is just a TextBox with some properties and visual stuff attached:
TheVM ViewModel class doesn't have anything fancy as well:
If it's relevant, TheVM is stored in a parent VM which is inherited from NotifyPropertyChangedBase and declared like this:
Now, I'm continuously polling the Scale property value on TheVM object from code while typing into TextBox/d:LabeledInputBox. The property gets updated properly when TextBox is used, but not with d:LabeledInputBox, although it gets the default Scale value if I set one. What am I doing wrong?
My View:
Code: Select all
<Grid DataContext="{Binding TheVM}">
<!-- THIS WORKS -->
<!-- <TextBox Text="{Binding Scale}" /> -->
<!-- THIS DOESN"T WORK -->
<d:LabeledInputBox Text="{Binding Scale}" />
</Grid>
Code: Select all
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="clr-namespace:D"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="D.LabeledInputBox">
<Grid>
<!-- ...other stuff -->
<TextBox Text="{Binding Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type d:LabeledInputBox}}}}" />
</Grid>
</UserControl>
Code: Select all
NS_IMPLEMENT_REFLECTION(LabeledInputBox, "D.LabeledInputBox")
{
Noesis::DependencyData* data = NsMeta<Noesis::DependencyData>(Noesis::TypeOf<SelfClass>());
data->RegisterProperty<Noesis::String>(TextProperty,
"Text",
Noesis::PropertyMetadata::Create(Noesis::String{}));
}
Code: Select all
NS_IMPLEMENT_REFLECTION(TheVM, "D.TheVM") // inherited from Noesis::DependencyObject
{
Noesis::DependencyData* data = NsMeta<Noesis::DependencyData>(Noesis::TypeOf<SelfClass>());
data->RegisterProperty<Noesis::String>(
ScaleProperty, "Scale", Noesis::PropertyMetadata::Create(Noesis::String{}));
}
Code: Select all
NsProp("TheVM", &ParentVM::GetTheVM);
-
sfernandez
Site Admin
- Posts: 3154
- Joined:
Re: Binding to a UserControl's property which is bound to VM
TextBox's Text property is defined to bind TwoWay by default:
You can define your UserControl's Text property the same way, or explicitly set the mode in the binding:
If you don't do that updates only go from the VM's property to the UserControl's property.
Code: Select all
data->RegisterProperty<String>(TextProperty, "Text",
FrameworkPropertyMetadata::Create(String(),
FrameworkPropertyMetadataOptions_BindsTwoWayByDefault,
UpdateSourceTrigger_LostFocus));
Code: Select all
<Grid DataContext="{Binding TheVM}">
<d:LabeledInputBox Text="{Binding Scale, Mode=TwoWay}" />
</Grid>
Re: Binding to a UserControl's property which is bound to VM
Thanks, that did the trick.
I've also spent an unnecessarily big amount of time fiddling with setting DataContext properties to get all my bindings work with deeply nested VMs etc. Usually a binding would just fail silently without any logging, perhaps that's due to some of them being DataTemplates (so different tree or something?). Is there something similar to PresentationTraceSources.TraceLevel=High in Noesis?
I've also spent an unnecessarily big amount of time fiddling with setting DataContext properties to get all my bindings work with deeply nested VMs etc. Usually a binding would just fail silently without any logging, perhaps that's due to some of them being DataTemplates (so different tree or something?). Is there something similar to PresentationTraceSources.TraceLevel=High in Noesis?
Re: Binding to a UserControl's property which is bound to VM
There is extra binding verbosity in the "Binding" channel available in the logging callback. By default, our application framework only enables that information when using "--log_binding".
Please, let me know if this gives you enough information, it should be similar to WPF high tracing level.
Code: Select all
SetLogHandler([](const char*, uint32_t, uint32_t level, const char* channel, const char* msg)
{
// By default only global channel is dumped
bool filter = !StrIsNullOrEmpty(channel);
// Enable "Binding" channel by command line
if (gCommandLine.HasOption("log_binding"))
{
if (StrEquals(channel, "Binding"))
{
filter = false;
}
}
Re: Binding to a UserControl's property which is bound to VM
Sorry for the late reply. We do not use the application framework and our callback is set to log everything. So I guess I've already seen some output from that channel, but there were definitely cases with silent failures. At the moment, all of those issues are fixed, and I couldn't reproduce any trying things from the top of my head. I'll update this thread if something comes up. Thank you for your support!
Re: Binding to a UserControl's property which is bound to VM
Ok, I've just stumbled upon a case when the binding silently fails:
As you can see, since Something Control is not in the visual tree, you should e.g. specify its DataContext via ElementName, so Y property will be bound properly, while X will silently fail.
Code: Select all
<UserControl.Resources>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger ...>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Control x:Name="Something">
<Control.RenderTransform>
<!-- silently fails to bind -->
<TranslateTransform X="{Binding SomeProp}"/>
<!-- works ok! -->
<TranslateTransform Y="{Binding SomeProp, ElementName=DataContextName}"/>
</Control.RenderTransform>
</Control>
...
-
sfernandez
Site Admin
- Posts: 3154
- Joined:
Re: Binding to a UserControl's property which is bound to VM
The problem in your xaml is not with the Binding, is that you are assigning 2 TranslateTransforms to the RenderTransform property.
When that xaml gets loaded it shows the following parsing error:
If I change the xaml to this:
Then I get the following binding error message:
When that xaml gets loaded it shows the following parsing error:
And the first TranslateTransform is simply ignored, only the second one is set. That is why you don't get any message from the first binding when the template gets applied.[NOESIS/E] BindingTest.xaml(15): Duplicate assignment of property 'UIElement.RenderTransform' in 'Control'.
If I change the xaml to this:
Code: Select all
<Control.RenderTransform>
<TranslateTransform X="{Binding Something}" Y="{Binding SomeProp, ElementName=DataContextName}"/>
</Control.RenderTransform>
[NOESIS/W] Type 'Boxed<String>' does not contain a property named 'Something'
[NOESIS/E] Binding failed: Path=Something, Source=Boxed<String>(''), Target=TranslateTransform(''), TargetProperty=TranslateTransform.x
Re: Binding to a UserControl's property which is bound to VM
Oh, right, that makes sense, I guess it was too late that day :) It'd be still nice to have some warning/trace that the property would be overwritten though?
-
sfernandez
Site Admin
- Posts: 3154
- Joined:
Re: Binding to a UserControl's property which is bound to VM
That is what the first message tries to say when the xaml gets parsed, perhaps we can improve it by saying that previous assigned value will be ignored.It'd be still nice to have some warning/trace that the property would be overwritten though?
Who is online
Users browsing this forum: Bing [Bot] and 4 guests