C# winui 3 将事件从组件控件路由到父自定义控件

C# winui 3 route event from component control to parent custom control

提问人:Woz9 提问时间:10/26/2023 更新时间:10/26/2023 访问量:46

问:

使用 C# winui 3 (.NET 6),我编写了一个自定义控件,该控件由 包含 an 和 .这个想法是,我可以为应用程序中的所有按钮保持一致的外观,并简化我的 xaml,因此我只需要添加 、 和 的值。CommonButtonControlButtonImageTextBlockCommonButtonControlImageSourceTextCommand

到目前为止一切正常,但我还想添加一个事件,当组件事件被触发时,该事件将被触发。这将增加与标准控件的一致性,并允许我从中捕获事件,以便在执行任何其他操作之前执行诸如显示确认对话框之类的操作。ClickCommonButtonControlButton ClickButtonClickCommonButtonControl

我已将一个事件添加到我的班级中。但是,我在 a(根据本文档)中定义了 xaml,当我尝试捕获组件的事件时,它只会调用后面代码中的代码,而不是类中的代码。似乎没有任何方法可以调用该对象来告诉它组件事件已触发,以便我可以引发该事件。有什么建议吗?将 a 添加为 .ClickCommonButtonControlCommonButtonControlDataDictionaryClickButtonDataDictionaryCommonButtonControlCommonButtonControlButtonCommonButtonControl ClickRoutedEventHandlerDependencyProperty

资源字典

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

    <Style TargetType="local:CommonButtonControl" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CommonButtonControl">
                    <Button
                        Command="{x:Bind Command, Mode=OneWay}"
                        Click="Button_Click">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="32"/>
                                <ColumnDefinition Width="auto"/>
                            </Grid.ColumnDefinitions>

                            <Image
                                Grid.Column="0"
                                Width="32"
                                Height="32"
                                Source="{x:Bind ImageSource, Mode=OneWay}"
                                />

                            <TextBlock
                                Margin="8,0,0,0"
                                Grid.Column="1"
                                VerticalAlignment="Center"
                                Text="{x:Bind Text, Mode=OneWay}"
                                Style="{ThemeResource BodyTextBlockStyle}"
                            />
                        </Grid>
                    </Button>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


</ResourceDictionary>

资源字典背后的代码

partial class DataTemplatesDictionary
    {
        public DataTemplatesDictionary()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            //how to route this to the OnButtonClick method in CommonButtonControl?
        }


    }

CommonButtonControl 类

public sealed class CommonButtonControl : Control
    {
        #region members

        private DependencyProperty TextProperty = DependencyProperty.Register(
            nameof(Text),
            typeof(string),
            typeof(CommonButtonControl),
            new PropertyMetadata(default(string)));

        private DependencyProperty ImageSourceProperty = DependencyProperty.Register(
            nameof(ImageSource),
            typeof(ImageSource),
            typeof(CommonButtonControl),
            new PropertyMetadata(default(ImageSource)));

        private DependencyProperty CommandProperty = DependencyProperty.Register(
            nameof(Command),
            typeof(ICommand),
            typeof(CommonButtonControl),
            new PropertyMetadata(null));

        public event RoutedEventHandler Click;

        #endregion

        #region properties

        private void OnButtonClick(object sender, RoutedEventArgs e)
        {
            //how do I call this method when the component button click event is raised?
            Click?.Invoke(this, new());
        }


        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }

        public ImageSource ImageSource
        {
            get => (ImageSource)GetValue(ImageSourceProperty);
            set => SetValue(ImageSourceProperty, value);
        }

        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

        #endregion



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

用法 1 - 通过命令 (works)

           <local:CommonButtonControl 
                Command="{x:Bind _app.DeleteItemCommand}"
                Text="Delete Item"
                ImageSource="{x:Bind _app.DeleteIcon}"
                />

用法 2 - 通过点击事件(如何让它工作?

            <local:CommonButtonControl 
                Click="CommonButtonControl_Click"
                Text="Delete Item"
                ImageSource="{x:Bind _app.DeleteIcon}"
                />
C# 自定义控件 WinUI-3

评论


答:

2赞 Andrew KeepCoding 10/26/2023 #1

如果你命名你的 ,让我们说“ButtonControl”,Button

<Style TargetType="local:CustomButton">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:CustomButton">
                <Button x:Name="ButtonControl" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

然后,您可以在自定义控件类中获取它并订阅 ButtonControl 的事件:Click

自定义按钮 .cs

public sealed class CustomButton : Control
{
    public CustomButton()
    {
        this.DefaultStyleKey = typeof(CustomButton);
    }

    public event RoutedEventHandler? Click;

    private Button? ButtonControl { get; set; }

    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        if (ButtonControl is not null)
        {
            ButtonControl.Click -= ButtonControl_Click;
        }

        if (GetTemplateChild(nameof(ButtonControl)) is Button button)
        {
            ButtonControl = button;
            ButtonControl.Click += ButtonControl_Click;
        }
    }

    private void ButtonControl_Click(object sender, RoutedEventArgs e)
    {
        Click?.Invoke(this, e);
    }
}

评论

0赞 Woz9 10/29/2023
太好了,谢谢!我曾尝试在 CommonButtonControl 中将 OnButtonClick 设为公共,以便我可以从模板 xaml(有效)绑定到它,但我更喜欢您的解决方案,因为它使方法保持私有。