0

I'm new both to WinUI and XAML and I'm creating a WinUI 3 Library that contains various CustomControl.

They're all independent and can be used stand alone. But one of those controls, is made by embedding some other custom controls that are in the library.

XAML of Container control

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyCustomControls">
    
    <Style TargetType="local:CustomContainer">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CustomContainer">
                    <StackPanel>
                        <local:CustomTextBlock x:Name="Text"
                                            DisplayMode="{TemplateBinding DisplayMode}"
                                            Message="{TemplateBinding Message}">
                        </local:CustomTextBlock>

                        <local:CustomIndicator x:Name="Indicator"></local:CustomIndicator>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
</ResourceDictionary>

As you can see in the sample, the CustomTextBlock custom control contains 2 DependecyProperty (DisplayMode and Message) that I need to "replicate" on the CustomContainer control to be able to set them when I want to use the CustomContainer on a page.

Here in the XAML I've used TemplateBinding with 2 DependecyProperty that I should declare on the code behind of CustomContainer.

Code behind of CustomTextBlock control

private static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(
    nameof(DisplayMode), typeof(bool), typeof(CustomTextBlock), new PropertyMetadata(null));

public bool DisplayMode
{
    get => (bool)GetValue(DisplayModeProperty);
    set => SetValue(DisplayModeProperty, value);
}

private static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
    nameof(Message), typeof(string), typeof(CustomTextBlock), new PropertyMetadata(default(string), (d, e) => ((CustomTextBlock)d).MessageChanged(d, e)));

public string Message
{
    get => (string)GetValue(MessageProperty);
    set => SetValue(MessageProperty, value);
}

How can I expose the 2 properties of CustomTextBlock on the CustomContainer control so that those values directly sets the underlying properties? Do they still need to be DependencyProperty type?

It seems something like wrapping or inheritance concept, but I'm not able to figure it out, especially for the Message property that is also registered with an event handler.

2
  • Usually you need to relay them. So create the same DPs in the outer control. If the number of DPs that you want to expose are too much, you might need to consider inheritance. Commented Sep 8, 2022 at 15:24
  • @AndrewKeepCoding Can you please explain in more details both the proposed solution, maybe also with some code? Thanks! Commented Sep 9, 2022 at 6:50

1 Answer 1

1

In this sample, we have:

  • InnerControl with a TextBlock and an InnerText dependency-property bound to the TextBlock
  • OuterControl with an InnerControl and a TextForInnerControl dependency-property bound to the InnerControl
  • MainWindow with a TextBox and an OuterControl where its TextForInnerControl is bound to the TextBox

The text coming form the TextBox in the MainWindow will be passed to the OuterControl and then to the InnerControl.

Here's the code:

InnerControl.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace WinUI3CustomControlTest;

public sealed class InnerControl : Control
{
    public static readonly DependencyProperty InnerTextProperty =
        DependencyProperty.Register(
            nameof(InnerText),
            typeof(string),
            typeof(InnerControl),
            new PropertyMetadata(string.Empty));

    public InnerControl()
    {
        this.DefaultStyleKey = typeof(InnerControl);
    }

    public string InnerText
    {
        get => (string)GetValue(InnerTextProperty);
        set => SetValue(InnerTextProperty, value);
    }
}

OuterControl.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace WinUI3CustomControlTest;

public sealed class OuterControl : Control
{
    public static readonly DependencyProperty TextForInnerControlProperty =
        DependencyProperty.Register(
            nameof(TextForInnerControl),
            typeof(string),
            typeof(OuterControl),
            new PropertyMetadata(string.Empty));

    public OuterControl()
    {
        this.DefaultStyleKey = typeof(OuterControl);
    }

    public string TextForInnerControl
    {
        get => (string)GetValue(TextForInnerControlProperty);
        set => SetValue(TextForInnerControlProperty, value);
    }
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUI3CustomControlTest">

    <Style TargetType="local:OuterControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:OuterControl">
                    <StackPanel Orientation="Vertical">
                        <local:InnerControl InnerText="{TemplateBinding TextForInnerControl}" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="local:InnerControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:InnerControl">
                    <StackPanel>
                        <TextBlock Text="{TemplateBinding InnerText}" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

MainWindow.xaml

<Window
    x:Class="WinUI3CustomControlTest.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:local="using:WinUI3CustomControlTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel>
        <TextBox
            x:Name="InputTextBox"
            PlaceholderText="Enter you text.." />
        <Button
            Click="Button_Click"
            Content="Set text programmatically" />
        <local:OuterControl
            x:Name="MyOuterControl"
            TextForInnerControl="{x:Bind InputTextBox.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </StackPanel>

</Window>

MainWindow.xaml.cs

using Microsoft.UI.Xaml;
using System;

namespace WinUI3CustomControlTest;

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.MyOuterControl.TextForInnerControl = DateTime.Now.ToString();
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Ok, it works! But what if I want to set the TextForInnerControl value from code using a method like SetText("string", otherOption)? Should I create a public method on InnerControl? Or on OuterControl? Or both...?
To access a control from code-behind, you need to name it with x:Name. I added a Button which sets text to TextForInnerControl property.
And what if I want to access an inner control of a CustomControl from code behind? I tried with the {x:Name} but it seems it's not the right way, because the name it's not recognized and so I don't have a reference to the control.
If you need to access a control (InnerControl in this case) hidden inside a control (OuterControl) in this case, I just happen to post a tutorial last week. It might help you.
I came with another approach that might help you. I created another question and answered it myself.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.