C# 如何使用UI自动化从ListView或类似控件中获取文本?
我试图从外部应用程序中获取类似ListView的控件。现在我使用的是C# 如何使用UI自动化从ListView或类似控件中获取文本?,c#,vb.net,ui-automation,C#,Vb.net,Ui Automation,我试图从外部应用程序中获取类似ListView的控件。现在我使用的是System.Windows.Automation。使用AutoIt v3,我提取了以下有关要从中提取文本的确切控件的信息: >>>> Control <<<< Class: WindowsForms10.Window.8.app.0.34f5582_r6_ad1 Instance: 20 ClassnameNN: WindowsForms10.Window.8.app
System.Windows.Automation
。使用AutoIt v3,我提取了以下有关要从中提取文本的确切控件的信息:
>>>> Control <<<<
Class: WindowsForms10.Window.8.app.0.34f5582_r6_ad1
Instance: 20
ClassnameNN: WindowsForms10.Window.8.app.0.34f5582_r6_ad120
Name:
Advanced (Class): [CLASS:WindowsForms10.Window.8.app.0.34f5582_r6_ad1; INSTANCE:20]
ID: 1510520
Text:
Position: 182, 164
Size: 1411, 639
ControlClick Coords: 300, 202
Style: 0x56010000
ExStyle: 0x00000000
Handle: 0x0000000000170C78
该控件看起来像ListView或类似的控件,但我无法对其执行任何其他操作
现在如何获取此控件的内容
更新:由于Jimi的推荐,Windows 10 SDK中的inspect.exe工作得最好!我能够深入到DataGridView
我假设您可以找到包含要从中提取数据的DataGridView的窗口。
GeDataGridViewDataTable()
方法需要该窗口的句柄
我们来分解这些方法:
要在已知句柄时获取感兴趣窗口的AutomationElement,我们可以使用Window=
► 这里我使用的是一个,因为您可能有ProcessID和窗口标题,所以不必使用AutomationElement.ControlTypeProperty
和AutomationElement.NativeWindowHandleProperty
进行筛选,您可以使用AutomationElement.ProcessIdProperty
和AutomationElement.NameProperty
作为条件
如果找到该窗口,将解析树作用域.SubTree
作用域中的第一个子元素(该窗口中的所有UI元素)以查找Table()类型的第一个元素
► 当然,该窗口可能承载多个DataGridView:在这种情况下,我们可以使用FindAll()
而不是FindFirst()
,然后使用其他条件(列数、标题文本、单元格内容、位置、大小、父容器等)确定哪个是哪个
找到感兴趣的DataGridView后,我们可以提取其单元格的内容。下面是第二个方法,
GetDataGridViewRowsCollection()
:
- 第一个过滤掉DataGridView滚动条,可能还有网格的其他子控件(定制可能包括一些)
- 之后,我们检查DGV是否有标题:如果有,则第一个子行元素名称为
。然后,我们可以使用标题文本来命名将存储提取数据的DataTable的列。否则,只需添加一些默认名称顶行
- 然后,对于每个行元素,我们枚举其子元素,表示单元格。我添加了一个过滤器以排除行标题单元格,
,如果有ControlType.Header
- 然后,我们迭代单元格的集合,使用方法提取它们的值,将属性类型设置为,将这些值添加到一个列表中,该列表将提供
DataTable.Rows.add()的
数组参数参数
专用数据表GeDataGridViewDataTable(IntPtr windowHwnd)
{
var条件=新的AND条件(
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Window),
新属性条件(AutomationElement.NativeWindowHandleProperty,windowHwnd.ToInt32())
);
var window=AutomationElement.RootElement.FindFirst(TreeScope.Children,条件);
if(window==null)返回null;
var dgv=window.FindFirst(TreeScope.Subtree,
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Table);
如果(dgv==null)返回null;
var dt=GetDataGridViewRowsCollection(dgv);
返回dt;
}
私有数据表GetDataGridViewRowsCollection(AutomationElement dgv)
{
var dt=新数据表();
//跳过滚动条和其他子元素
var条件=新的或条件(
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Custom),
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Header)
);
var rows=dgv.FindAll(TreeScope.Children,condition).OfType().ToList();
bool hasColumnHeader=(行[0]。Current.Name=“顶行”);
//第一个元素是标题(如果有)
var dgvHeaderColumns=行[0]。FindAll(TreeScope.Children,Condition.TrueCondition);
//跳过顶部/左侧标题
对于(int i=1;i
这是一个DataGridView。这些行具有自动化角色或行(0x1C
)。但这些只是容器。您必须获取每行的子元素并检查它们的ControlType属性(第一个元素可能具有标题
角色,其他的编辑
,复选框
等),您可能会发现使用应用程序对确定公开的自动化模式很有用。如果subject控件是DatagridView,它将公开@TnTinMn,谢谢,但是Windows的可访问性洞察没有显示有关应用程序的任何内容。还有其他类似的应用程序可以做同样的事情吗?@Jimi你有什么代码可以让我看看吗?我想我可以。您知道如何获取拥有DataGridView的窗口的句柄吗?我的意思是,在编程上,不使用工具。您使用什么详细信息来查找它(标题、流程ID、特定内容等)。无论如何,我会发布一些标准的UIAutomation(System.Windows.Automation
)给我
AutomationElement element = AutomationElement.FromHandle(1510520);
private DataTable GeDataGridViewDataTable(IntPtr windowHwnd)
{
var condition = new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window),
new PropertyCondition(AutomationElement.NativeWindowHandleProperty, windowHwnd.ToInt32())
);
var window = AutomationElement.RootElement.FindFirst(TreeScope.Children, condition);
if (window == null) return null;
var dgv = window.FindFirst(TreeScope.Subtree,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Table));
if (dgv == null) return null;
var dt = GetDataGridViewRowsCollection(dgv);
return dt;
}
private DataTable GetDataGridViewRowsCollection(AutomationElement dgv)
{
var dt = new DataTable();
// Skips ScrollBars and other child elements
var condition = new OrCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Header)
);
var rows = dgv.FindAll(TreeScope.Children, condition).OfType<AutomationElement>().ToList();
bool hasColumnHeader = (rows[0].Current.Name == "Top Row");
// First element is the Header (if there's one)
var dgvHeaderColumns = rows[0].FindAll(TreeScope.Children, Condition.TrueCondition);
// Skip the Top/Left header
for (int i = 1; i < dgvHeaderColumns.Count; i++) {
dt.Columns.Add(hasColumnHeader ? dgvHeaderColumns[i].Current.Name : "Column"+i);
}
// Skips the Row Header, if any
var notCondition = new NotCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Header));
foreach (AutomationElement row in rows) {
var cells = row.FindAll(TreeScope.Children, notCondition);
var values = new List<object>();
foreach (AutomationElement cell in cells) {
values.Add(cell.GetCurrentPropertyValue(ValuePattern.ValueProperty));
}
dt.Rows.Add(values.ToArray());
}
return dt;
}