教程5:指令

作者: YottaYuan | 来源:发表于2020-03-13 10:34 被阅读0次

指令指令

教程数据

命令使您可以在一处定义动作,然后从所有用户界面控件(如MenuItemsToolBarsButtons)中引用它们。命令示例包括在许多应用程序中发现的CopyCutPaste操作。应用程序通常通过多种机制同时公开这些动作:菜单中的MenuItems,ContextMenu中的 MenuItems ,工具栏上的按钮,键盘快捷键等。

命令有几个目的。第一个目的是将语义和调用命令的对象与执行命令的逻辑分开。这允许多个不同的源调用相同的命令逻辑,并且允许针对不同目标定制命令逻辑。例如,在许多应用程序中找到的编辑操作CopyCutPaste可以通过使用不同的用户操作(如果使用命令来实现)来调用。应用程序可能允许用户通过单击按钮,选择菜单中的项目或使用组合键(例如CTRL + X)来剪切选定的对象或文本。。通过使用命令,可以每种类型的用户操作绑定到相同的逻辑。

命令还是任何MVVM(模型-视图-视图模型)应用程序的重要组件之一。MVVM体系结构有助于解耦应用程序的组件,从而使应用程序更易于单元测试,维护和扩展。

命令绑定

命令实际上并不自己执行任何操作。从根本上讲,它们由ICommand接口组成,该接口仅定义一个事件和两个方法:Execute()CanExecute()。第一个用于执行实际动作,而第二个用于确定该动作当前是否可用。要执行命令的实际动作,您需要在命令和代码之间建立链接,而CommandBinding正是在此起作用。

的CommandBinding通常在定义窗口用户控件,并持有到参考命令其处理,以及为应付实际的事件处理程序执行()CanExecute()的事件命令

命令的语义在应用程序和类之间可以保持一致,但是操作的逻辑特定于所作用的特定对象。例如,组合键CTRL + X在文本类,图像类和Web浏览器中调用Cut命令,但是执行Cut操作的实际逻辑是由执行剪切的应用程序定义的。一的RoutedCommand使客户能够实现逻辑。文本对象可以将所选文本剪切到剪贴板中,而图像对象可以剪切所选图像。当应用程序处理Executed事件时,它可以访问命令的目标并可以采取适当的措施。 动作取决于目标的类型。

内置命令

诸如ButtonCheckBoxMenuItem之类的控件具有代表您与任何命令进行交互的逻辑。它们公开了一个简单的Command属性。设置后,这些控件在引发Click事件时会自动调用命令的Execute方法(当CanExecute返回true时)。此外,它们通过利用CanExecuteChanged事件自动将其IsEnabled的值与CanExecute的值保持同步。通过简单的属性分配支持所有这些功能,XAML即可提供所有这些逻辑。

ApplicationCommands提供了一组可供使用的内置命令。

<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <DockPanel Width="300" Height="300" Background="#505860">
    <Menu DockPanel.Dock="Top">
      <MenuItem Header="File"/>
      <MenuItem Header="Edit">
        <MenuItem Header="Copy" Command="ApplicationCommands.Copy"/>
        <MenuItem Header="Cut" Command="ApplicationCommands.Cut"/>
        <MenuItem Header="Paste" Command="ApplicationCommands.Paste"/>
      </MenuItem>
      <MenuItem Header="Help"/>
    </Menu>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
      <TextBox Width="200"/>
      <TextBox Width="200" Margin="0,10,0,0"/>
    </StackPanel>
  </DockPanel>
</Grid>

自定义命令

创建自定义命令的最简单方法是实现ICommand接口。例如,调用委托命令的实现如下:

注意

请有关扩展noesisGUI的更多信息,请参见我们的扩展NoesisGUI教程

C ++

class DelegateCommand final: public Noesis::BaseCommand
{
public:
    typedef Noesis::Delegate<void (BaseComponent*)> ExecuteFunc;
    typedef Noesis::Delegate<bool (BaseComponent*)> CanExecuteFunc;

    DelegateCommand(const ExecuteFunc& execute): _execute(execute) {}
    DelegateCommand(const ExecuteFunc& execute, const CanExecuteFunc& canExecute):
        _execute(execute), _canExecute(canExecute) {}

    bool CanExecute(BaseComponent* param) const override
    {
        return _canExecute.Empty() || _canExecute(param);
    }

    void Execute(BaseComponent* param) const override
    {
        _execute(param);
    }

private:
    ExecuteFunc _execute;
    CanExecuteFunc _canExecute;
};

C#

