WPF将应用程序命令绑定到ViewModel ICommand [英] WPF Binding Application Commands to ViewModel ICommand

查看:50
本文介绍了WPF将应用程序命令绑定到ViewModel ICommand的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过一个小的编辑器项目学习WPF并在考虑MVVM的情况下进行设计.

Learning WPF with a small editor project and designing it with MVVM in mind.

以下代码引发在'System.Windows.Data.Binding'上提供值引发异常."在运行时首次解析XAML时.没有构建错误.

The following code is throwing "Provide value on 'System.Windows.Data.Binding' threw an exception." at run time when the XAML is first parsed. No Build errors.

如何最好地将ICommands绑定到应用程序命令的关闭,另存为,另存为,打开,新建等

How best to bind my ICommands to Application Commands Close, Save, Save As, Open, New etc.

目前,我只有关闭"和新建"设置.

Currently I have just the Close and New setup.

XAML代码:

<Window x:Class="Editor.Views.EditorView"
        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:local="clr-namespace:Editor.Views"
        xmlns:vm="clr-namespace:Editor.ViewModels"
        xmlns:userControls="clr-namespace:Editor.UserControls"
        mc:Ignorable="d"
        Title="EditorView" Height="600" Width="800" WindowStartupLocation="CenterScreen">

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:DocumentViewModel}">
            <ContentControl Content="{Binding DocTextBox}" />
        </DataTemplate>
    </Window.Resources>

    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Close"
                        Executed="{Binding ExitCommand}" />
        <CommandBinding Command="ApplicationCommands.New"
                        Executed="{Binding NewDocumentCommand}" />
        <!--<CommandBinding Command="ApplicationCommands.Open"
                        Executed="OpenDocument" />
        <CommandBinding Command="ApplicationCommands.Save"
                        CanExecute="SaveDocument_CanExecute"
                        Executed="SaveDocument" />
        <CommandBinding Command="ApplicationCommands.SaveAs"
                        Executed="SaveDocumentAs" />-->
    </Window.CommandBindings>

    <Window.InputBindings>
        <KeyBinding Key="N" Modifiers="Control" Command="{Binding NewDocumentCommand}" />
        <KeyBinding Key="F4" Modifiers="Control" Command="{Binding CloseDocumentCommand}" />
    </Window.InputBindings>

    <DockPanel>
        <userControls:Menu x:Name="menu"
                              DockPanel.Dock="Top" />

        <TabControl ItemsSource="{Binding Documents}" SelectedIndex="{Binding SelectedIndex}">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <WrapPanel>
                        <TextBlock Text="{Binding FileName}" />
                        <Button Command="{Binding CloseCommand}" Content="X" Margin="4,0,0,0" FontFamily="Courier New" Width="17" Height="17" VerticalContentAlignment="Center" />
                    </WrapPanel>
                </DataTemplate>
            </TabControl.ItemTemplate>
        </TabControl>
    </DockPanel>
</Window>

ViewModel代码:

The ViewModel Code:

public class EditorViewModel : ViewModelBase
{
    private static int _count = 0;
    public EditorViewModel()
    {
        Documents = new ObservableCollection<DocumentViewModel>();
        Documents.CollectionChanged += Documents_CollectionChanged;
    }

    #region Event Handlers

