Как связать команды с моделью представления? - PullRequest
1 голос
/ 02 августа 2020

Я прочитал несколько сообщений о привязке и командах, но изо всех сил пытаюсь получить то, что хочу, работало.

Приведенное ниже работает нормально

public partial class TailoredReading : Window
    {

        public static RoutedUICommand myRoutingCommand = new RoutedUICommand("myCommand", "myCommand", typeof(InputGestureWindow));

        public TailoredReading()
        {
            InitializeComponent();
        }

        private void SaveResource_Click(object sender, RoutedEventArgs e)
        {
            //ViewModel.SaveResource();
        }

        void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
        {
            String command = ((RoutedCommand)e.Command).Name;
            MessageBox.Show("The \"" + command + "\" command has been invoked NOW. ");
        }

        void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

    }
<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">

    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

    <Window.InputBindings>
        <KeyBinding Command="{x:Static this:TailoredReading.myRoutingCommand}" Key="F1" />
    </Window.InputBindings>

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static this:TailoredReading.myRoutingCommand}" CanExecute="myRoutingCommandCanExecute" Executed="myRoutingCommandExecuted"/>
    </Window.CommandBindings>

Однако, Я хотел бы сохранить команду logi c отдельно, в собственном классе.

public class Commands
    {
        public static readonly RoutedUICommand myRoutingCommand = new RoutedUICommand("myCommand", "myCommand", typeof(InputGestureWindow));

        void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
        {
            String command = ((RoutedCommand)e.Command).Name;
            MessageBox.Show("The \"" + command + "\" command has been invoked. ");
        }

        void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
    }
<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">

    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

<Window.InputBindings>
        <KeyBinding Command="{x:Static this:Commands.myRoutingCommand}" Key="F1" />
    </Window.InputBindings>

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static this:Commands.myRoutingCommand}" CanExecute="myRoutingCommandCanExecute" Executed="myRoutingCommandExecuted"/>
    </Window.CommandBindings>

Когда я это делаю, даже после очистки и восстановления я получаю сообщение об ошибке, что команд нет в пространство имен. Хотя он и расположен чуть ниже класса окна.

Есть идеи?

Пол

Ответы [ 4 ]

2 голосов
/ 03 августа 2020

myRoutingCommandCanExecute и myRoutingCommandExecuted - обработчики событий. Вы не можете определить их в другом классе.

Фактически, использование RoutedUICommand не очень полезно, если вы хотите отделить лог c выполнения от представления. Пожалуйста, обратитесь к этому сообщению в блоге для получения дополнительной информации об этом.

Вам нужно создать собственный класс, который реализует ICommand и принимает Action<object> и Predicate<object> :

public class DelegateCommand : System.Windows.Input.ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public DelegateCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
            return true;

        return _canExecute(parameter);
    }

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

    public event EventHandler CanExecuteChanged;
}

Затем вы создаете экземпляры команды в своей модели представления, где также можете определить журнал выполнения c:

public class ViewModel
{
    public ViewModel()
    {
        MyCommand = new DelegateCommand(MyCommandExecuted, MyCommandCanExecute);
    }

    public DelegateCommand MyCommand { get; }

    private void MyCommandExecuted(object obj)
    {
        MessageBox.Show("The command has been invoked.");
    }

    private bool MyCommandCanExecute(object obj)
    {
        return true;
    }
}

Затем представление привязывается к свойству команды модели представления:

<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">
    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

    <Window.InputBindings>
        <KeyBinding Command="{Binding MyCommand}" Key="F1" />
    </Window.InputBindings>
</Window>

Очевидно, вам не нужно реализовывать Action<object> и Predicate<object>, которые вы передаете команде в классе модели представления. Вы можете реализовать их где угодно.

1 голос
/ 03 августа 2020
  1. Лог c ваших команд не имеет ничего общего с работой с данными, поэтому нет смысла реализовывать его в ViewModel. Ваш класс Commands не является ViewModel, вспомогательным классом, который является частью View.

  2. Расширение разметки «x: Stati c», насколько мне известно, может получить значение констант, перечислений или полей и свойств STATI C. Но не ОБЫЧНЫЙ МЕТОД!

Попробуйте эту реализацию:

