【问题标题】:Can't get Click or Command to work on dynamically created MenuFlyOutItems无法让 Click 或 Command 处理动态创建的 MenuFlyOutItems
【发布时间】:2016-04-21 01:06:46
【问题描述】:

我有一个包含多个图层的 TreeView。每个项目都填充有 MenuFlyoutItem 的 ObservableCollection,具体取决于其在运行时动态创建的对象类型。请务必注意,这是在 Windows 10 通用应用程序中,因此任何解决方案或建议都需要与它们相关。

TreeView 对象是这样创建的:

public TreeViewItemModel(object thing)
    {
        MenuItems.Clear();
        if (thing.GetType() == typeof (Space))
        {
            var space = (Space)thing;
            var parentName = string.Empty;
            if (space.Parent != null)
            {
                parentName = space.Parent.Name;
            }
            Name = space.Name;
            ParentName = parentName;
            Id = space.Id;
            var addDeviceMenuItem = new MenuFlyoutItem { Name = "AddDevice", Text = "Add Device"};
            var addSensorMenuItem = new MenuFlyoutItem { Name = "AddSensor", Text = "Add Sensor" };
            var addSpaceMenuItem = new MenuFlyoutItem { Name = "AddSpace", Text="Add Space"};
            var updateMenuItem = new MenuFlyoutItem { Name = "UpdateSpaceInfo", Text = "Update Space Info" };
            var deleteMenuItem = new MenuFlyoutItem { Name = "DeleteSpace", Text = "Delete Space" };
            var items = new ObservableCollection<MenuFlyoutItem> {addDeviceMenuItem, addSensorMenuItem,addSpaceMenuItem, updateMenuItem, deleteMenuItem};
            MenuItems = items;
            Children = new ObservableCollection<TreeViewItemModel>(space.Children.Select(s => new TreeViewItemModel(s)).Union(space.Devices.Select(d => new TreeViewItemModel(d)).Union(space.Sensors.Select(sensor => new TreeViewItemModel(sensor)))));
        }

        else if (thing.GetType() == typeof (Device))
        {
            var device = (Device) thing;
            var parentName = device.Space.Name;
            Name = device.Name;
            ParentName = parentName;
            Id = device.Id;
            var addMenuItem = new MenuFlyoutItem { Name = "AddSensor", Text = "Add Sensor" };
            var updateMenuItem = new MenuFlyoutItem { Name = "UpdateDeviceInfo", Text = "Update Device Info" };
            var deleteMenuItem = new MenuFlyoutItem { Name = "DeleteDevice", Text = "Delete Device" };
            var items = new ObservableCollection<MenuFlyoutItem> { addMenuItem, updateMenuItem, deleteMenuItem };
            MenuItems = items;
            Children = new ObservableCollection<TreeViewItemModel>(device.Sensors.Select(s => new TreeViewItemModel(s)));
        }

        else if (thing.GetType() == typeof(Sensor))
        {
            var sensor = (Sensor) thing;
            var space = sensor.Space.Name ?? string.Empty;
            var device = sensor.Device;
            ParentName = device == null ? "No Matching Device" : device.Name;
            Name = sensor.Id.ToString();
            Id = sensor.Id;
            ParentName = space;
            var updateMenuItem = new MenuFlyoutItem { Name = "UpdateSensorInfo", Text = "Update Sensor Info" };
            var deleteMenuItem = new MenuFlyoutItem { Name = "DeleteSensor", Text = "Delete Sensor" };
            var items = new ObservableCollection<MenuFlyoutItem> {updateMenuItem, deleteMenuItem};
            MenuItems = items;
            Children = null;
        }

    }

我在 xaml 中的 TreeView 如下所示:

<controls:TreeView x:Name="TreeViewList" Grid.Row="0" Margin="5" ItemsSource="{Binding TreeSpaces}">
        <controls:TreeView.ItemTemplate>
            <DataTemplate>
                <data:DataTemplateExtensions.Hierarchy>
                    <data:HierarchicalDataTemplate ItemsSource="{Binding Children}"/>
                </data:DataTemplateExtensions.Hierarchy>
                <Button x:Name="TreeButton" Content="{Binding Name}" BorderThickness="0" BorderBrush="Transparent" Background="Transparent">
                    <Button.Flyout>
                        <Flyout common:BindableFlyout.ItemsSource="{Binding MenuItems}">
                            <common:BindableFlyout.ItemTemplate>
                                <DataTemplate>
                                    <MenuFlyoutItem Text="{Binding Text}"/>
                                </DataTemplate>
                            </common:BindableFlyout.ItemTemplate>
                        </Flyout>
                    </Button.Flyout>
                </Button>
            </DataTemplate>
        </controls:TreeView.ItemTemplate>
    </controls:TreeView>

我似乎无法在 MenuFlyoutItem 上触发任何形式的事件处理。

最初我尝试了&lt;MenuFlyoutItem Name={Binding Name} Text={Binding Text} view:EventHandlers.Attach="Click"/&gt; 这可以通过自定义 MVVM 实现将事件附加到 ViewModel 处理程序。在幕后,我们的附加机制获取对象的名称并与单击相关联,因此如果名称为 SaveButton:public void SaveButton_Click(object sender, RoutedEventArgs e),则在视图模型中看起来像这样。现在,我通常从来没有遇到过这个问题,但我认为问题可能源于尝试使用 DataBinding 作为 MenuFlyoutItem 的名称,而不是传统的 x:Name="blah blah";但是,尝试也没有奏效。我想这可能是因为它是一个 MenuFlyoutItem 而不是一个按钮,所以我尝试将其更改为 &lt;Button/&gt; 以及所有相应的东西,但这也不起作用。所以,我回到 MenuFlyoutItem 并尝试使用 Command 属性。即Command="{Binding MenuItemSelected}"。然后在我的 ViewModel 中,我有以下内容:

public RelayCommand<object> MenuItemSelected { get; internal set; }
public TreeViewPageVM()
{
    MenuItemSelected = new RelayCommand<object>(TestAction);
}

private void TestAction(object sender)
{

}

那也不起作用...因此,尽管我想使用 MVVM,但我还是通过使用传统的 &lt;MenuFlyoutItem x:Name="MenuItem" Text="{Binding Text} Click="MenuItem_Clicked" 和相应的 private void MenuItem_Clicked(object sender, RoutedEventArgs e) 处理程序来尝试后面的代码。令我惊讶的是,这也不起作用。因此,我不确定是什么导致了我的 MenuFlyoutItem 生成事件的能力似乎受到抑制,但我们将不胜感激。

理想情况下,无论是单击事件还是命令事件,我都希望在我的 ViewModel 中处理它,并且我希望命令或单击事件能够获取生成 Flyout 的按钮上的 Content菜单(在事件的后期处理过程中我需要它)以及来自被点击的 MenuFlyoutItem 的 Text。

以防万一需要进一步明确: 我会像这样在树中有对象

----Object1
--------SubObject

如果我单击子对象(在本例中是一个附加了弹出按钮的按钮),则会出现一个弹出菜单,其中包含以下选项:添加、更新、删除。例如,当我单击/点击 Add 时,我需要我的结束事件处理程序或命令知道 SubObject(特别是 Content,因为它是一个按钮)和被单击的 MenuFlyoutItem(特别是 Text 属性,所以我知道我是否需要添加、更新或删除)。

【问题讨论】:

    标签: c# xaml mvvm event-handling treeview


    【解决方案1】:

    上面给出的快速而正确的答案(即在创建 MenuFlyoutItem 的同一代码中添加命令)导致您的代码违反 MVVM 模式,因为您在 ViewModel 中创建 View 的控件,因此更好的解决方案是

    1) 为 Flyout 实现 ViewModel

    public class DeviceViewModel
        {
            public string Name { get; set; }
    
            internal int treeNum;
    
            private DelegateCommand flyoutCommand;
            public ICommand FlyoutCommand
            {
                get
                {
                    if (flyoutCommand == null)
                    {
                        flyoutCommand = new DelegateCommand((parameter) => FlyoutLogic(), (parameter) => CanFlyout());
                    }
                    return flyoutCommand;
                }
            }
    
            private bool CanFlyout()
            {
                return true;
            }
    
            private void FlyoutLogic()
            {
                Debug.WriteLine("here we go " + Name + treeNum);
            }
        }
    

    2) 实例化 ViewModel

                        var addDeviceMenuItem = new DeviceViewModel { Name = "AddDevice", treeNum = _itemId  };
                        var addSensorMenuItem = new DeviceViewModel { Name = "AddSensor", treeNum = _itemId  };
                        var addSpaceMenuItem = new DeviceViewModel { Name = "AddSpace", treeNum = _itemId  };
                        var updateMenuItem = new DeviceViewModel { Name = "UpdateSpaceInfo", treeNum = _itemId  };
                        var deleteMenuItem = new DeviceViewModel { Name = "DeleteSpace", treeNum = _itemId  };
                        var items = new ObservableCollection<DeviceViewModel> { addDeviceMenuItem, addSensorMenuItem, addSpaceMenuItem, updateMenuItem, deleteMenuItem };
                        tree.Add(
                            new TreeItemModel
                            {
                                Branch = b,
                                Depth = d,
                                Text = "Item " + _itemId++,
                                Children = BuildTree(d, b),
                                MenuItems = items
                    });
    

    与

    ObservableCollection<DeviceViewModel> _menuItems;
    public ObservableCollection<DeviceViewModel> MenuItems
    {
        get { return _menuItems; }
        set { this.SetProperty(ref _menuItems, value); }
    }
    

    3) 最后将 XAML 绑定到 ViewModel

    <controls:TreeView x:Name="TreeViewList" Grid.Row="0" Margin="5" ItemsSource="{Binding TreeItems}">
        <controls:TreeView.ItemTemplate>
            <DataTemplate>
                <data:DataTemplateExtensions.Hierarchy>
                    <data:HierarchicalDataTemplate ItemsSource="{Binding Children}"/>
                </data:DataTemplateExtensions.Hierarchy>
                <Button
                    x:Name="TreeButton" Content="{Binding Text}" BorderThickness="3" Background="Transparent">
                    <Button.Flyout>
                        <Flyout common:BindableFlyout.ItemsSource="{Binding MenuItems}">
                            <common:BindableFlyout.ItemTemplate>
                                <DataTemplate>
                                    <MenuFlyoutItem Text="{Binding Name}" Command="{Binding FlyoutCommand}"/>
                                </DataTemplate>
                            </common:BindableFlyout.ItemTemplate>
                        </Flyout>
                    </Button.Flyout>
                </Button>
            </DataTemplate>
        </controls:TreeView.ItemTemplate>
    </controls:TreeView>
    

    【讨论】:

      【解决方案2】:

      您可以在创建MenuFlyoutItem 的相同代码中添加命令,所以

       var addDeviceMenuItem = new MenuFlyoutItem { Name = "AddDevice", Text = "Add Device"
       };
       addDeviceMenuItem.Command = AddDeviceCommand;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-24
        • 1970-01-01
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-22
        • 1970-01-01
        相关资源
        最近更新 更多