3

在尝试了几种解决方案后,我迫切需要帮助。

我尝试了几种方法,最后复制并仍然坚持使用使用 UIAutomation 获取 Datagrid 的完整内容的解决方案。

让我们谈谈代码,请考虑评论:

// Get Process ID for desired window handle
uint processID = 0;
GetWindowThreadProcessId(hwnd, out processID);

var desktop = AutomationElement.RootElement;

// Find AutomationElement for the App's window
var bw = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ProcessIdProperty, (int)processID));

// Find the DataGridView in question
var datagrid = bw.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));

// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));

// Badumm Tzzz: loginLines has 0 items, foreach is therefore not executed once 

foreach (AutomationElement loginLine in loginLines)
{
    var loginLinesDetails = loginLine.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

    for (var i = 0; i < loginLinesDetails.Count; i++)
    {
        var cacheRequest = new CacheRequest 
        { 
            AutomationElementMode = AutomationElementMode.None,
            TreeFilter = Automation.RawViewCondition
        };

        cacheRequest.Add(AutomationElement.NameProperty);
        cacheRequest.Add(AutomationElement.AutomationIdProperty);

        cacheRequest.Push();

        var targetText = loginLinesDetails[i].FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "TextBlock"));

        cacheRequest.Pop();

        var myString = targetText.Cached.Name;
    }

}

我既不能获取 a GridPattern,也不能从中获取TablePattern实例datagrid,两者都会导致异常:

GridPattern gridPattern = null;

try
{
    gridPattern = datagrid.GetCurrentPattern(GridPattern.Pattern) as GridPattern;
}
catch (InvalidOperationException ex)
{
    // It fails!
}

TablePattern tablePattern = null;

try
{
    tablePattern = datagrid.GetCurrentPattern(TablePattern.Pattern) as TablePattern;
}
catch (InvalidOperationException ex)
{
    // It fails!
}

这些行是DataGridView预先添加的,如下所示:

dgvControlProperties.Rows.Add(new object[] { false, "Some Text", "Some other text" });

我正在编译为 .Net Framework 4.5。尝试了 UI 自动化客户端的常规用户权限和提升的管理员权限,两者都产生了此处描述的相同结果。

为什么DataGridView返回 0 行?

为什么我不能得到其中一种模式?

感谢您帮助我!


更新:

詹姆斯的帮助对我来说并没有成功。以下代码很难返回所有行(包括标题):

var rows = dataGrid.FindAll(TreeScope.Children, PropertyCondition.TrueCondition);

然后可以通过它们ControlType的 of来识别标题单元格ControlType.Header

4

4 回答 4

4

您正在复制的代码有缺陷。我刚刚在上面的代码中对这个示例程序进行了改编,并测试了这个场景,它可以工作。

主要区别在于上面的代码TreeScope.Children用于抓取数据网格元素。此选项仅获取父级的直接子级,因此如果您的数据网格是嵌套的,它将无法工作。将其更改为 use TreeScope.Descendants,它应该可以按预期工作。

var datagrid = bw.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));

这是一个链接,指向各种 Treescope 选项的行为方式。另外,我不知道您是如何将行绑定到网格的,但我在测试场景中这样做了,它完美地工作。

希望这会有所帮助。

public class DataObject
{
    public string FieldA { get; set; }
    public string FieldB { get; set; }
    public string FieldC { get; set; }
}

List<DataObject> items = new List<DataObject>();
items.Add(new DataObject() {FieldA="foobar",FieldB="foobar",FieldC="foobar"});
items.Add(new DataObject() { FieldA = "foobar", FieldB = "foobar", FieldC = "foobar" });
items.Add(new DataObject() { FieldA = "foobar", FieldB = "foobar", FieldC = "foobar" });

dg.ItemsSource = items;
于 2013-10-26T00:08:26.010 回答
3

您的代码看起来不错,尽管这可能是一个焦点问题。

即使您正在获得对这些自动化元素对象的引用,您也应该在使用它们之前将焦点设置在它们上(使用恰当命名的SetFocus方法)。

尝试:

var desktop = AutomationElement.RootElement;

desktop.SetFocus();

// Find AutomationElement for the App's window
var bw = desktop.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ProcessIdProperty, (int)processID));

如果这不起作用,请尝试在调用“FindAll”之前明确关注dataGrid,即

datagrid.SetFocus()
于 2013-10-24T22:19:27.143 回答
1

我也遇到了这个问题,经过研究,我发现您只能使用LegacyIAccessible. 但是,.NET 不支持这一点。所以,这里是步骤:

  1. UIA 有 2 个版本:托管版本和非托管版本(本机代码)。在某些情况下,非托管版本可以做托管版本不能做的事情。

=> 如此处所述,使用tblimp.exe(与 Windows SDK 一起使用)围绕非托管 UIA API 生成 COM 包装器,因此我们可以从 C# 调用。
我在这里做了

  1. 我们现在可以使用它来访问 DataGridView 但数据非常有限,您可以使用它Inspect.exe来查看。

代码是:

using InteropUIA = interop.UIAutomationCore;

