Search code examples
pythonc#python.netflaui

Python.NET: How to access generic member of an interface


I am trying to use a generic member from an interface in FlaUI through Python.NET.

The below C# code works,

// myLegacyTreeItem is a valid AutomationElement
// this works and the default action expands the tree node
myLegacyTreeItem.Patterns.LegacyIAccessible.Pattern.DoDefaultAction();

However, the below corresponding Python.NET code doesn't work,

# my_legacy_tree_item is a valid element
my_legacy_tree_item.Patterns.LegacyIAccessible.Pattern.DoDefaultAction()

I am getting the following error,

AttributeError: 'UIA3FrameworkAutomationElement' object has no attribute 'LegacyIAccessible'

I can see the the LegacyIAccessible member is using generic syntax. But I am not able to figure out how to bind it to the concrete type.

https://github.com/FlaUI/FlaUI/blob/master/src/FlaUI.Core/FrameworkAutomationElementBase.Patterns.cs

using FlaUI.Core.Patterns;

namespace FlaUI.Core
{
    public abstract partial class FrameworkAutomationElementBase : FrameworkAutomationElementBase.IFrameworkPatterns
    {
        private IAutomationPattern<IAnnotationPattern> _annotationPattern;
        private IAutomationPattern<IDockPattern> _dockPattern;
        private IAutomationPattern<IDragPattern> _dragPattern;
        private IAutomationPattern<IDropTargetPattern> _dropTargetPattern;
        private IAutomationPattern<IExpandCollapsePattern> _expandCollapsePattern;
        private IAutomationPattern<IGridItemPattern> _gridItemPattern;
        private IAutomationPattern<IGridPattern> _gridPattern;
        private IAutomationPattern<IInvokePattern> _invokePattern;
        private IAutomationPattern<IItemContainerPattern> _itemContainerPattern;
        private IAutomationPattern<ILegacyIAccessiblePattern> _legacyIAccessiblePattern;
        ...
        public IFrameworkPatterns Patterns => this;

        IAutomationPattern<IAnnotationPattern> IFrameworkPatterns.Annotation => _annotationPattern ?? (_annotationPattern = InitializeAnnotationPattern());
        IAutomationPattern<IDockPattern> IFrameworkPatterns.Dock => _dockPattern ?? (_dockPattern = InitializeDockPattern());
        IAutomationPattern<IDragPattern> IFrameworkPatterns.Drag => _dragPattern ?? (_dragPattern = InitializeDragPattern());
        IAutomationPattern<IDropTargetPattern> IFrameworkPatterns.DropTarget => _dropTargetPattern ?? (_dropTargetPattern = InitializeDropTargetPattern());
        IAutomationPattern<IExpandCollapsePattern> IFrameworkPatterns.ExpandCollapse => _expandCollapsePattern ?? (_expandCollapsePattern = InitializeExpandCollapsePattern());
        IAutomationPattern<IGridItemPattern> IFrameworkPatterns.GridItem => _gridItemPattern ?? (_gridItemPattern = InitializeGridItemPattern());
        IAutomationPattern<IGridPattern> IFrameworkPatterns.Grid => _gridPattern ?? (_gridPattern = InitializeGridPattern());
        IAutomationPattern<IInvokePattern> IFrameworkPatterns.Invoke => _invokePattern ?? (_invokePattern = InitializeInvokePattern());
        IAutomationPattern<IItemContainerPattern> IFrameworkPatterns.ItemContainer => _itemContainerPattern ?? (_itemContainerPattern = InitializeItemContainerPattern());
        IAutomationPattern<ILegacyIAccessiblePattern> IFrameworkPatterns.LegacyIAccessible => _legacyIAccessiblePattern ?? (_legacyIAccessiblePattern = InitializeLegacyIAccessiblePattern());
        ...
    }
}

If I change the call as below, there is no error, but the tree item is not expanded,

((FrameworkAutomationElementBase.IFrameworkPatterns)(my_legacy_tree_item.Patterns)).LegacyIAccessible.Pattern.DoDefaultAction()

Trying to use the below cast throws error,

((FrameworkAutomationElementBase.IFrameworkPatterns[ILegacyIAccessiblePattern])(parent.Patterns)).LegacyIAccessible.Pattern.DoDefaultAction()
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at Python.Runtime.GenericUtil.GenericByName(String ns, String name, Int32 paramCount)
   at Python.Runtime.ClassBase.type_subscript(IntPtr idx)

Solution

  • Answering my own question as it was a silly mistake.

    The below line indeed expands the legacy tree item,

    ((FrameworkAutomationElementBase.IFrameworkPatterns)(my_legacy_tree_item.Patterns)).LegacyIAccessible.Pattern.DoDefaultAction()
    

    However, since in my case, I was also immediately right clicking on the same item, the context menu was getting popped up before the tree item could be expanded. So adding sleep(1) after the above call worked for me.

    Alternatively my_legacy_tree_item.DoubleClick() followed by sleep(1) worked too.