c# - 在动态创建的菜单 (WPF) 中为 PART_Popup 组件绑定“Placement”属性
问题描述
长话短说。我有一个动态创建的菜单,每个项目的视图模型如下:
public class MenuItemViewModel
{
private readonly ICommand _command;
public MenuItemViewModel(Action<object> action)
{
_command = new CommandViewModel(action);
}
public string Header { get; set; }
public System.Windows.Controls.Primitives.PlacementMode Placement { get; set; }
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get
{
return _command;
}
}
}
整个窗口的数据上下文是另一个具有这些 menuViewModel 项的公共 ObservableCollection 的视图模型”
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
在 propgram 启动后,我添加了这样的新条目:
var currentDevice = (ControlableDeviceViewModel)sender;
var cmds = new ObservableCollection<MenuItemViewModel>();
foreach(var cmd in currentDevice.AvailableCommands)
{
cmds.Add(new MenuItemViewModel(cmd.Execute) { Header = cmd.CommandName,
Placement = System.Windows.Controls.Primitives.PlacementMode.Right});
}
MenuItems[0].MenuItems.Add(
new MenuItemViewModel((delegate {
RaiseAddNewDeviceEvent();
}))
{ Header = "Add new..." });
MenuItems[0].MenuItems.Add(
new MenuItemViewModel(null)
{
Header = currentDevice.Name + "->",
MenuItems = cmds
});
}
正如您在此处看到的,我仅将位置添加到 cmds 项目,因为我希望它们向右弹出。默认行为是弹出到底部。当我在 XAML 中的模板中设置 Placement 时,它显然为所有项目设置了它。但我只想为特定项目设置它。无论我尝试以哪种方式绑定模型中的“属性”放置,它都无法正常工作。它始终具有默认值“底部”
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="{Binding RelativeSource={RelativeSource AncestorType={x:Type MenuItem},AncestorLevel='2'}, Path= Placement }" diag:PresentationTraceSources.TraceLevel="High" VerticalOffset="-1">
下面是负责菜单布局的 xaml 部分:
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}"
Grid.Column="0"
Grid.ColumnSpan="5"
Grid.Row="0"
Foreground="White"
Background="Black"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<DockPanel>
<ContentPresenter x:Name="Icon" ContentSource="Icon" Margin="4,0,6,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<Path x:Name="GlyphPanel" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="7,0,0,0" Visibility="Collapsed" VerticalAlignment="Center"/>
<ContentPresenter x:Name="content" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</DockPanel>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="{Binding RelativeSource={RelativeSource AncestorType={x:Type MenuItem},AncestorLevel='1'}, Path= Placement }" diag:PresentationTraceSources.TraceLevel="High" VerticalOffset="-1">
<Border BorderThickness="2" BorderBrush="Black" Background="Black">
<ScrollViewer x:Name="SubMenuScrollViewer" CanContentScroll="true" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" Margin="-1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextBlock.Foreground" Value="Gray" TargetName="content"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:MenuItemViewModel}" ItemsSource="{Binding Path=MenuItems}">
<TextBlock Text="{Binding Header}"
Padding="3"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
非常感谢帮助我解决这个问题
解决方案
看起来 viewModel 没有实现INotifyPropertyChanged
接口,这就是为什么放置总是默认的。
尝试这个
public class MenuItemViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private readonly ICommand _command;
public MenuItemViewModel(Action<object> action)
{
_command = new CommandViewModel(action);
}
private string header;
public string Header
{
get
{
return header;
}
set
{
header = value;
NotifyPropertyChanged();
}
}
private System.Windows.Controls.Primitives.PlacementMode placement;
public System.Windows.Controls.Primitives.PlacementMode Placement
{
get
{
return placement;
}
set
{
placement = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get
{
return _command;
}
}
}
推荐阅读
- contour - 如何为模型参数的最小二乘法制作等高线图
- flutter - 我该如何解决这个 Viewport.dart 问题?
- c - 基本 C 内存分配
- hazelcast - Hazlecast 驱逐不适用于带有 hazlecast5.0 的 USED_HEAP_SIZE
- c# - 尝试在 Unity 中增加 Cubes 比例,然后在达到一定数量时减少
- python - 无法在 python 中打印名称和链接
- powershell - 执行脚本和点源一样吗?
- j - 如何在 J lang 的交换中使用任意选择器?
- google-fit - 为什么 GoogleFit 不同步步数数据?
- ios - 位置数据访问 XCode13