if (senderElement.Current.ControlType.Equals(ControlType.Custom))
{
    var automation = new InteropUIA.CUIAutomation();
    var element = automation.GetFocusedElement();
    var pattern = (InteropUIA.IUIAutomationLegacyIAccessiblePattern)element.GetCurrentPattern(10018);
    Logger.Info(string.Format("{0}: {1} - Selected", pattern.CurrentName, pattern.CurrentValue));
}
于 2013-11-27T03:03:44.080 回答
1

为什么 DataGridView 返回 0 行?

DataGridViewRows 的 ControlType 为 ControlType.Custom。所以我修改了这条线

// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));

var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

这将为您提供 DataGridView 中的所有行。但是您的代码在循环中包含它。

DataGridView(不是 DataGrid)支持什么模式?

LegacyIAccessiblePattern。尝试这个-

 LegacyIAccessiblePattern legacyPattern = null;

       try
       {
           legacyPattern = datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern) as LegacyIAccessiblePattern;
       }
       catch (InvalidOperationException ex)
       {
           // It passes!
       }

正如我对@James 的回答所评论的那样,DataGridView 不支持 UIA(同样,不是 DataGrid)。

如果您在 google 中使用以下术语进行搜索:“UI Automation DataGridView”,则第一个结果的答案不完整。Mike 回复了要在源代码中创建的提供程序类(显然是扩展 DataGridView 的一个),不幸的是,我不知道如何使 UIA 将该类加载为提供程序。如果有人能提供一些线索,菲尔和我会非常高兴!

编辑

您的 DataGridView 应该实现上面那个链接中的接口。一旦你这样做了,Row 的 ControlType 将是 ControlType.DataItem 而不是 ControlType.Custom。然后,您可以像使用 DataGrid 一样使用它。

编辑

这就是我最终做的 -

创建如下所示的自定义 DataGridView。您的 datagridview 也可以将其子类化。这将使它支持 ValuePattern 和 SelectionItemPattern。

public class CommonDataGridView : System.Windows.Forms.DataGridView,
    IRawElementProviderFragmentRoot,
    IGridProvider,
    ISelectionProvider
{.. }

完整的代码可以在@this msdn 链接中找到。

一旦我这样做了,我就玩了一下 visualUIVerify 源代码。这是我如何访问 gridview 的单元格并更改值的方法。另外,请记下注释代码。我不需要那个。它允许您遍历行和单元格。

private void _automationElementTree_SelectedNodeChanged(object sender, EventArgs e)
    {
        //selected currentTestTypeRootNode has been changed so notify change to AutomationTests Control
        AutomationElementTreeNode selectedNode = _automationElementTree.SelectedNode;
        AutomationElement selectedElement = null;

        if (selectedNode != null)
        {
            selectedElement = selectedNode.AutomationElement;

            if (selectedElement.Current.ClassName.Equals("AutomatedDataGrid.CommonDataGridViewCell"))
            {
                if(selectedElement.Current.Name.Equals("Tej")) //Current Value
                {

                    var valuePattern = selectedElement.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;

                    valuePattern.SetValue("Jet");
                }



                //Useful ways to get patterns and values

                //System.Windows.Automation.SelectionItemPattern pattern = selectedElement.GetCurrentPattern(System.Windows.Automation.SelectionItemPattern.Pattern) as System.Windows.Automation.SelectionItemPattern;


                //var row = pattern.Current.SelectionContainer.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewRow", PropertyConditionFlags.IgnoreCase));

                //var cells = row.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewCell", PropertyConditionFlags.IgnoreCase));
                //foreach (AutomationElement cell in cells)
                //{
                //    Console.WriteLine("**** Printing Cell Value **** " + cell.Current.Name);

                //    if(cell.Current.Name.Equals("Tej")) //current name
                //    {

                //        var valuePattern = cell.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;

                //        valuePattern.SetValue("Suraj");
                //    }
                //}

                //Select Row
                //pattern.Select();

                //Get All Rows
                //var arrayOfRows = pattern.Current.SelectionContainer.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewRow", PropertyConditionFlags.IgnoreCase));


                //get cells
                //foreach (AutomationElement row in arrayOfRows)
                //{
                //   var cell = row.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "AutomatedDataGrid.CommonDataGridViewCell", PropertyConditionFlags.IgnoreCase));


                //    var gridItemPattern = cell.GetCurrentPattern(GridItemPattern.Pattern) as GridItemPattern;

                //    // Row number.
                //    Console.WriteLine("**** Printing Row Number **** " + gridItemPattern.Current.Row);

                //    //Cell Automation ID
                //    Console.WriteLine("**** Printing Cell AutomationID **** " + cell.Current.AutomationId);

                //    //Cell Class Name
                //    Console.WriteLine("**** Printing Cell ClassName **** " + cell.Current.ClassName);

                //    //Cell Name
                //    Console.WriteLine("**** Printing Cell AutomationID **** " + cell.Current.Name);                        
                //}

            }
        }

        _automationTests.SelectedElement = selectedElement;
        _automationElementPropertyGrid.AutomationElement = selectedElement;
    }

希望这会有所帮助。

于 2013-11-05T01:22:44.147 回答