簡體   English   中英

如何強制 ContextMenu 綁定自定義 WPF/XAML 行為?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM