manipular - datagridview c# example
La automatización de IU no funciona para DataGridView (4)
Después de probar varias soluciones, estoy en una necesidad desesperada de ayuda.
Probé varios enfoques, antes de finalmente copiar y seguir teniendo la solución de obtener el contenido completo de una cuadrícula de datos usando UIAutomation .
Hablemos del código, por favor considere los comentarios:
// 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;
}
}
No puedo buscar un GridPattern
, ni una instancia de TablePattern
desde la datagrid
de datagrid
, ambos resultados en una excepción:
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!
}
Las filas se agregaron a DataGridView
antemano, así:
dgvControlProperties.Rows.Add(new object[] { false, "Some Text", "Some other text" });
Estoy compilando .NET Framework 4.5. Intentó tanto los derechos de usuario habituales como los derechos de administrador elevados para el cliente de automatización de la interfaz de usuario, ambos arrojaron los mismos resultados que se describen aquí.
¿Por qué DataGridView
devuelve 0 filas?
¿Por qué no puedo obtener uno de los patrones?
Felicitaciones por ayudarme!
Actualizar:
James ayudó a hacer el truco para mí. El siguiente código difícil devuelve todas las filas (incluidos los encabezados):
var rows = dataGrid.FindAll(TreeScope.Children, PropertyCondition.TrueCondition);
Las celdas de encabezado luego pueden identificarse por su ControlType
of ControlType.Header
.
Su código se ve bien, aunque esto podría ser un problema de enfoque.
Aunque esté recibiendo una referencia a estos objetos del elemento de automatización, debe establecer el foco en ellos (utilizando el método SetFocus ) antes de usarlos.
Tratar:
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));
y si eso no funciona intente enfocarse explícitamente en dataGrid antes de llamar a "FindAll" en él, es decir,
datagrid.SetFocus()
El código que está copiando es defectuoso. Acabo de probar este escenario con una adaptación de este programa de ejemplo con su código anterior, y funciona.
La diferencia clave es que el código anterior está usando TreeScope.Children
para tomar el elemento de cuadrícula de datos. Esta opción solo capta los elementos secundarios inmediatos del elemento principal, por lo que si su cuadrícula de datos está anidada, no funcionará. TreeScope.Descendants
para usar TreeScope.Descendants
, y debería funcionar como se esperaba.
var datagrid = bw.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));
Aquí hay un enlace a cómo se comportan las distintas opciones de Treescope. Además, no sé cómo unir las filas a la cuadrícula, pero lo hice así en mi escenario de prueba y funcionó a la perfección.
Espero que esto ayude.
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;
¿Por qué DataGridView devuelve 0 filas?
Los DataGridViewRows tienen un ControlType de ControlType.Custom. Entonces modifiqué la línea
// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));
A
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
Eso te proporciona todas las filas dentro de DataGridView. Pero tu código tiene eso dentro del ciclo.
¿Qué patrón es compatible con DataGridView (no DataGrid)?
LegacyAccessiblePattern. Prueba esto-
LegacyIAccessiblePattern legacyPattern = null;
try
{
legacyPattern = datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern) as LegacyIAccessiblePattern;
}
catch (InvalidOperationException ex)
{
// It passes!
}
Como comenté en la respuesta de @James, no hay soporte para UIA para DataGridView (nuevamente, no DataGrid).
Si busca en google con términos: "UI Automation DataGridView", el primer resultado tiene una respuesta incompleta. Mike responde con qué clase de proveedor (Obviamente una que extiende DataGridView) para crear dentro de la fuente, desafortunadamente no tengo ni idea de cómo hacer que UIA cargue esa clase como proveedor. Si alguien puede arrojar algunas pistas, ¡Phil y yo estaremos muy contentos!
EDITAR
Su DataGridView debería implementar las interfaces en ese enlace de arriba. Una vez que lo haga, ControlType of Row será ControlType.DataItem en lugar de ControlType.Custom. Luego puede usarlo como usaría un DataGrid.
EDITAR
Esto es lo que terminé haciendo -
Creando un DataGridView personalizado como a continuación. Su datagridview podría subclasificar esto también. Eso lo hará compatible con ValuePattern y SelectionItemPattern.
public class CommonDataGridView : System.Windows.Forms.DataGridView,
IRawElementProviderFragmentRoot,
IGridProvider,
ISelectionProvider
{.. }
El código completo se puede encontrar en este enlace msdn .
Una vez que hice eso, jugué un poco con la fuente visualUIVerify. Aquí es cómo puedo acceder a la celda de la vista de cuadrícula y cambiar el valor. Además, tome nota del código comentado. No lo necesitaba. Le permite iterar a través de filas y celdas.
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;
}
Espero que esto ayude.
Me encuentro con este problema también, después de investigar, descubrí que solo se puede acceder a la vista de cuadrícula de datos con LegacyIAccessible. Sin embargo, .NET no es compatible con esto. Así que aquí están los pasos
- hay 2 versiones de UIA: versión administrada y versión no administrada (código nativo). en algunos casos, la versión no administrada puede hacer lo que la versión administrada no puede hacer.
=> como se dice aquí, usando tblimp.exe (junto con Windows SDK) para generar un contenedor COM alrededor de la API UIA no administrada, para que podamos llamar desde C # lo he hecho aquí
podemos usar esto para acceder a la vista de cuadrícula de datos ahora pero con datos muy limitados, puede usar Inspect.exe para ver. El código es:
utilizando 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} - Seleccionado", pattern.CurrentName, pattern.CurrentValue)); }