yuyoyuppe
Topic Author
Posts: 7
Joined: 06 Apr 2021, 12:46

Binding to a UserControl's property which is bound to VM

23 Jul 2021, 17:43

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:
<Grid DataContext="{Binding TheVM}">
  <!-- THIS WORKS -->
  <!-- <TextBox Text="{Binding Scale}" /> -->

  <!-- THIS DOESN"T WORK -->
  <d:LabeledInputBox Text="{Binding Scale}" />
</Grid>
LabeledInputBox UserControl is just a TextBox with some properties and visual stuff attached:
<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>
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{}));
}
TheVM ViewModel class doesn't have anything fancy as well:
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{}));
}
If it's relevant, TheVM is stored in a parent VM which is inherited from NotifyPropertyChangedBase and declared like this:
    NsProp("TheVM", &ParentVM::GetTheVM);
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?
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Binding to a UserControl's property which is bound to VM

23 Jul 2021, 18:44

TextBox's Text property is defined to bind TwoWay by default:
    data->RegisterProperty<String>(TextProperty, "Text",
        FrameworkPropertyMetadata::Create(String(),
            FrameworkPropertyMetadataOptions_BindsTwoWayByDefault,
            UpdateSourceTrigger_LostFocus));
You can define your UserControl's Text property the same way, or explicitly set the mode in the binding:
<Grid DataContext="{Binding TheVM}">
  <d:LabeledInputBox Text="{Binding Scale, Mode=TwoWay}" />
</Grid>
If you don't do that updates only go from the VM's property to the UserControl's property.
 
yuyoyuppe
Topic Author
Posts: 7
Joined: 06 Apr 2021, 12:46

Re: Binding to a UserControl's property which is bound to VM

24 Jul 2021, 00:21

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?
 
User avatar
jsantos
Site Admin
Posts: 3905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Binding to a UserControl's property which is bound to VM

26 Jul 2021, 17:04

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".
    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;
            }
        }
Please, let me know if this gives you enough information, it should be similar to WPF high tracing level.
 
yuyoyuppe
Topic Author
Posts: 7
Joined: 06 Apr 2021, 12:46

Re: Binding to a UserControl's property which is bound to VM

04 Aug 2021, 11:01

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!
 
yuyoyuppe
Topic Author
Posts: 7
Joined: 06 Apr 2021, 12:46

Re: Binding to a UserControl's property which is bound to VM

19 Aug 2021, 00:02

Ok, I've just stumbled upon a case when the binding silently fails:
    <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>
...
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.
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Binding to a UserControl's property which is bound to VM

19 Aug 2021, 14:06

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:
[NOESIS/E] BindingTest.xaml(15): Duplicate assignment of property 'UIElement.RenderTransform' in 'Control'.
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.

If I change the xaml to this:
<Control.RenderTransform>
  <TranslateTransform X="{Binding Something}" Y="{Binding SomeProp, ElementName=DataContextName}"/>
</Control.RenderTransform>
Then I get the following binding error message:
[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
 
yuyoyuppe
Topic Author
Posts: 7
Joined: 06 Apr 2021, 12:46

Re: Binding to a UserControl's property which is bound to VM

21 Aug 2021, 01:45

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?
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Binding to a UserControl's property which is bound to VM

26 Aug 2021, 13:01

It'd be still nice to have some warning/trace that the property would be overwritten though?
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.

Who is online

Users browsing this forum: Google [Bot], vinick and 20 guests