将动态创建的菜单项与按钮上下文菜单的 ICommand 绑定

3

我是WPF的新手,需要学习更多有关MVVM的知识。我的代码并没有专门设计成MVVM模式,但我已经将一个类指定为GUI的接口和控制器,而模型代码则位于另一组类中。我在网上寻找示例和与我相似的问题,虽然有很多,但是经过三天的努力我依然需要帮助。

我需要一个简单的下拉菜单,其中的项可以动态更新(这是一个与USB设备通信的应用程序,因此应显示可用设备的数量以及它们的设备ID和序列号),并且当前选择的项应该显示在按钮上(或者无论哪种下拉菜单实现方式)。在这个示例中,我只创建了一个静态列表,但是在完整的应用程序中将动态更新同样的列表。

到目前为止,我的代码看起来是正确的:我让当前选定的设备ID字符串显示在按钮上,并在按下按钮时获取所有可用设备的列表(当前选定的设备出现在列表中不会对我造成太大的困扰)。然而,我无法钩入任何事件,当项被选择时,因此无法更新按钮中的项,或者执行其他任何操作。

以下是我的XAML。请注意,这是粗略地混合在一起的,其中有一些毫无意义的东西,比如“IsActive”用于“IsChecked”属性,这来自示例。最大的问题是据我所知,ContextMenu.Resources中的任何Setter属性似乎都没有起作用...尝试更改字体大小但无济于事。当然,真正的大问题是“MyCommand”绑定不起作用,该方法从未被调用。

    <Label Content="Device Selected:" HorizontalAlignment="Left" Margin="25,22,0,0" VerticalAlignment="Top" Width="124" FontWeight="Bold" FontSize="14" Height="25"/>
    <Button x:Name="DeviceSelMenuButton" Content="{Binding DeviceID_and_SN, Mode=TwoWay}" HorizontalAlignment="Left" Height="28" Margin="25,52,0,0" VerticalAlignment="Top" Width="187" FontSize="14" Click="DeviceSelMenuButton_Click">
        <Button.ContextMenu>
            <ContextMenu ItemsSource="{Binding DeviceID_SN_Collection, Mode=TwoWay}">
                <ContextMenu.Resources>
                    <Style x:Key="SelectDeviceStyle" TargetType="MenuItem">
                        <Setter Property="Command" Value="{Binding MyCommand}"/>
                       <Setter Property="CommandTarget" Value="{Binding RelativeSource Self}"/> 
                        <Setter Property="IsChecked" Value="{Binding IsActive}"/>
                        <Setter Property="IsCheckable" Value="True"/>
                        <Setter Property="FontSize" Value="14"/>
                    </Style>
                </ContextMenu.Resources>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>

以下是MainWindow.xaml.cs中的代码:

public partial class MainWindow : Window
{
    CustomDeviceGUI _customDeviceGui = new CustomDeviceGUI();

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = _customDeviceGui;
    }

    private void DeviceSelMenuButton_Click(object sender, RoutedEventArgs e)
    {
        // " (sender as Button)" is PlacementTarget
        (sender as Button).ContextMenu.IsEnabled = true;
        (sender as Button).ContextMenu.PlacementTarget = (sender as Button);
        (sender as Button).ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
        (sender as Button).ContextMenu.IsOpen = true;
    }

    private void SomeMethod(object sender, DataTransferEventArgs e)
    {
        // TODO Somehow get the index of the selected menu item (collection index, 0-based)
        //     int selIndex = (sender as Button).ContextMenu.Items.IndexOf    ??         
        _customDeviceGui.UpdateDeviceID("RelayPro id updated");
    }

}

以及GUI代码:

class CustomDeviceGUI : INotifyPropertyChanged
{
    // Declare the event 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
    private string _deviceDisplayString;
    private ICommand _updateMenu; 
    List<string> ControllerDeviceList = new List<string>();

    private System.Collections.ObjectModel.ObservableCollection<string> _DeviceID_SN_Collection = new System.Collections.ObjectModel.ObservableCollection<string>();

    // CTOR
    public CustomDeviceGUI()
    {
        ControllerDeviceList.Add("CustomDevice Device 1");
        ControllerDeviceList.Add("CustomDevice Device 2");
        ControllerDeviceList.Add("CustomDevice Device 3");
        ControllerDeviceList.Add("CustomDevice Device 6");
        UpdateDeviceID(ControllerDeviceList[0]);
    }

    #region CustomDeviceGUI Properties

    public System.Collections.ObjectModel.ObservableCollection<string> DeviceID_SN_Collection
    {
        get
        {
            _DeviceID_SN_Collection.Clear();
            foreach (string str in ControllerDeviceList)
            {
                _DeviceID_SN_Collection.Add(str);
            }
            return _DeviceID_SN_Collection;
        }
        private set 
        {
            _DeviceID_SN_Collection = value;
        }
    }

    public string DeviceID_and_SN
    {
        get
        {
            return _deviceDisplayString;
        }
        private set
        {
            _deviceDisplayString = value;
        }
    }

    public ICommand MyCommand
    {
        get
        {
            if (_updateMenu == null)
                _updateMenu = new MyGuiCommand();

            return _updateMenu;
        }

    }


    #endregion

    #region Public Methods


    public void UpdateDeviceID(string deviceID)
    {
        this._deviceDisplayString = deviceID;
        RaisePropertyChangeEvent("DeviceID_and_SN");
        RaisePropertyChangeEvent("DeviceID_SN_Collection");            
    }

    #endregion

    protected void RaisePropertyChangeEvent(string name)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;

        try
        {
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        catch (Exception e)
        {
            //  ... TODO Remove this catchall or find specific exceptions
        }
    }


    public class MyGuiCommand : ICommand
    {
        public void Execute(object parameter)
        {
            //   Debug.WriteLine("Hello, world");
            int hmm = 3;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged // was ;
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }


} // class CustomDeviceGUI

设法拼凑出了一个解决方案。我在某种程度上理解了祖先部分,但不知道为什么ContextMenu.Resources没有起作用,或者为什么在下面的XAML中DataContext也没有起作用——请注意,我不得不重复完整的绑定命令: - Stephen
2个回答

1

我需要进行的所有更改都在XAML中。主要是使用祖先来获取正确的数据上下文。我还切换到了ContextMenu.ItemContainer而不是ContextMenu.Resources。

   <ContextMenu.ItemContainerStyle>
      <Style TargetType="MenuItem"> 
         <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=DataContext.MyCommand}"/>
      </Style>
   </ContextMenu.ItemContainerStyle>

-1

虽然我不确定,但我认为:

<Setter Property="Command" Value="{Binding MyCommand}"/>

绑定需要一个RoutedUICommand对象。

编辑: 我注意到的另一件事是,在此之前您没有设置任何命令绑定。像这样:

<Window.CommandBindings>
    <CommandBinding Command="MyCommand" Executed="Execute" />
</Window.CommandBindings>

这只是一个例子,你可以将CommandBindings设置到许多其他控件上。


我尝试了:<ContextMenu.Resources> <Style x:Key="SelectDeviceStyle" TargetType="MenuItem"> <Setter Property="Command" Value="{Binding MyRUICommand}"/>,并在DataContext类中添加了以下代码:private static RoutedUICommand myRuiCommand = new RoutedUICommand("MyRUICommand", "MyRUICommand", typeof(ApplicationCommands)); public static RoutedCommand MyRUICommand { get {return myRuiCommand;} }但上述语句仍未被执行。我还尝试了Window.CommandBindings,但我认为我需要创建一个ApplicationCommands。 - Stephen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接