I have a really frustrating issue where whenever I call
RefreshAll() from Microsoft.Interop.Excel it does not refresh the data when I open the workbook in excel and I have to manually click the refresh all button within excel... I have even tried calling refresh all via vba and it still does not refresh the data... I am always prompted with:
"The PivotTable report was saved without underlying data. Use the
Refresh Data command to update the report."
Despite the fact that I already called the "Refresh all command."
public void applyMacro(string excelFile)
{
var excelApplication = new Microsoft.Office.Interop.Excel.Application { Visible = false };
var targetExcelFile = excelApplication.Workbooks.Open(excelFile);
try
{
string[] macros = addMacros(ref targetExcelFile);
for (int i = 0; i < 2; i++)
excelApplication.Run(macros[i]);
targetExcelFile.RefreshAll();
targetExcelFile.Save();
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadKey();
}
finally
{
excelApplication.Quit();
}
}
and in vba have tried adding the following before calling the refresh:
For Each ws In Worksheets
For Each qt In ws.QueryTables
qt.Refresh BackGroundQuery:=False
next qt
Next ws
Any ideas??
You can use this on your pivot table :
YourPivotTable.PivotCache().RefreshOnFileOpen = true;
YourPivotTable.SaveData = true;
Related
in the following code I want to have my OpenFileDialog open in a method until a valid file is selected. This works only conditionally. For some reason it adds a column after the message is displayed. This causes correct data tables to also be read incorrectly if I have previously selected an incorrect file.
public static InputData GetCSVData()
{
InputData InputData = new InputData();
OpenFileDialog OFDReader = new OpenFileDialog();
//Filter OpenFileDialog; show only CSV-Files
OFDReader.Filter = "CSV files|*.csv;";
// check if data contains "Date/Time" .
OFDReader.FileOk += delegate (object s, CancelEventArgs ev)
{
//search for Line to start reader
int LineCounter = 0;
var readertmp = new StreamReader(OFDReader.FileName);
while (true)
{
string LineTmp = readertmp.ReadLine();
string record = "Date/Time";
if (LineTmp.Contains(record))
{ break; }
else if (readertmp.EndOfStream)
{
MessageBox.Show("Data has no DataPoints !", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
ev.Cancel = true;
{ break; }
}
LineCounter++;
}
//read InputData
var reader = new StreamReader(OFDReader.FileName);
for (int i = 0; i < LineCounter; i++)
{
reader.ReadLine();
}
// settings CSVHelper
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";", // Set delimiter
};
var csv = new CsvReader(reader, config);
var DataRead = new CsvDataReader(csv);
InputData.DataTable.Load(DataRead);
//check for enough columns
int ColumnCounter = 0;
ColumnCounter = InputData.DataTable.Columns.Count;
if (ColumnCounter <= 2)
{
MessageBox.Show("Data has not enough columns!", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
ev.Cancel = true;
}
};
if (OFDReader.ShowDialog() == DialogResult.OK)
{
InputData.FilePath = OFDReader.FileName;
}
return InputData;
}
}
It appears you are making this more complicated than it has to be. For starters it seems odd (at least to me) that you would bother with the FileOK delegate. I do not see what difference it would make if the user is presented with an OpenFileDialog once, twice or many times. Using a single OpenFileDialog for this just seems like a waste of effort.
If the user selects a file and it fails to meet the necessary requirements, then simply open another OpenFileDialog and let the user try again. Doing this in a single dialog is certainly possible, however, “where” else would you use this? It appears this dialog is “specific” to a “certain” type of file, why limit the dialog to the requirement we need. I would think a simple method that loops forever until the user selects a valid file or Cancels the OpenFileDialog would be an easier approach.
With that said, following your code is a little odd. The reason for your issue is that the code is reading the file into the InputData.DataTable regardless if the file FAILS having datapoints OR enough columns. Put a breakpoint on the line…
InputData.DataTable.Load(DataRead);
You will see that the DataTable is filled with the data even if the data has no “DataPoints.” After the above line of code executes the next few lines check to see if the DataTable has 2 or more columns of data. If there are not enough columns, then the code simply pops up a message box indicating this.
This appears straight forward, however, the InputData.DataTable STILL HAS THE DATA even if it was bad. Next time you call the above Load method, it will simply ADD the new table to the existing table. It will add the columns if needed and simply add the rows to the bottom of the existing DataTable. Try opening several BAD files then eventually open the good file and you will see many added columns and rows.
I will assume that you may be under the impression that when you call…
ev.Cancel = true;
That the code stops right there and goes back to the first line in the delegate…
int LineCounter = 0;
… this would not be true. The code continues after ev.Cancel = true; is executed.
This can be seen by the fact that you are getting extra columns and rows every time a BAD file is attempted to be opened. A simple solution is to simply create a “new” InputData object just before you call the load method. Something like…
InputData = new InputData();
InputData.DataTable.Load(DataRead);
This will fix the extra columns issue, however, IF the user selects a BAD file and the error message pops up and the user clicks the OK button to go back to the open file dialog… THEN… IF the user then clicks the open file dialogs “Cancel” button, the BAD file will still be displayed in the grid. I am confident you may not want this behavior.
Without going into detail about some of the other strange aspects of the posted code. I proffer one other possible simpler solution as described in the beginning. Granted, the code below uses multiple OpenFileDialogs, however the user still cannot escape until they pick a valid file or cancel the dialog.
Much of the code below is taken from the existing posted code however, it is structured differently. Initially some variables are created before we stat an endless loop. Specifically, the CsvConfiguration variable config has some added properties set that ignore some code crashing problems when reading the file. I am confident you will want to set up the CsvReader to handle these problems the way you want them to be handled.
Once inside the endless while loop, the code creates a new InputData object, initializes a new OpenFileDialog and sets its properties. Then the code displays the OpenFileDialog and when the dialog returns, the DialogResult result variable is set to the dialogs returned DialogResult.
If the dialog returns OK then the code checks to see if the file is an “empty” file. If the file is empty, a message box is displayed to inform the user, then we branch back up to the begging of the loop. If the dialog result is Cancel, then the code will return a “new” InputData object. The reason for the empty check is that an exception (No header record was found) will be throw on the line…
DataRead = new CsvDataReader(csv);
If the file is empty.
I am confident that there may be some CsvHelper property that I missed that would prevent this “empty” file exception. If there is some better way to check for this “empty” file or prevent the exception, I am open to suggestions.
If the file is NOT empty, we continue by opening the file and go ahead and read its data as intended using the CsvDataReader. The idea is that… IF the file reads correctly without errors and fits the requirements, then we will already have the InputData.DataTable set and all that is left to do is to set its FilePath property and return the InputData object.
Once we have the InputData.DataTable we can check the number of columns in the InputData.DataTable. If the number of columns is less than two (2), then pop up the error message box to the user and loop back to the begging of the while loop.
If the InputData.DataTable meets the two (2) or more columns requirement, then another check is made by looping through all the columns in the data table. If at least ONE (1) column name is “Date/Time” then we are done checking the requirements and simply set the InputData.FileName property and return the InputData object.
If none of the column names in the InputData.DataTable columns is named ”Date/Time,” then again we pop up the error message box and loop back to the begging of the while loop.
It should be noted that if the file fails the number of columns test or the column named Date/Time test… then as with your problem, the InputData.DataTable STILL HAS THE DATA. This is OK here since we will re-initialize a “new” InputData object when we loop back up to the begging of the while loop.
Lastly, you do not show the InputData Class, however it appears to have at least two (2) properties… 1) a string FilePath and 2) a DataTable named DataTable??? this looks odd and is ambiguous… I have renamed my InputData object’s DataTable property to DT. The same “ambiguity” applies to the InputData variable which I have changed to TempInputData.
Since the code may “potentially” create numerous InputData objects each time the user selects a BAD file, I have implemented the IDisposable interface in the InputData Class. This way we can use this Class in a using statement and properly dispose of the unused InputData objects the code creates. I hope I have implemented this correctly.
public class InputData : IDisposable {
public DataTable DT;
public string FilePath;
private bool isDisposed;
public InputData() {
DT = new DataTable();
FilePath = "";
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (isDisposed) {
return;
}
if (disposing) {
DT?.Dispose();
FilePath = null;
}
isDisposed = true;
}
}
private InputData GetInputDataFromSCV() {
InputData TempInputData;
OpenFileDialog OFDReader;
string initialDirectory = #"D:\Test\CSV";
DialogResult result;
CsvConfiguration config = new CsvConfiguration(CultureInfo.InvariantCulture) {
Delimiter = ";",
IgnoreBlankLines = true,
MissingFieldFound = null,
BadDataFound = null
};
CsvReader csv;
CsvDataReader DataRead;
StreamReader readertmp;
FileInfo fi;
while (true) {
using (TempInputData = new InputData()) {
using (OFDReader = new OpenFileDialog()) {
OFDReader.Filter = "CSV files|*.csv;";
OFDReader.InitialDirectory = initialDirectory;
result = OFDReader.ShowDialog();
if (result == DialogResult.OK) {
fi = new FileInfo(OFDReader.FileName);
if (fi.Length != 0) {
using (readertmp = new StreamReader(OFDReader.FileName)) {
csv = new CsvReader(readertmp, config);
DataRead = new CsvDataReader(csv);
TempInputData.DT.Load(DataRead);
if (TempInputData.DT.Columns.Count > 2) {
foreach (DataColumn column in TempInputData.DT.Columns) {
if (column.ColumnName == "Date/Time") {
TempInputData.FilePath = OFDReader.FileName;
return TempInputData;
}
}
// if we get here we know a column named "Date/Time" was NOT found
MessageBox.Show("Data has no DataPoints !", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else {
MessageBox.Show("Data has less than 2 columns?", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
else {
MessageBox.Show("File is empty!", "Wrong Data", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else {
if (result == DialogResult.Cancel) {
return new InputData();
}
}
}
}
}
}
I hope this makes sense and helps.
Im sorry for the inconvenience. Sometimes I really make it too complicated for myself. I have now solved it as follows:
if (result == DialogResult.Cancel)
{
if (inputDataHistory.Loadcount != 0)
{
TempInputData.FilePath = inputDataHistory.FilePathCache;
TempInputData.LineCounter = inputDataHistory.LinecounterCache;
var reader = new StreamReader(TempInputData.FilePath);
for (int i = 0; i < TempInputData.LineCounter; i++)
{
reader.ReadLine();
}
csv = new CsvReader(reader, config);
DataRead = new CsvDataReader(csv);
TempInputData.DT.Load(DataRead);
TempInputData.IsDisposed = true;
return TempInputData;
}
else
{
return new InputData();
}
I don't know if it is the most efficient solution but I read the key variables into another class before. These are used when canceling to re-read the file before.
I am developing an Excel 2013 VSTO project. I need to execute existing menu items (e.g., File->Export operation).
How can I do it? I've tried to search for examples but didn't find any.
This is the VBA page for the ExportAsFixedFormat method of the Worksheet Object: https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.exportasfixedformat . There are all sorts of functionality to call from Excel Object Model, and that page has more info than the Interop Pages.
You can call the Excel Object Model through your VSTO usually like:
//inside a function call from your VSTO project
Excel.Worksheet ws = Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1];
string exportMessage = await SaveASPDFAsync(ws, #"C:\Test\.test.pdf", false);
//more code - if empty string, it was a good export
A method might look like this:
private async Task<string> SaveASPDFAsync(Microsoft.Office.Interop.Excel.Worksheet ws, string filepath, bool openAfterPublish)
{
return await Task.Run(() =>
{
bool originalDisplayAlerts = ws.Application.DisplayAlerts;
try {
ws.Application.DisplayAlerts = false;
ws.ExportAsFixedFormat(Type: Excel.XlFixedFormatType.xlTypePDF, Filename: filepath, OpenAfterPublish: openAfterPublish);
ws.Application.DisplayAlerts = originalDisplayAlerts;
return "";
}
catch (Exception ex)
{
return ex.ToString();
}
});
}
I am observing a very weird behavior here. I have an excel document (.xlsx) and My goal is to edit all the hyperlinks that meet a certain criteria.
The first time I iterate, I am able to edit most of the links. There are 4 links that still remain. Then I run the program again. Now 2 remain. Then I run the program yet again, and finally all links are replaced.
Here is the snippet:
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(FilePath, true))
{
WorkbookPart wbPart = doc.WorkbookPart;
// Replace hyperlink targets
foreach (var worksheet in wbPart.WorksheetParts)
{
var hyperLinkRelations = worksheet.HyperlinkRelationships;
for (int i = 0; i < hyperLinkRelations.Count(); i++)
{
var link = hyperLinkRelations.ElementAt(i);
try
{
if (link != null && link.Uri != null && Utils.IsToBeReplaced(link.Uri.AbsoluteUri))
{
string relationId = link.Id;
string destUrl = GetReplacementUrl(link.Uri.AbsoluteUri);
if (destUrl != null)
{
worksheet.DeleteReferenceRelationship(link);
worksheet.AddHyperlinkRelationship(new Uri(destUrl, UriKind.Absolute), true, relationId);
}
}
}
catch (Exception ex)
{
Logger.Log(ex);
}
}
}
}
I have checked the utility methods "IsToBeReplaced()" and "GetReplacementUrl()" are functioning normally. I put a break point and discovered that the URLs that don't get replaced, simply do not show up in the "HyperlinkRelationships" collection.
Here is the excel file that I am working with: https://app.box.com/s/j3ulbxfafzxgcqiaep148a8yq4iy37ow
I would guess your indexing is throwing it off.
As a first attempt I would try working backwards.
ie something like:
var hcount = hyperLinkRelations.Count();
for (int i = hcount - 1; i >= 0; i--)
Otherwise, after you delete then add the relationship your i variable updates but the collection has been renumbered / shifted up by one so i will have skipped an element.
I am attempting to export a datagrid to excel via a popup. I am able to successfully export to excel when removing the code from my application, however, when attempting to export to excel using the exact same code within my application, I receive the following errors:
From my try/catch:
Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.
...and from the console:
Error: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed.
My server-side code is as follows:
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
ExportDataSetToExcel();
}
private void ExportDataSetToExcel()
{
try
{
DataTable dt = new DataTable("GridView_Data");
foreach (TableCell cell in gvPatientRoster.HeaderRow.Cells)
{
dt.Columns.Add(cell.Text);
}
foreach (GridViewRow row in gvPatientRoster.Rows)
{
dt.Rows.Add();
for (int i = 0; i < row.Cells.Count; i++)
{
dt.Rows[dt.Rows.Count - 1][i] = row.Cells[i].Text;
}
}
if (dt != null && dt.Rows.Count > 0)
{
Response.ContentType = "application/vnd.ms-excel";
Response.AppendHeader("Content-Disposition",
string.Format("attachment; filename=PatientRoster.xls"));
System.IO.StringWriter tw = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter hw =
new System.Web.UI.HtmlTextWriter(tw);
DataGrid dgGrid = new DataGrid();
dgGrid.DataSource = dt;
//Report Header
hw.WriteLine("<b><u><font size='5'>" +
"Patient Roster</font></u></b>");
hw.WriteLine("<br><br>");
//hw.Write(BuildCriteriaString());
hw.WriteLine("<br>");
// Get the HTML for the control.
dgGrid.HeaderStyle.Font.Bold = true;
dgGrid.DataBind();
dgGrid.RenderControl(hw);
// Write the HTML back to the browser.
this.EnableViewState = false;
Response.Write(tw.ToString());
Response.End();
}
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.Message;
}
}
I added the following to the PageLoad() and the export now works:
ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
scriptManager.RegisterPostBackControl(this.btnExportToExcel);
It looks like the UpdatePanel and ScriptManager were elements that I overlooked. When I separated the functionality from the main application, I did not include all HTML that was in the main application. Since the ScriptManager and UpdatePanel were not part of the isolated functionality, it worked properly. I will be sure to post the HTML next time
This was a solution from the following post:
Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed
I would suggest using one of the many good, free C# "Export to Excel" libraries out there.
You've already written the code to read your DataView into a DataTable, and once it's in this format, using the following free C# library...
ExportToExcel library
...you'd be able to export the data into a "real" Excel 2007 file in one line of code....
// Export the DataTable into an Excel file, and write it to the web Response
CreateExcelFile.CreateExcelDocument(dt, "SomeFilename.xlsx", Response);
Okay, okay, two lines of code. I added a comment.
But you get the point !
After trying out several solutions, I am in desperate need for help.
I tried several approaches, before finally copying and still being stuck with the solution from Getting full contents of a Datagrid using UIAutomation.
Let's talk code, please consider the comments:
// 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;
}
}
I can neither fetch a GridPattern, nor a TablePattern instance from datagrid, both results in an 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!
}
The rows were added to the DataGridView beforehand, like this:
dgvControlProperties.Rows.Add(new object[] { false, "Some Text", "Some other text" });
I am compiling to .Net Framework 4.5. Tried both regular user rights and elevated admin rights for the UI Automation client, both yielded the same results described here.
Why does the DataGridView return 0 rows?
Why can't I get one of the patterns?
Kudos for helping me out!
Update:
James help didn't to the trick for me. The following code tough returns all rows (including the headers):
var rows = dataGrid.FindAll(TreeScope.Children, PropertyCondition.TrueCondition);
Header cells can then be identified by their ControlType of ControlType.Header.
The code that you are copying is flawed. I just tested this scenario with an adaptation of this sample program factoring in your code above, and it works.
The key difference is that the code above is using TreeScope.Children to grab the datagrid element. This option only grabs immediate children of the parent, so if your datagrid is nested it's not going to work. Change it to use TreeScope.Descendants, and it should work as expected.
var datagrid = bw.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "dgvControlProperties"));
Here is a link to how the various Treescope options behave. Also, I don't know how your binding the rows to the grid, but I did it like this in my test scenario and it worked flawlessly.
Hopefully this helps.
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;
Your code looks fine though this could be a focus issue.
Even though you are getting a reference to these automation element objects you should set focus on them (using the aptly named SetFocus method) before using them.
Try:
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));
and if that does not work try explicitly focusing on the dataGrid prior to calling "FindAll" on it i.e.
datagrid.SetFocus()
Why does the DataGridView return 0 rows?
The DataGridViewRows have a ControlType of ControlType.Custom. So I modified the line
// Find all rows from the DataGridView
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));
To
var loginLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
That gets you all the rows within the DataGridView. But your code has that within the loop.
What pattern is supported by DataGridView (not DataGrid)?
LegacyIAccessiblePattern. Try this-
LegacyIAccessiblePattern legacyPattern = null;
try
{
legacyPattern = datagrid.GetCurrentPattern(LegacyIAccessiblePattern.Pattern) as LegacyIAccessiblePattern;
}
catch (InvalidOperationException ex)
{
// It passes!
}
As I commented on #James answer, there is no support for UIA for DataGridView (again, not DataGrid).
If you search in google with terms: "UI Automation DataGridView" the first result has an incomplete answer. Mike replies with what provider class (Obviously one that extends DataGridView)to create within the source, unfortunately I have no clue as to how to make UIA load that class a provider. If anyone can shed some clues, Phil and I will be extremely pleased!
EDIT
Your DataGridView should implement the interfaces in that link above. Once you do that the ControlType of Row will be ControlType.DataItem instead of ControlType.Custom. You can then use it how you'd use a DataGrid.
EDIT
This is what I ended up doing -
Creating a Custom DataGridView like below. Your datagridview could subclass this too. That will make it support ValuePattern and SelectionItemPattern.
public class CommonDataGridView : System.Windows.Forms.DataGridView,
IRawElementProviderFragmentRoot,
IGridProvider,
ISelectionProvider
{.. }
The complete code can be found # this msdn link.
Once I did that, I played a bit with visualUIVerify source. Here is how I can access the gridview's cell and change the value. Also, make note of the commented code. I didn't need that. It allows you to iterate through rows and cells.
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;
}
Hopefully this helps.
I run into this problem too, after researching, i figured out that you can only access data grid view with LegacyIAccessible. However, .NET does not support this. So, here are the steps:
there are 2 versions of UIA: managed version and unmanaged version (native code). In some cases, the unmanaged version can do what the managed version can't.
=> as said here, using tblimp.exe (go along with Windows SDK) to generate a COM wrapper around the unmanaged UIA API, so we can call from C#.
I have done it here
we can use this to access DataGridView now but with very limited data, you can use Inspect.exe to see.
The code is:
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));
}