public class DelegateCommand: ICommand
{
    public DelegateCommand(Action<object> execute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        _execute = execute;
    }

    public DelegateCommand(Func<object, bool> canExecute, Action<object> execute)
    {
        if (canExecute == null)
        {
            throw new ArgumentNullException("canExecute");
        }
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        _canExecute = canExecute;
        _execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, System.EventArgs.Empty);
        }
    }

    private Func<object, bool> _canExecute;
    private Action<object> _execute;
}

MVVM示例

MVVM模式可以通过使用命令和ViewModel类在NoesisGUI中实现。该视图模型是负责将数据绑定到所述XAML和执行将由命令调用的委托的类。例如:

注意

在此示例中,未按堆方式分配DelegateCommand来减少分配,如《C ++体系结构指南》中所述。这是可以安全执行且不需要引用计数的情况之一。

C ++

class ViewModel: public NotifyPropertyChangedBase
{
public:
    ViewModel()
    {
        _command.SetExecuteFunc(MakeDelegate(this, &ViewModel::SayHello));
    }

    const char* GetInput() const
    {
        return _input;
    }

    void SetInput(const char* input)
    {
        String::Copy(_input, sizeof(_input), value);
    }

    const char* GetOutput() const
    {
        return _output;
    }

    void SetOutput(const char* output)
    {
        if (!String::Equals(_output, value))
        {
            String::Copy(_output, sizeof(_output), value);
            OnPropertyChanged("Output");
        }
    }

    DelegateCommand* GetSayHelloCommand() const
    {
        return &_command;
    }

private:
    void SayHello(BaseComponent* param_)
    {
        if (Boxing::CanUnbox<NsString>(param_))
        {
            const char* param = Boxing::Unbox<NsString>(param_).c_str();

            char text[512];
            snprintf(text, sizeof(text), "Hello, %s (%s)", _input, param);
            SetOutput(text);
        }
    }

private:
    DelegateCommand _command;
    char _input[256] = "";
    char _output[256] = "";

    NS_IMPLEMENT_INLINE_REFLECTION(ViewModel, NotifyPropertyChangedBase)
    {
        NsMeta<TypeId>("Commands.ViewModel");
        NsProp("Input", &ViewModel::GetInput, &ViewModel::SetInput);
        NsProp("Output", &ViewModel::GetOutput, &ViewModel::SetOutput);
        NsProp("SayHelloCommand", &ViewModel::GetSayHelloCommand);
    }
};

C#

public class ViewModel : NotifyPropertyChangedBase
{
    public string Input { get; set; }

    private string _output = string.Empty;
    public string Output
    {
        get { return _output; }
        set
        {
            if (_output != value)
            {
                _output = value;
                OnPropertyChanged("Output");
            }
        }
    }

    public Noesis.Samples.DelegateCommand SayHelloCommand { get; private set; }

    public ViewModel()
    {
        SayHelloCommand = new Noesis.Samples.DelegateCommand(SayHello);
    }

    private void SayHello(object parameter)
    {
        string param = (string)parameter;
        Output = System.String.Format("Hello, {0} ({1})", Input, param);
    }
}

以下XAML显示了如何使用以前的ViewModel。将其设置为根元素的DataContext。面板内部的按钮获取从ViewModel分配的命令。每当按下按钮时,就会触发该命令。

CommandsTutorialImg1.jpg
<Grid
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Viewbox>
    <Border x:Name="myBorder" DataContext="{StaticResource ViewModel}" Width="400" Margin="50"
        Background="#801C1F21" BorderThickness="1" CornerRadius="5" BorderBrush="#D0101611"
        Padding="5" HorizontalAlignment="Center" VerticalAlignment="Center">
      <StackPanel Orientation="Vertical">
        <TextBox MinWidth="300" Margin="3" Text="{Binding Input, Mode=TwoWay}" FontSize="28"/>
        <TextBox x:Name="Param" MinWidth="300" Margin="3" Text="" FontSize="24"/>
        <Button Content="Say Hello" Margin="3" Command="{Binding SayHelloCommand}"
            CommandParameter="{Binding Text, ElementName=Param}" FontSize="28" />
        <Viewbox Margin="5" Height="50">
          <TextBlock Margin="5" Padding="0" TextAlignment="Center" Text="{Binding Output}"
              FontSize="28" Foreground="White"/>
        </Viewbox>
      </StackPanel>
    </Border>
  </Viewbox>

</Grid>

相关文章

网友评论

    本文标题:教程5:指令

    本文链接:https://www.haomeiwen.com/subject/lfgfshtx.html