[英]WPF => Binding ContextMenu with CollectionViewSource
[英]How to force ContextMenu binding custom WPF/XAML behavior?
問題:我們如何在自定義行為中訪問分配給 MenuItem 的綁定命令? ContextMenu 不是可視化樹的一部分,並且不會綁定,直到由於在自定義行為中被抑制而永遠不會發生的單擊事件。
目的我有一個使用 Microsoft.Xaml.Behaviors 的自定義行為,該行為旨在僅在用戶單擊 ListView 中的 object 時顯示上下文菜單。 我想在訪問視圖標記中的引用 ICommand 時使用自定義行為修改命令參數。
代碼:
自定義行為(Microsoft.Xaml.Behaviors):
public class RightClickContextMenuBehavior : Behavior<ListView>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.ContextMenuOpening += AssociatedObject_ContextMenuOpening;
AssociatedObject.PreviewMouseRightButtonDown += AssociatedObject_PreviewMouseRightButtonDown;
AssociatedObject.PreviewMouseRightButtonUp += AssociatedObject_PreviewMouseRightButtonUp;
}
protected override void OnDetached()
{
base.OnDetached();
AssociatedObject.ContextMenuOpening -= AssociatedObject_ContextMenuOpening;
AssociatedObject.PreviewMouseRightButtonDown -=
AssociatedObject_PreviewMouseRightButtonDown;
AssociatedObject.PreviewMouseRightButtonUp -= AssociatedObject_PreviewMouseRightButtonUp;
}
private void AssociatedObject_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
//Store the click locations so that we can determine drag on mouse move.
originPoint = e.GetPosition(null);
//Grab the item from the ListView
var listViewItem = TryFindFromPoint<ListViewItem>((UIElement)sender,
e.GetPosition(AssociatedObject));
if (listViewItem == null)
return;
itemReference = listViewItem;
}
private void AssociatedObject_PreviewMouseRightButtonUp(object sender,
MouseButtonEventArgs e)
{
if (itemReference != null)
{
// Only display the context menu if the user clicked on the object
// inside the listview, not the listview itself.
var targetPosition = e.GetPosition((UIElement)itemReference);
HitTestResult hitResult = VisualTreeHelper.HitTest((UIElement)itemReference,
targetPosition);
if (hitResult != null && sender is ListView)
{
var listView = sender as ListView;
//Set the context menu's visibility flag
listView.ContextMenu.IsOpen = true;
foreach (var item in listView.ContextMenu.Items)
{
if (item is MenuItem)
{
var customMemuItem = item as MenuItem;
//Apply the item the was in our hitbox as context.
customMemuItem.CommandParameter = itemReference.DataContext;
/*
ISSUE:
Bindings here are always null, how do I force the bound
ICommand in the XAML markup to be available here. I'd like
to use the Command binding from the markup, and functionally
apply my DataContext customMemuItem.Command is null
*/
}
}
e.Handled = true; //Handle the bubble
}
}
else
{
if (sender is ListView)
{
//Hide the context menu.
var listView = sender as ListView;
listView.ContextMenu.IsOpen = false;
e.Handled = true;
}
}
}
}
看法:
<ListView Grid.Row="1"
ItemsSource="{Binding Path= ItemQueue, IsAsync=True}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<behaviors:Interaction.Behaviors>
<commandbehaviors:RightClickContextMenuBehavior />
</behaviors:Interaction.Behaviors>
<ListView.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Context Menu Command"
Command="{Binding Source={x:Type models:MyViewModel},
Path=BindingContext.MenuTestCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</ListView.ContextMenu>
[...]
</ListView>
編輯#1:我們正在使用 ListView 所在的 MVVM:
public partial class MyViewModelView : UserControl
{
public DocumentTileManagementView()
{
InitializeComponent();
}
}
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
MenuTestCommand = new ApplicationRelayCommand(MenuTestCommandBehavior);
}
public ObservableCollection<ObjectViewModel> ItemQueue
{
get
{
return _ItemQueue;
}
set
{
this.MutateVerbose(ref _ItemQueue, value, this.RaisePropertyChanged());
}
}
private ObservableCollection<MyDataObject> _ItemQueue= new ObservableCollection<MyDataObject>();
public ICommand MenuTestCommand { get; }
private async void MenuTestCommandBehavior(object obj)
{
}
}
如果不允許 ListView 上的初始右鍵單擊事件鏈,我還沒有找到調用綁定的方法。
我有一個可行的解決方案,我允許第一次右鍵單擊事件並覆蓋每次后續單擊中的行為。 我想調用自定義(Microsoft.Xaml.Behaviors)行為中第一次右鍵單擊期間發生的任何事情。
我沒有嘗試覆蓋整個事件鏈,而是附加到ContextMenuOpening
事件。 在第一個PreviewMouseRightButtonUp
上,我們允許事件鏈完成從標記綁定所有參數和命令。 ContextMenuOpening
事件允許初始命中框配置和CommandParameter
參數分配。
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.ContextMenuOpening += AssociatedObject_ContextMenuOpening;
[...]
}
private void AssociatedObject_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
if (!isFirstClick)
{
isFirstClick = true;
e.Handled = false;
AssociatedObject.ContextMenu.Visibility = Visibility.Collapsed;
return;
}
else
{
[...]
}
}
private void AssociatedObject_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
if (AssociatedObject.Items.Count > 0)
{
var listViewItem = TryFindFromPoint<ListViewItem>((UIElement)e.OriginalSource, new Point(e.CursorLeft, e.CursorTop));
foreach (var item in AssociatedObject.ContextMenu.Items)
{
if (item is MenuItem && listViewItem != null)
{
var customMemuItem = item as MenuItem;
customMemuItem.CommandParameter = listViewItem.DataContext;
}
}
AssociatedObject.ContextMenu.Visibility = Visibility.Visible;
AssociatedObject.ContextMenu.IsOpen = true;
}
else
{
AssociatedObject.ContextMenu.Visibility = Visibility.Collapsed;
AssociatedObject.ContextMenu.IsOpen = false;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.