UI自动化不适用于DataGridView

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

我尝试了几种方法,然后才最终复制并仍然坚持使用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; } } 

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

 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); 

然后可以通过ControlTypeControlType.Header来标识标题单元格。

您正在复制的代码存在缺陷。 我刚刚测试了这个场景,并在上面的代码中对这个示例程序进行了调整,并且它有效。

关键的区别在于上面的代码使用TreeScope.Children来获取datagrid元素。 此选项仅捕获父项的直接子项,因此如果您的数据网格嵌套,则它不起作用。 将其更改为使用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 items = new List(); 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; 

您的代码看起来很好,但这可能是一个焦点问题。

即使您获得了对这些自动化元素对象的引用,您也应该在使用它们之前将注意力集中在它们上(使用恰当命名的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() 

为什么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回答的评论,没有支持UIA的DataGridView(再次,不是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 {.. } 

完整的代码可以在这个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; } 

希望这会有所帮助。

我也遇到了这个问题,经过研究,我发现你只能使用LegacyIAccessible访问数据网格视图。 但是,.NET不支持这一点。 所以这是步骤

  1. UIA有2个版本:托管版本和非托管版本(本机代码)。 在某些情况下,非托管版本可以执行托管版本无法执行的操作。

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

  1. 我们现在可以使用它来访问数据网格视图,但是数据非常有限,您可以使用Inspect.exe查看。 代码是:

    使用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)); }