0

这个问题的变体似乎很常见,但我还没有找到适合我的解决方案。我正在尝试使用 Observable 集合在 Button.ContextMenu 中放置一个下拉菜单,并认为我在正确的轨道上,缺少一个:我还没有能够获得所选项目的索引,虽然我可以在调试器中看到我的收藏,我开始怀疑这些项目是否真的被找到了,我会边走边解释。首先,XAML……你可以看到我有一个按钮内容的绑定,这个想法是在一个菜单项被选中后,我的代码将更新该属性。如果我可以获得正在收集的集合的索引,它可以:

    <Button x:Name="DeviceSelMenuButton" Content="{Binding DeviceID_and_SN, Mode=TwoWay}" HorizontalAlignment="Left" Height="28" Margin="25,103,0,0" VerticalAlignment="Top" Width="187" FontSize="14" Click="DeviceSelMenuButton_Click">
        <Button.ContextMenu>
            <ContextMenu ItemsSource="{Binding DeviceID_SN_Collection, Mode=TwoWay}">
                <ContextMenu.ItemContainerStyle>
                    <Style TargetType="MenuItem">
                        <Setter Property="IsCheckable" Value="true"/>
                        <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=DataContext.MyCommand}"/>
                        <Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
                    </Style>
                </ContextMenu.ItemContainerStyle>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>

正如您所想象的,我已经为 CommandParameter 绑定尝试了许多变体,但是有了这个,我至少可以在我的 ICommand 方法中看到一些东西。困扰我的是,在第一次按下按钮时(在输出窗口中没有关于绑定的错误),在 ContextMenu cm 的 Items 属性下,我在 items.CurrentItem 和 item 下看到了一个合法的项目标签.CurrentPosition 是 0 - 起初看起来很有希望,希望我可以将其用作索引,直到我意识到我正在查看最后一项,所以它一定是没有意义的。之后,第二次以及所有后续按下按钮,items.CurrentItem 为空,items.Current 位置为 0xffffffff。粘贴相关的代码,从定义集合、ICommand 等的类开始:

class CustomDeviceGUI : INotifyPropertyChanged
{
    // Declare the event 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
    private string _deviceDisplayString;
    private ICommand UpdateMenuICommand; 
    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(3);  // TODO Get from GUI!!!
    }

    #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 (UpdateMenuICommand == null)
                UpdateMenuICommand = new MyGuiCommand();

            return UpdateMenuICommand;
        }
        set
        {
            UpdateMenuICommand = value;
            RaisePropertyChangeEvent("MyCommand");  // ????
        }
    }

    public void UpdateDeviceID(int deviceID)
    {
        this._deviceDisplayString = ControllerDeviceList[deviceID];
        RaisePropertyChangeEvent("DeviceID_and_SN");
        RaisePropertyChangeEvent("DeviceID_SN_Collection");    
    }

    public class MyGuiCommand : ICommand
    {
        // Two events are kicked off when the command is executed
        public static event UpdateDeviceSelectedEventHandler UpdateDeviceSelectedEvent;

        // defining signature for any event handlers for the events we create here
        public delegate void UpdateDeviceSelectedEventHandler(int deviceIndex); 

        public void Execute(object parameter)
        {
            ContextMenu cm = (ContextMenu)parameter;
            var itemSource = cm.ItemsSource;
            var itemBG = cm.ItemBindingGroup;
            var items = cm.Items;

            UpdateDeviceSelectedEvent(1); // TODO parameter with index from GUI    
        }

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

最后是来自 MainWindow.xaml.cs 的相关代码,这里发生的事情不多:

    // register for the event from the ICommand.Execute
    WpfBindingAttempts.CustomDeviceGUI.MyGuiCommand.UpdateDeviceSelectedEvent += new WpfBindingAttempts.CustomDeviceGUI.MyGuiCommand.UpdateDeviceSelectedEventHandler(UpdateDeviceSelectedAfterSwitch);

    // Handles event that occurs when a different device is selected
    //    via the dropdown menu -- sets the active device, and updates its ID/SN
    void UpdateDeviceSelectedAfterSwitch(int deviceIndex)
    {
         _customDeviceGui.UpdateDeviceID(deviceIndex);
    }

按钮后面的代码:

    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;
    }

认为这就是一切。任何帮助是极大的赞赏。如果这看起来很熟悉,那就是。我越过了第一道障碍,撞上了这堵砖墙。

4

1 回答 1

1

I don't think there's any direct way to return the selected index of an item within a ContextMenu. I don't believe the CurrentItem property holds anything to do with which item has been selected. I'm not sure exactly what this property does do (it might be something used internally by the framework), but I would recommend that you ignore it.

Instead of making the context menu be the CommandParameter, make the items in your collection the CommandParameters. To do this, change the CommandParameter setter to the following:

<Setter Property="CommandParameter" Value="{Binding}" />

Then, pass the list of all devices to your MyGuiCommand, in a constructor argument for example. Finally, in your Execute method, search within this list of devices to find the selected device, which will be in the parameter.

于 2013-11-09T15:04:00.977 回答