WinUI 3 (0.8.1), C++/WinRT (2.0.210714.1), Desktop - Using "BindingOperations::SetBinding(...)

Alfred Handke 96 Reputation points
2021-07-20T14:26:00.187+00:00

Hi,
I'm binding a ListBox in Xaml to a NetworkViewModel instance, which holds a collection of NodeViewModel instances. A NodeViewModel instance shall be displayed on a canvas in X and Y positions, which are provided by the NodeViewModel. I want to bind ListBoxItem's Canvas.Left and Canvas.Top to X and Y. I assume that in WinUI it is not possible to use a binding in a Style. A workaround could be a helper class with attached properties for the source paths of the bindings. It creates the bindings in code behind in a PropertyChangedCallback of the helper property. But the binding doesn't work, why?

Best regards
Alfred

void BindingHelperNodes::OnBindingPathPropertyChanged( winrt::Microsoft::UI::Xaml::DependencyObject const& d,
winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e )
{

    if(Microsoft::UI::Xaml::Controls::ListBoxItem item{ d.try_as<Microsoft::UI::Xaml::Controls::ListBoxItem>() })
    {
        Microsoft::UI::Xaml::Controls::Canvas::LeftProperty;

        Microsoft::UI::Xaml::Data::Binding binding;
        binding.Mode( Microsoft::UI::Xaml::Data::BindingMode::OneWay );
        Microsoft::UI::Xaml::PropertyPath propertyPath( L"Y" );
        binding.Path( propertyPath );
        Microsoft::UI::Xaml::Data::RelativeSource relativeSource;
        relativeSource.Mode( Microsoft::UI::Xaml::Data::RelativeSourceMode::Self );
        binding.RelativeSource( relativeSource );

        Microsoft::UI::Xaml::Data::BindingOperations::SetBinding(
            d,
            Microsoft::UI::Xaml::Controls::Canvas::LeftProperty(),
            binding);
        //item.SetBinding( Microsoft::UI::Xaml::Controls::Canvas::LeftProperty(), binding );
    }
}

<Grid>
    <Grid.Resources>
        <ResourceDictionary>
            <Style x:Name="listBoxItemContainerStyle2" TargetType="ListBoxItem">
                <Setter Property="Canvas.Left" Value="100"/>
            </Style>
        </ResourceDictionary>
    </Grid.Resources>

    <ListBox Grid.Row="1" ItemsSource="{x:Bind mainViewModel.NetworkViewModel.Nodes, Mode=OneWay}"
        >
        <ItemsControl.ItemContainerStyle>
            <Style x:Name="listBoxItemContainerStyle1" TargetType="ListBoxItem">
                <Setter Property="local:BindingHelperNodes.CanvasLeftBindingPath" Value="X"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ListBox.Template>
            <ControlTemplate TargetType="ListBox">
                <ItemsPresenter />
            </ControlTemplate>
        </ListBox.Template>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Background="Transparent" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate x:DataType="local:NodeViewModel">
                <Grid
                    Width="120"
                    Height="60"
                    >
                    <!-- This rectangle is the main visual for the node. -->
                    <Rectangle
                        Stroke="Black"
                        Fill="White"
                        RadiusX="40"
                        RadiusY="40" 
                    />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,762 questions
0 comments No comments
{count} votes

Accepted answer
  1. Alfred Handke 96 Reputation points
    2021-07-20T15:36:55.487+00:00

    Hi,
    I luckily found the problem! The real problem was the call to SetBinding() in line 13, which had no effect. But I actually forgot to set the attribute "[Microsoft.UI.Xaml.Data.Bindable]" in the IDL file!

    namespace NetworkView
    {   
        [Microsoft.UI.Xaml.Data.Bindable]
        runtimeclass NodeViewModel : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
        {
            // Declaring a constructor (or constructors) in the IDL causes the runtime class to be
            // activatable from outside the compilation unit.
            NodeViewModel();
    
            String Name;
            Double X;
            Double Y;
        }
    }
    
    
    void BindingHelperNodes::OnBindingPathPropertyChanged( winrt::Microsoft::UI::Xaml::DependencyObject const& d, winrt::Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e )
    {
        if(Microsoft::UI::Xaml::Controls::ListBoxItem item{ d.try_as<Microsoft::UI::Xaml::Controls::ListBoxItem>() })
        {
            Microsoft::UI::Xaml::Data::Binding binding;
            binding.Mode( Microsoft::UI::Xaml::Data::BindingMode::OneWay );
            Microsoft::UI::Xaml::PropertyPath propertyPath( L"X" );
            binding.Path( propertyPath );
    
            // Alternative 1 works
            //Microsoft::UI::Xaml::Data::BindingOperations::SetBinding(
            //    d,
            //    Microsoft::UI::Xaml::Controls::Canvas::LeftProperty(),
            //    binding);
            // Alternative 2 works, too
            item.SetBinding( Microsoft::UI::Xaml::Controls::Canvas::LeftProperty(), binding );
        }
    }
    

    Best regards
    Alfred

    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.