public static class Commands
{
    public static RoutedUICommand MyRoutingCommand { get; } = new RoutedUICommand("myCommand", "myCommand", typeof(Commands));

    public static ExecutedRoutedEventHandler MyRoutingCommandExecuted { get; } 
      = myRoutingCommandExecuted;

    private static void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
    {
        string command = ((RoutedCommand)e.Command).Name;
        MessageBox.Show("The \"" + command + "\" command has been invoked. ");
    }

    public static CanExecuteRoutedEventHandler MyRoutingCommandCanExecute { get; } 
      = myRoutingCommandCanExecute;

    private static void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }
}

XAML:

<Window.InputBindings>
    <KeyBinding Command="{x:Static this:Commands.MyRoutingCommand}" Key="F1" />
</Window.InputBindings>

<Window.CommandBindings>
    <CommandBinding Command="{x:Static this:Commands.MyRoutingCommand}"
                    CanExecute="{x:Static this:Commands.MyRoutingCommandCanExecute}"
                    Executed="{x:Static this:Commands.MyRoutingCommandExecuted}"/>
</Window.CommandBindings>

И, конечно, убедитесь, что пространства имен верны.

После внесения изменений в XAML может появиться предупреждение об ошибке. Но это связано с тем, что информация для XAML извлекается не из проекта, а из сборки. Следовательно, ошибки должны исчезнуть после сборки измененного проекта.

0 голосов
/ 02 августа 2020

Не знаю, устранили ли проблему? Я предлагаю ее решить. Если нет необходимости использовать RoutedUICommand, измените его на собственный класс с именем RelayCommand (ссылка: реализация RelayCommand ), производный от ICommand. Тогда ваш Commands класс будет выглядеть так:

namespace WpfAppNet.Commands
{
    public class Commands
    {
        public static ICommand MyRoutingCommand = new RelayCommand(MyRoutingCommandExecuted, (o) =>
        {
            return MyRoutingCommandCanExecute();
        });

        private static void MyRoutingCommandExecuted(object target)
        {
            MessageBox.Show("The command has been invoked. ");
        }

        private static bool MyRoutingCommandCanExecute()
        {
            return true;
        }
    }
}

В вашем TailoredReading Window XAML-файле вы добавили пространство имен, в котором вы разместили класс. В моем примере это clr-namespace:WpfAppNet.Commands (как первая строка кода, вырезанного). Если вы уже добавили это в псевдоним namspace, вам не нужно этого делать.

Посмотрите, есть ли в ваших фрагментах кода свой файл Commands.cs и TailoredReading.cs в папку Resources . Если нет, это может быть причиной вашей ошибки.

<Window x:Class="WpfAppNet.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
        xmlns:local="clr-namespace:WpfAppNet"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        mc:Ignorable="d"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        <!-- add an namespace alias where the file Commands.cs is located -->
        xmlns:commands="clr-namespace:WpfAppNet.Commands"
        Title="MainWindow" Height="450" Width="800">

    <Window.InputBindings>
        <!-- Bind to the command -->
        <KeyBinding Command="{x:Static commands:Commands.MyRoutingCommand}" Key="F1" />
    </Window.InputBindings>
 ...
</Window>

Преимущество класса RelayCommand состоит в том, чтобы избежать реализации Execute и CanExecute для каждую новую команду. Вам нужно только реализовать для них методы / функции.

PS: Также я видел, что вы добавили дважды один и тот же путь для разных псевдонимов пространства имен:

xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"

Один из них можно удалить.

0 голосов
/ 02 августа 2020

При привязке команд в XAML: я думаю, это потому, что методы, привязанные к CanExecute и Executed, должны быть членами экземпляра кода, стоящего за классом.

Если вы хотите делать то, что делаете, вы можете, например, сделать это в конструкторе stati c в коде:

  public class Commands
  {
    public static RoutedCommand MyCommand = new RoutedCommand("MyCommand", typeof(TailoredReading ));

    static Commands()
    {
      CommandBinding commandBinding = new CommandBinding(MyCommand, MyCommandCmd_Executed, MyCommandCmd_CanExecute);
      CommandManager.RegisterClassCommandBinding(typeof(TailoredReading ), commandBinding);
    }

    public static void MyCommandCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

    public static void MyCommandCmd_Executed(object sender, ExecutedRoutedEventArgs e)
    {

    }

  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...