WPF将应用程序命令绑定到ViewModel ICommand [英] WPF Binding Application Commands to 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屋!