将上下文菜单绑定到视图模型属性
本文关键字:视图 模型 属性 绑定 上下文 菜单 | 更新日期: 2023-09-27 17:56:26
我有一个上下文菜单,它与数据网格中的按钮相关联。 我希望上下文菜单项根据视图模型中的字符串列表进行更改。当我单击按钮时,什么都没有显示。
这是我正在使用的 xaml,它位于数据网格中:
<Button Grid.Column="1" Content="..." Click="Button_Click">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.SelectableDescriptions}">
<TextBlock Text="{Binding}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
以下是整个 DataGrid xaml:
<DataGrid Grid.Row="1" Grid.ColumnSpan="4" CanUserAddRows="True" AutoGenerateColumns="False" CanUserDeleteRows="True" ItemsSource="{Binding JobPricings, Mode=TwoWay}" SelectedItem="{Binding SelectedJobPricing, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Description" Width="25*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Description,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1" Content="..." Click="Button_Click">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.SelectableDescriptions}">
<TextBlock Text="{Binding}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Unit Price" Binding="{Binding UnitPrice, Mode=TwoWay}" Width="25*"/>
<DataGridTextColumn Header="Unit" Binding="{Binding Unit, Mode=TwoWay}" Width="25*"/>
<DataGridTemplateColumn Header="Currency " Width="25*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox SelectedValue="{Binding CurrencyID, Mode=TwoWay}" SelectedValuePath="ID" DisplayMemberPath="Description" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.Currencies}" ></ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
这是我通过视图模型将上下文菜单绑定到的属性:
public ObservableCollection<string> SelectableDescriptions
{
get
{
_selectableDescriptions.Add("One");
_selectableDescriptions.Add("Two");
return _selectableDescriptions;
}
set
{
_selectableDescriptions = value;
}
}
关于为什么我的列表不会显示在上下文菜单中的任何想法?
正如 John 所说,ContextMenu
是它自己的独立窗口,与按钮的可视化树分开,因此它不会自动继承按钮的DataContext
。
但是,上下文菜单确实有一个指向放置它的对象的链接(在您的情况下是按钮):PlacementTarget
。通过转到菜单的放置目标,您可以找到该按钮,找到该按钮后,您可以找到其DataContext。
因此,在 xaml 中,您可以通过将自己的 DataContext 绑定到自己的PlacementTarget.DataContext
来手动使 ContextMenu 继承按钮的 DataContext,并且所有其他绑定(例如用于ItemsSource
)都可以像正常一样编写:
<Button Grid.Column="1" Content="..." Click="Button_Click">
<Button.ContextMenu>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.DataContext}"
ItemsSource="{Binding Path=SelectableDescriptions}" >
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=SomePropertyOnItem}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Button.ContextMenu>
</Button>
像 ContextMenu 和 Popup 这样的东西存在于主可视化树之外,因此不能使用 RelativeSource 走到父对象。在大多数情况下,元素名称也会中断。根据具体情况,有各种解决方法可用。我喜欢使用继承的附加属性来传递其他数据,因为它不涉及更改 VM,并且在设置后保留在 XAML 中。这是一篇博客文章,通过示例解释了该技术: http://blogs.interknowlogy.com/2011/04/26/binding-to-alternate-datacontexts/
我认为您必须对上下文菜单执行类似操作,请注意,我的假设是设置货币的方式正在起作用。
<ContextMenu>
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
我最终在后面的代码中为上下文菜单设置了 ItemsSource(即使我真的不想使用这种方法:),这有效
这是 xaml
<Button Grid.Column="1" Content="..." Name="ellipseButton" Click="ellipseButton_Click">
<Button.ContextMenu>
<ContextMenu >
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Button.ContextMenu>
</Button>
下面是背后的代码:
private void ellipseButton_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
button.ContextMenu.ItemsSource = JobPricingViewModel.SelectableDescriptions;
if (button != null) button.ContextMenu.IsEnabled = true;
var placementTarget = sender as Button;
if (placementTarget != null) placementTarget.ContextMenu.PlacementTarget = placementTarget;
var button1 = sender as Button;
if (button1 != null)
button1.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
var button2 = sender as Button;
if (button2 != null) button2.ContextMenu.IsOpen = true;
}