[UWP]了解模板化控件(7):支持Command

简介: 原文:[UWP]了解模板化控件(7):支持Command以我的经验来说,要让TemplatedControl支持Command的需求不会很多,大部分情况用附加属性解决这个需求会更便利些,譬如UWPCommunityToolkit的HyperlinkExtensions。
原文: [UWP]了解模板化控件(7):支持Command

以我的经验来说,要让TemplatedControl支持Command的需求不会很多,大部分情况用附加属性解决这个需求会更便利些,譬如UWPCommunityToolkit的HyperlinkExtensions

如果正在从头设计自定义控件并真的需要提供命令支持,可以参考这篇文章。支持Command的步骤比较简单,所以这篇文章比较简短。

要实现Command支持,控件中要执行如下步骤:

  • 定义Command和CommandParameter属性。
  • 监视Command的CanExecuteChanged事件。
  • 在CanExecuteChanged的事件处理函数及CommandParameter的PropertyChangedCallback中,根据Command.CanExecute(CommandParameter)的结果设置控件的IsEnabled属性。
  • 在某个事件(Click或者ValueChanged)中执行Command。

MenuItem是实现了Command支持的示例,重载了OnPointerPressed并且在其中执行Command:

public class MenuItem : Control
{
    /// <summary>
    /// 标识 Command 依赖属性。
    /// </summary>
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(MenuItem), new PropertyMetadata(null, OnCommandChanged));

    private static void OnCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        MenuItem target = obj as MenuItem;
        ICommand oldValue = (ICommand)args.OldValue;
        ICommand newValue = (ICommand)args.NewValue;
        if (oldValue != newValue)
            target.OnCommandChanged(oldValue, newValue);
    }

    /// <summary>
    /// 标识 CommandParameter 依赖属性。
    /// </summary>
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(MenuItem), new PropertyMetadata(null, OnCommandParameterChanged));

    private static void OnCommandParameterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        MenuItem target = obj as MenuItem;
        object oldValue = (object)args.OldValue;
        object newValue = (object)args.NewValue;
        if (oldValue != newValue)
            target.OnCommandParameterChanged(oldValue, newValue);
    }

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


    public event RoutedEventHandler Click;

    /// <summary>
    /// 获取或设置Command的值
    /// </summary>  
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    /// <summary>
    /// 获取或设置CommandParameter的值
    /// </summary>  
    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    protected virtual void OnCommandParameterChanged(object oldValue, object newValue)
    {
        UpdateIsEnabled();
    }

    protected virtual void OnCommandChanged(ICommand oldValue, ICommand newValue)
    {
        if (oldValue != null)
            oldValue.CanExecuteChanged -= OnCanExecuteChanged;

        if (newValue != null)
            newValue.CanExecuteChanged += OnCanExecuteChanged;

        UpdateIsEnabled();
    }

    protected virtual void UpdateVisualState(bool useTransitions)
    {
        if (IsEnabled)
        {
            VisualStateManager.GoToState(this, "Normal", useTransitions);
        }
        else
        {
            VisualStateManager.GoToState(this, "Disabled", useTransitions);
        }
    }

    protected override void OnPointerPressed(PointerRoutedEventArgs e)
    {
        base.OnPointerPressed(e);
        Click?.Invoke(this, new RoutedEventArgs());
        if ((null != Command) && Command.CanExecute(CommandParameter))
        {
            Command.Execute(CommandParameter);
        }
    }

    private void OnCanExecuteChanged(object sender, EventArgs e)
    {
        UpdateIsEnabled();
    }

    private void UpdateIsEnabled()
    {
        IsEnabled = (null == Command) || Command.CanExecute(CommandParameter);
        UpdateVisualState(true);
    }
}

以下是使用示例,作用是当TextBox的Text不为空时可以点击MenuItem,并且将Text作为MessageDialog的内容输出:

<StackPanel>
    <TextBox x:Name="TextElement"/>
    <local:MenuItem Command="{Binding}" CommandParameter="{Binding ElementName=TextElement,Path=Text}"/>
</StackPanel>
public MenuItemSamplePage()
{
    this.InitializeComponent();
    var command = new DelegateCommand<object>(Click, CanExecute);
    this.DataContext = command;
}

private void Click(object parameter)
{
    MessageDialog dialog = new MessageDialog(parameter.ToString());
    dialog.ShowAsync();
}

private bool CanExecute(object parameter)
{
    string text = parameter as string;
    return string.IsNullOrWhiteSpace(text) == false;
}

这里用到的DelegateCommand也是UWPCommunityToolkit中的类 :DelegateCommand

目录
相关文章
|
7月前
|
C# Windows
2000条你应知的WPF小姿势 基础篇<78-81 Dialog/Location/WPF设备无关性>
2000条你应知的WPF小姿势 基础篇<78-81 Dialog/Location/WPF设备无关性>
57 0
|
9月前
|
前端开发 C# 容器
WPF技术之Command
WPF Command是一种在MVVM(Model-View-ViewModel)模式中用于处理用户界面交互的机制。它可以将用户界面事件(如按钮点击、菜单项选择等)与应用程序逻辑(命令处理)解耦,同时提供了一种便捷的方式来管理和执行命令
151 2
|
C#
一起谈.NET技术,WPF 自定义快捷键命令(Command)
命令简介      WPF 中的命令是通过实现 ICommand 接口创建的。ICommand 公开两个方法(Execute 及 CanExecute)和一个事件(CanExecuteChanged)。
1481 0
|
前端开发 容器 开发工具
[uwp]自定义Behavior之随意拖动
原文:[uwp]自定义Behavior之随意拖动   由于最近有需求,所以自定义了一个随意拖动元素的Behavior.   当然在使用这个自定义的Behavior时,有个小假设:拖动元素必须是Canvas容器的子元素。
806 0
|
C#
WPF DataTomplate中Command无效
原文:WPF DataTomplate中Command无效 问题:在DataTomplate中添加一个Button,Button添加Command,但是Command生效。 原因:ItemTemplate的DataContext指代不明,需要改为父类的DataContext。
1250 0
|
C# 前端开发
WPF 绑定Command方式
WPF中,我们使用MVVM,在ViewModel中定义Command和其业务逻辑,界面绑定Command。 那么是不是所有的事件都可以定义Command呢,然后将业务全部放在ViewModel中呢? 界面CommandBindings 如果只是交互的处理,可以直接定义RoutedCommand即可 1.
2024 0
|
Windows
背水一战 Windows 10 (76) - 控件(控件基类): Control - 基础知识, 焦点相关, 运行时获取 ControlTemplate 和 DataTemplate 中的元素
原文:背水一战 Windows 10 (76) - 控件(控件基类): Control - 基础知识, 焦点相关, 运行时获取 ControlTemplate 和 DataTemplate 中的元素 [源码下载] 背水一战 Windows 10 (76) - 控件(控件基类): Control -...
1113 0