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);
然后可以通过ControlType
的ControlType.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不支持这一点。 所以这是步骤
- UIA有2个版本:托管版本和非托管版本(本机代码)。 在某些情况下,非托管版本可以执行托管版本无法执行的操作。
=>如此处所述,使用tblimp.exe(与Windows SDK一起)生成围绕非托管UIA API的COM包装器,因此我们可以从C#调用我在这里完成了
-
我们现在可以使用它来访问数据网格视图,但是数据非常有限,您可以使用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)); }