Search code examples
c#xamlcustom-controlswinui-3winui

How to access your nested custom control?


This question comes from this related question.

One way to access your nested inner control, is to create dependency-properties in the outer control and pass values to the inner control.

How can I access directly the inner control from outside the outer control?


Solution

  • One way to do this, is to store the inner control in a public property.

    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;
    
    [TemplatePart(Name = nameof(MyInnerControl), Type = typeof(InnerControl))]
    public sealed class OuterControl : Control
    {
        public OuterControl()
        {
            this.DefaultStyleKey = typeof(OuterControl);
        }
    
        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            MyInnerControl = GetTemplateChild(nameof(MyInnerControl)) as InnerControl;
        }
    
        public InnerControl? MyInnerControl { get; private set;}
    }
    

    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 x:Name="MyInnerControl" />
                        </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>
            <Button
                Click="Button_Click"
                Content="Set text programmatically" />
            <local:OuterControl x:Name="MyOuterControl" />
        </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)
        {
            if (this.MyOuterControl.MyInnerControl is InnerControl innerControl)
            {
                innerControl.Text = DateTime.Now.ToString();
            }
        }
    }