5

我正在尝试使用 UI 自动化实现右键单击上下文菜单。由于 UI 自动化没有原生右键单击模式,我将 ExpandCollapse 提供程序添加到列表视图的 AutomationPeer 类,并将展开和折叠映射到打开和关闭上下文菜单。

我的问题是,有没有更好的方法来调用上下文菜单而不涉及尝试使用私有构造函数实例化一个类?我不能将 SendKeys 与 Shift-F10 一起使用。我想使用 PopupControlService 但它被标记为内部。

我糟糕的解决方法:

public class MyListViewAutomationPeer : ListViewAutomationPeer, IExpandCollapseProvider
{

    public MyListViewAutomationPeer(MyListView owner)
        : base(owner){}

    public override object GetPattern(PatternInterface patternInterface)
    {
        if (patternInterface == PatternInterface.ExpandCollapse)
        {
            return this;
        }
        return base.GetPattern(patternInterface);
    }

    public void Expand()
    {
        MyListView owner = (MyListView)Owner;

        //**********************
        //Ouch!!! What a hack
        //**********************

        //ContextMenuEventArgs is a sealed class, with private constructors
        //Instantiate it anyway ...
        ContextMenuEventArgs cmea = (ContextMenuEventArgs)FormatterServices.GetUninitializedObject(typeof(ContextMenuEventArgs));
        cmea.RoutedEvent = MyListView.ContextMenuOpeningEvent;
        cmea.Source = owner;

        //This will fire any developer code that is bound to the OpenContextMenuEvent
        owner.RaiseEvent(cmea);

        //The context menu didn't open because this is a hack, so force it open
        owner.ContextMenu.Placement = PlacementMode.Center;
        owner.ContextMenu.PlacementTarget = (UIElement)owner;
        owner.ContextMenu.IsOpen = true;

    }
4

2 回答 2

0

我也在为同样的问题而苦苦挣扎。作为一种解决方法,我正在使用 user32.dll 使用 mouse_event 函数,并在获取可点击区域的 X、Y 坐标后执行右键单击。

这不是一个好方法,因为屏幕的 X 、 Y 坐标随屏幕分辨率而变化。

于 2010-02-20T07:25:03.590 回答
0

你所拥有的是一个好的开始,但你应该直接使用 Activator 来创建ContextMenuEventArgs类。(你所拥有的是使用内部使用反射的东西。你应该直接使用它。)

此外,为了完整起见,您应该Handled在实际打开菜单之前检查该属性以与预期行为一致。

我在下面说明了这两件事。

var owner = (MyListView)Owner;

// Construct the ContextMenuEventArgs
var constructorArgs = new object[] { owner, true };

var contextMenuEventArgs = (ContextMenuEventArgs)Activator.CreateInstance(
    typeof(ContextMenuEventArgs),
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    constructorArgs,
    null);

// Configure and use as needed
contextMenuEventArgs.RoutedEvent = FrameworkElement.ContextMenuOpeningEvent;
owner.RaiseEvent(contextMenuEventArgs);

if (contextMenuEventArgs.Handled)
    return;

owner.ContextMenu.Placement = PlacementMode.Center;
owner.ContextMenu.PlacementTarget = (UIElement)owner;
owner.ContextMenu.IsOpen = true;

我更进一步,创建了一个我可以调用的全局函数,它简化了上述对非公共构造函数的调用。这是功能...

public static class PrivateActivator {

    public static T CreateInstance<T>(params object?[] arguments) { 

        var item = Activator.CreateInstance(
            typeof(T),
            BindingFlags.Instance | BindingFlags.NonPublic,
            null,
            arguments,
            null);

        if (item is T typedItem)
            return typedItem;
        else
            throw new Exception("Cannot create type");
    }
}

这里是如何使用它......

var contextMenuEventArgs = PrivateActivator.CreateInstance<ContextMenuEventArgs>(AssociatedObject, true);
于 2021-06-21T09:16:23.263 回答