无法让 Click 或 Command 处理动态创建的 MenuFlyOutItems
Can't get Click or Command to work on dynamically created MenuFlyOutItems
我有一个包含多个图层的 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 工作。
最初我尝试了 <MenuFlyoutItem Name={Binding Name} Text={Binding Text} view:EventHandlers.Attach="Click"/>
这可以通过自定义 MVVM 实现来将事件附加到 ViewModel 处理程序。在幕后,我们的附加机制获取对象的名称并与 Click 相关联,因此如果名称为 SaveButton:public void SaveButton_Click(object sender, RoutedEventArgs e)
,它在视图模型中看起来像这样。现在,我通常从来没有遇到过这个问题,但我认为问题可能源于尝试对 MenuFlyoutItem 的名称使用 DataBinding 而不是传统的 x:Name="blah blah"
;但是,尝试也没有用。我想这可能是因为它是一个 MenuFlyoutItem 而不是一个按钮,所以我尝试将它更改为 <Button/>
以及所有相应的东西,但这也没有用。因此,我返回到 MenuFlyoutItem 并尝试改用命令 属性。即 Command="{Binding MenuItemSelected}"
。然后在我的 ViewModel 中有以下内容:
public RelayCommand<object> MenuItemSelected { get; internal set; }
public TreeViewPageVM()
{
MenuItemSelected = new RelayCommand<object>(TestAction);
}
private void TestAction(object sender)
{
}
那也不管用...所以,尽管我想使用 MVVM,但我还是尝试使用传统的 <MenuFlyoutItem x:Name="MenuItem" Text="{Binding Text} Click="MenuItem_Clicked"
和相应的 private void MenuItem_Clicked(object sender, RoutedEventArgs e)
处理程序来隐藏代码。令我惊讶的是,这也不起作用。因此,我不确定是什么原因导致我的 MenuFlyoutItem 生成事件的能力似乎受到抑制,但我们将不胜感激。
理想情况下,无论是单击事件还是命令事件,我都希望在我的 ViewModel 中处理它,并且我希望命令或单击事件能够获取按钮上的 Content
生成弹出菜单(在 post 处理事件期间我需要它)以及被单击的 MenuFlyoutItem 中的 Text
。
以防万一需要进一步说明:
我会像这样在树中放置对象
----Object1
--------SubObject
如果我单击子对象(在本例中是一个附加了弹出按钮的按钮),则会出现一个弹出菜单,其中包含以下选项:添加、更新、删除。例如,当我在 Add 上 click/tap 时,我需要我的结束事件处理程序或命令来了解 SubObject(特别是 Content,因为它是一个按钮)和被单击的 MenuFlyoutItem(特别是 Text 属性 所以我知道我是否需要添加、更新或删除)。
您可以在创建 MenuFlyoutItem
的相同代码中添加命令,因此
var addDeviceMenuItem = new MenuFlyoutItem { Name = "AddDevice", Text = "Add Device"
};
addDeviceMenuItem.Command = AddDeviceCommand;
上面给出的快速而正确的答案(i.e.adding 在您创建 MenuFlyoutItem
的相同代码中的命令)导致您的代码违反了 MVVM 模式,因为您创建了视图的ViewModel 中的控件,因此更好的解决方案是
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>
我有一个包含多个图层的 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 工作。
最初我尝试了 <MenuFlyoutItem Name={Binding Name} Text={Binding Text} view:EventHandlers.Attach="Click"/>
这可以通过自定义 MVVM 实现来将事件附加到 ViewModel 处理程序。在幕后,我们的附加机制获取对象的名称并与 Click 相关联,因此如果名称为 SaveButton:public void SaveButton_Click(object sender, RoutedEventArgs e)
,它在视图模型中看起来像这样。现在,我通常从来没有遇到过这个问题,但我认为问题可能源于尝试对 MenuFlyoutItem 的名称使用 DataBinding 而不是传统的 x:Name="blah blah"
;但是,尝试也没有用。我想这可能是因为它是一个 MenuFlyoutItem 而不是一个按钮,所以我尝试将它更改为 <Button/>
以及所有相应的东西,但这也没有用。因此,我返回到 MenuFlyoutItem 并尝试改用命令 属性。即 Command="{Binding MenuItemSelected}"
。然后在我的 ViewModel 中有以下内容:
public RelayCommand<object> MenuItemSelected { get; internal set; }
public TreeViewPageVM()
{
MenuItemSelected = new RelayCommand<object>(TestAction);
}
private void TestAction(object sender)
{
}
那也不管用...所以,尽管我想使用 MVVM,但我还是尝试使用传统的 <MenuFlyoutItem x:Name="MenuItem" Text="{Binding Text} Click="MenuItem_Clicked"
和相应的 private void MenuItem_Clicked(object sender, RoutedEventArgs e)
处理程序来隐藏代码。令我惊讶的是,这也不起作用。因此,我不确定是什么原因导致我的 MenuFlyoutItem 生成事件的能力似乎受到抑制,但我们将不胜感激。
理想情况下,无论是单击事件还是命令事件,我都希望在我的 ViewModel 中处理它,并且我希望命令或单击事件能够获取按钮上的 Content
生成弹出菜单(在 post 处理事件期间我需要它)以及被单击的 MenuFlyoutItem 中的 Text
。
以防万一需要进一步说明: 我会像这样在树中放置对象
----Object1
--------SubObject
如果我单击子对象(在本例中是一个附加了弹出按钮的按钮),则会出现一个弹出菜单,其中包含以下选项:添加、更新、删除。例如,当我在 Add 上 click/tap 时,我需要我的结束事件处理程序或命令来了解 SubObject(特别是 Content,因为它是一个按钮)和被单击的 MenuFlyoutItem(特别是 Text 属性 所以我知道我是否需要添加、更新或删除)。
您可以在创建 MenuFlyoutItem
的相同代码中添加命令,因此
var addDeviceMenuItem = new MenuFlyoutItem { Name = "AddDevice", Text = "Add Device"
};
addDeviceMenuItem.Command = AddDeviceCommand;
上面给出的快速而正确的答案(i.e.adding 在您创建 MenuFlyoutItem
的相同代码中的命令)导致您的代码违反了 MVVM 模式,因为您创建了视图的ViewModel 中的控件,因此更好的解决方案是
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>