0

I have a Page called HomePage in a WinUI 3 Desktop App. HomePage has two DependencyProperties, P1 and P2. I would like to create a binding that uses P1 as the source and P2 as the target (assume P1 and P2 are of the same type). This can be done easily in code but, can I create the binding in XAML?

This is the code for the binding:

public sealed partial class HomePage : Page
{
    public HomePage()
    {
        InitializeComponent();

        // Dependency property identifiers are static public fields. This is the identifier of the Target.
        var targetInfo = typeof(HomePage).GetField("P2Property", BindingFlags.Static | BindingFlags.Public);

        if (targetInfo is not null && targetInfo.FieldType == typeof(DependencyProperty))
        {
            Binding bBinding = new() { Path = new PropertyPath("P1"), Source=this, Mode = BindingMode.OneWay };

            // this is a static field (there are no instance versions) and the value is the identifier of the DependencyProperty
            var dp = (DependencyProperty)targetInfo.GetValue(null);

            ClearValue(dp);
            SetBinding(dp, bBinding);       // create the binding
        }
    }

    // P1
    public static readonly DependencyProperty P1Property = DependencyProperty.Register(
        "P1", typeof(object), typeof(HomePage), new PropertyMetadata(null));
    public object P1
    {
        get => (object)GetValue(P1Property);
        set => SetValue(P1Property, value);
    }

    // P2
    public static readonly DependencyProperty P2Property = DependencyProperty.Register(
        "P2", typeof(object), typeof(HomePage), new PropertyMetadata(null));
    public object P2
    {
        get => (object)GetValue(P2Property);
        set => SetValue(P2Property, value);
    }
}

Can the binding (bBinding) be created in XAML instead?

[Edit] Thank you for the answers, which I have tried, but issues remain. Here is the XAML for HomePage:

<Page
    x:Class="PFSI.Views.HomePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:views="using:PFSI.Views"
    x:Name="homePage">

    <Grid>
        <!-- Blah -->
    </Grid>
</Page>

Defining the binding isn't the issue - any of: P2="{Binding P1, ElementName=homePage}"; P2="{Binding RelativeSource={RelativeSource Self}, Path=P1}"; or P2="{x:Bind P1, Mode=OneWay}" should work provided they can be applied to an element. DataContext is set to the HomeViewModel but that shouldn't matter here.

I tried using the suggested <views:HomePage ... /> but the result is a stack overflow as the the compiler loops from the element to the class and back.

I thought a Style might work (something like:

<Setter Property="P2" Value="{Binding RelativeSource={RelativeSource Self}, Path=P1}"/>

) but Setters in WinUI 3 Styles don't support bindings (although, curiously, bindings work in VisualStateManager Setters).

I'm sure this should work in XAML - maybe it just needs to wait for Setter Binding support?

aturnbul
  • 347
  • 2
  • 12

2 Answers2

0

You can do it this way.

MainWindow.xaml

<Window
    x:Class="WinUI3AppPageTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="using:WinUI3AppPageTest"
    mc:Ignorable="d">

    <Grid>
        <local:HomePage P2="{Binding RelativeSource={RelativeSource Mode=Self}, Path=P1}"/>
    </Grid>

</Window>

HomePage.xaml

<Page
    x:Class="WinUI3AppPageTest.HomePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:WinUI3AppPageTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <StackPanel>
        <TextBox x:Name="TextControl" Text="{x:Bind P1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{x:Bind P2, Mode=OneWay}" />
    </StackPanel>
</Page>

HomePage.xaml.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;

namespace WinUI3AppPageTest;

public sealed partial class HomePage : Page
{
    public static readonly DependencyProperty P1Property =
        DependencyProperty.Register(
            nameof(P1),
            typeof(string),
            typeof(HomePage),
            new PropertyMetadata(default));

    public static readonly DependencyProperty P2Property =
        DependencyProperty.Register(
            nameof(P2),
            typeof(string),
            typeof(HomePage),
            new PropertyMetadata(default));

    public HomePage()
    {
        this.InitializeComponent();
    }

    public string P1
    {
        get => (string)GetValue(P1Property);
        set => SetValue(P1Property, value);
    }

    public string P2
    {
        get => (string)GetValue(P2Property);
        set => SetValue(P2Property, value);
    }
}
Andrew KeepCoding
  • 7,040
  • 2
  • 14
  • 21
  • Please see edit, above. This results in stack overflow. – aturnbul Sep 08 '22 at 13:35
  • This works for me. I posted the all the related code. – Andrew KeepCoding Sep 08 '22 at 14:13
  • Yes, I see, thanks. This code does bind P2 to P1 but it does it in the parent of the HomePage. The code version in the original post creates the binding whenever a HomePage is instantiated. I sort of envisioned HomePage.xaml as doing something equivalent (sort of the XAML constructor), not the parent. That's what I was originally asking but I may be able to get the result I want (which was for testing) using your approach when the page is loaded. Dang, Binding twists my mind :P – aturnbul Sep 08 '22 at 14:29
0

This kind of binding is so-called self binding, which is used for binding to/between tag's own properties.

1 - Self-binding

Use RelativeSourceMode.Self to access tag's properties by specifying them in the Pathproperty

<local:HomePage P2="{Binding RelativeSource={RelativeSource Self}, Path=P1}"/>

Almost similar to Andrew's answer (explicit mode specification):

<local:HomePage P2="{Binding RelativeSource={RelativeSource Mode=Self}, Path=P1}"/>

2 - ElementName

You can also try binding through ElementName if your layout contains multiple number of elements of the same type (or self):

<local:HomePage x:Name="MyHomePage" P2="{Binding ElementName=MyHomePage, Path=P1}"/>

More

Explore more about RelativeSource and Binding markup extension to dive deep into it, coz it is really handy thing to use in your further projects. Btw have a look at @james.lee detailed answer on this topic.

ikhtiyarnovruzov
  • 131
  • 3
  • 10