WPF CommandBindings、ViewModel 和 UserControl

问题描述 投票:0回答:1

我对 WPF 还很陌生,所以最好描述一下其意图:

主窗口内有多个用户控件。每个用户控件都有自己的按钮,我想使用键盘快捷键将其绑定到命令。

MainWindow.xaml
是这样的:

<Window ...>
   <Window.DataContext>
      <viewModel:MainViewModel/>
   </Window.DataContext>    
   ...
   <ContentControl Grid.Column="1"
                   Margin="5"
                   Content="{Binding HomeToolbarView}"/>
    ...
</Window>

如您所见,我在代码隐藏中使用了一些虚拟机绑定。在

app.xaml

<Application ...>
   <Application.Resources>
       <ResourceDictionary>
           ...
           <DataTemplate DataType="{x:Type viewModel:HomeToolbarViewModel}">
               <view:HomeToolbar/>
           </DataTemplate>
           ...
       </ResourceDictionary>
   </Application.Resources>
</Application>

我将

HomeToolbar
绑定到它的
HomeToolbarViewModel
并在
MainViewModel
:

public class MainViewModel : ObservableObject {
    // This is bound to the XAML
    private object homeToolbarView;
    ...
    public HomeToolbarViewModel HomeToolbarVM { get; set; }
    ...
    public object HomeToolbarView
    {
        get { return homeToolbarView; }
        set
        {
            homeToolbarView = value;
            OnPropertyChanged();
        }
    }
    ...

    public MainViewModel() {
        ...
        HomeToolbarVM = new HomeToolbarViewModel();
        homeToolbarView = HomeToolbarVM;
        ...
    }
}

最后

HomeToolbar.xaml

<UserControl xmlns:self="clr-namespace:ChordTuner.MVVM.ViewModels"
             ...>
    <UserControl.CommandBindings>
        <!-- Error: Compiler binds this to the DataContext of the view, i.e. HomeToolbar.xaml.cs,  and not to the real DataContext, i.e. the HomeToolbarViewModel!
        <CommandBinding Command="self:HomeToolbarCommands.NewCommand" Executed="{Binding NewCommand_Executed}" .../> />
    </UserControl.CommandBindings>
    <!-- Only to simplify -->
    <StackPanel>
        <Button Command="self:HomeToolbarCommands.NewCommand" />
        <!-- No error, the command is correctly bound to the DataContext, i.e. the VM! -->
        <Button Command={Binding ARelayCommandProp}"/>
    </StackPanel>
</UserControl>

HomeToolbarViewModel.cs

namespace ChordTuner.MVVM.ViewModels {
    public class HomeToolbarViewModel : ObservableObject {
        ...
        private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("New from XAML");
        }
        ...
    }

    public static class HomeToolbarCommands
    {
        public static readonly RoutedUICommand NewCommand = new RoutedUICommand
        (
            "New1",
            "New2",
            typeof(HomeToolbarCommands),
            new InputGestureCollection()
            {
                 new KeyGesture(Key.N, ModifierKeys.Control)
            }
        );}
}

Visual Studio 输出以下错误:

CS1061“HomeToolbar”不包含“NewCommand_Executed”的定义,并且找不到接受“HomeToolbar”类型的第一个参数的可访问扩展方法“NewCommand_Executed”(您是否缺少 using 指令或程序集引用?)ChordTuner

令我感到奇怪的是,使用

RelayCommand
类的按钮正确绑定到视图模型(如
app.xaml
中所示),但
CommandBindings
却没有。

所有这一切的目标是为所有用户控制按钮分配全局快捷键。使用

InputBindings
强制用户控件处于焦点状态。但我有多个用户控件需要响应不同的命令/快捷方式,无论焦点如何。

更新 这是

HomeToolbarView
构造函数

public HomeToolbar()
{
    InitializeComponent();

    for (int index = 0; index < MidiIn.NumberOfDevices; index++)
    {
        midiInComboBox.Items.Add(MidiIn.DeviceInfo(index).ProductName);
    }

    for (int index = 0; index < MidiOut.NumberOfDevices; index++)
    {
        midiOutComboBox.Items.Add(MidiOut.DeviceInfo(index).ProductName);
    }

    // This event is called when DataContext is changed. This happens because we binded the HomeToolbarViewModel inside the app.xaml.
    DataContextChanged += new DependencyPropertyChangedEventHandler(HomeToolbar_DataContextChanged);
}
...

void HomeToolbar_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    viewModel = (HomeToolbarViewModel?)DataContext;

    if (viewModel == null) return; 

    if (MidiOut.NumberOfDevices > 0)
    {
        viewModel.SelectedMidiOutIndex = 0;
    }

    if (MidiIn.NumberOfDevices > 0)
    {
        viewModel.SelectedMidiInIndex = 0;
    }
}

有人解释一下我错过了什么吗?

c# wpf xaml icommand commandbinding
1个回答
0
投票

如果要允许视图模型执行视图命令,则必须使用

HomeToolbar
控制
ICommand
属性,您可以将其绑定到外部
ICommand
实现(例如在视图模型类上定义)。
然后,控件的内部结构会绑定到控件的这个
ICommand

HomeToolbar.xaml.cs

partial class HomeToolbar : UserControl
{
  public ICommand SaveCommand
  {
    get => (ICommand)GetValue(SaveCommandProperty);
    set => SetValue(SaveCommandProperty, value);
  }

  public static readonly DependencyProperty SaveCommandProperty = DependencyProperty.Register(
    "SaveCommand", 
    typeof(ICommand), 
    typeof(HomeToolbar), 
    new FrameworkPropertyMetadata(default));
}

HomeToolbar.xaml

<UserControl>
  <Button Content="Save data"
          Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=SaveCommand}" />
</UserControl>

应用程序.xaml

<Application ...>
  <Application.Resources>
    <ResourceDictionary>
      <DataTemplate DataType="{x:Type viewModel:HomeToolbarViewModel}">
        <view:HomeToolbar SaveCommand="{Binding SaveDataCommand}"/>
      </DataTemplate>
    </ResourceDictionary>
  </Application.Resources>
</Application>

HomeToolbarViewModel.cs

class HomeToolbarViewModel : INotifyPropertyChanged
{
  public ICommand SaveDataCommand { get; }
}
© www.soinside.com 2019 - 2024. All rights reserved.