    void Documents_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count != 0)
            foreach (DocumentViewModel document in e.NewItems)
                document.RequestClose += this.OnDocumentRequestClose;

        if (e.OldItems != null && e.OldItems.Count != 0)
            foreach (DocumentViewModel document in e.OldItems)
                document.RequestClose -= this.OnDocumentRequestClose;
    }

    private void OnDocumentRequestClose(object sender, EventArgs e)
    {
        CloseDocument();
    }

    #endregion

    #region Commands

    private RelayCommand _exitCommand;
    public ICommand ExitCommand
    {
        get { return _exitCommand ?? (_exitCommand = new RelayCommand(() => Application.Current.Shutdown())); }
    }

    private RelayCommand _newDocumentCommand;
    public ICommand NewDocumentCommand
    {
        get { return _newDocumentCommand ?? (_newDocumentCommand = new RelayCommand(NewDocument)); }
    }

    private void NewDocument()
    {
        _count++;
        var document = new DocumentViewModel { FileName = "New " + _count, DocTextBox = new RichTextBox() };
        Documents.Add(document);
        SelectedIndex = Documents.IndexOf(document);
    }

    private RelayCommand _closeDocumentCommand;
    public ICommand CloseDocumentCommand
    {
        get { return _closeDocumentCommand ?? (_closeDocumentCommand = new RelayCommand(CloseDocument, param => Documents.Count > 0)); }
    }

    private void CloseDocument()
    {
        Documents.RemoveAt(SelectedIndex);
        SelectedIndex = 0;
    }

    #endregion

    #region Public Members

    public ObservableCollection<DocumentViewModel> Documents { get; set; }

    private int _selectedIndex = 0;
    public int SelectedIndex
    {
        get { return _selectedIndex; }
        set
        {
            _selectedIndex = value;
            OnPropertyChanged();
        }
    }

    #endregion
}

推荐答案

使用 CommandBinding 时,可以说您正在配置 view 应该处理的命令.因此,我不清楚在视图模型中实现命令是否有意义.相反,如果视图模型应该拥有该命令,则使用 its 命令,而不要使用预定义的命令.

When you are using CommandBinding, arguably you are configuring commands that the view should be handling. As such, it's not clear to me that it makes sense to implement the command in the view model. Conversely, if the view model should own the command, then use its command, not a pre-defined one.

要求将您的 ICommand 对象绑定到应用程序命令没有任何意义. ApplicationCommands 对象本身就是 ICommand 实现!(具体来说是 RoutedUICommand .)

It doesn't make sense to ask to bind your ICommand object to an application command. The ApplicationCommands objects are themselves ICommand implementations! (RoutedUICommand, to be specific.)

如果您的视图模型已经为标准命令实现了 ICommand ,则只需绑定到这些命令即可:

If your view model already implements ICommand for the standard commands, then just bind to those:

<CommandBinding Command="{Binding ExitCommand}"/>

如果您确实要使用 ApplicationCommands 命令,则需要为 Executed CanExecute 订阅事件处理程序方法.事件,然后将它们委托给视图模型.例如:

If you really want to use the ApplicationCommands commands, then you'll need to subscribe an event handler method to the Executed and CanExecute events and then delegate those to the view model. For example:

<CommandBinding Command="ApplicationCommands.Close"
                Executed="Close_Executed" />

然后在代码隐藏中,如下所示:

Then in code-behind, something like this:

void Close_Executed(object sender, ExecutedRoutedEventArgs e)
{
    ICommand command = (ICommand)e.Parameter;

    command.Execute(null);
}

请注意,在这种情况下,必须确保在命令本身的源位置设置 CommandParameter .IE.在调用命令的 InputBinding Button 中包括 CommandParameter = {Binding ExitCommand} .这可能会很乏味.

Note that you'd have to make sure in this case that you set the CommandParameter at the source of the command itself. I.e. include CommandParameter={Binding ExitCommand} in the InputBinding and Button where you invoke the command. This could get tedious.

或者,您可以假设 Source 对象的 DataContext 是您的视图模型,并直接从该视图获取命令:

Alternatively, you could assume that the DataContext of the Source object is your view model and get the command directly from that:

void Close_Executed(object sender, ExecutedRoutedEventArgs e)
{
    EditorViewModel viewModel = (EditorViewModel)((FrameworkElement)e.Source).DataContext;
    ICommand command = viewModel.ExitCommand;

    command.Execute(e.Parameter);
}

这篇关于WPF将应用程序命令绑定到ViewModel ICommand的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