Accessing a grid control with UIAutomation in .NET 5 - wpf

We've got a C# GUI application (WPF) that for various reasons uses .NET Core 3.1. In that application we have a custom grid control that we would like to talk to using UIAutomation.
Our test harness application, a console app using .NET 5.0, does this:
Uses Process.Start() to run our main application.
Creates an instance of CUIAutomation.
Passes the handle of the main application's main window to IUIAutomation.ElementFromHandle() to get an IUIAutomationElement object.
Calls FindAll(TreeScope.TreeScope_Children) recursively to find all the elements. Those elements that have an automation ID are stored in a dictionary.
That all works great. In the test harness, we can load the main application, and then find the element with automation ID "ExitButton" and click on it. Woohoo!
So back in the main application, we have a grid. We assign the grid an automation ID, and when we access that element in the test harness, we can see the grid's OnCreateAutomationPeer() being called. It returns a custom automation peer object (derives from FrameworkElementAutomationPeer, also implements IGridProvider), and our peer object responds to a few basic requests (IsControlElementCore, GetAutomationIdCore) so we know the test harness & peer are talking.
So now the challenge.
How do we get our test harness to do any grid stuff?
From reading online, it seems like we need to call something like
gridElement.GetCurrentPropertyValue(GridPattern.ColumnCountProperty)
but no matter what we try, we cannot find anything named "GridPattern". It doesn't show up in any of the Windows UIAutomation DLLs, nothing in Nuget seems to help.
Where is GridPattern defined? Or are we getting at the grid testing functionality the wrong way?
EDIT #1
By manually editing our test harness' CSPROJ file and adding a reference to Microsoft.WindowsDesktop.App.WPF (as described in this question) the compiler now knows what GridPattern is. However, we're still not sure how to access grid functionality in our test harness.
EDIT #2
The compiler lets us call
GetCurrentPattern(GridPatternIdentifiers.ColumnCountProperty.Id)
in our test harness, but at runtime it throws an exception "Value does not fall within the expected range."
EDIT #3
Calling GetCurrentPropertyValue(GridPatternIdentifiers.ColumnCountProperty.Id) in the test harness works correctly to retrieve the value of the ColumnCount property on the peer object in the main application
EDIT #4
Calling GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) in our test harness doesn't throw an exception, but returns an object of type System.__ComObject which is not obviously useful. Simply casting the __ComObject to a GridPattern throws the expected exception "Cannot convert type 'System.__ComObject' to 'System.Windows.Automation.GridPattern'"
EDIT #5
From looking through the source code, we discovered the GUID of UIAutomationClient.IUIAutomationGridPattern. Passing this to GetCurrentPatternAs(GridPatternIdentifiers.Pattern.Id, gridPatternGUID) works, in that it doesn't fail, but rather returns the IntPtr value 1526502600256.
Only remaining question:
There is a function GridPattern.GetItem() used to retrieve an automation element the represents a specific cell. We still don't know how to get an actual GridPattern object in the test harness.

Finally found the last piece of the magic incantation:
UIAutomationClient.IUIAutomationGridPattern gridPattern = transmitterGrid.Auto.GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) as UIAutomationClient.IUIAutomationGridPattern;
This gives me a gridPattern object that, when called from the test harness, invokes the matching function in the peer in the main application.

Related

Attempt to invoke virtual method 'void android.graphics.Point.setAntiAlias(boolean)' on a null object reference

Within a Codename1 app, I'm getting a CrashReport: java.lang.NullPointException: Attempt to invoke virtual method 'void android.graphics.Point.setAntiAlias(boolean)' on a null object reference. This seems to occur after I invoke a "show()" on the main form of my application.
Note that the code runs fine in the simulator, but consistently gets this error prior to doing the "show()" on Android.
Interestingly, if I put a Dialog like this:
Dialog.show("Wait a sec", "Showing interrupt point", "OK", null);
before the .show(), and then click "OK", then everything runs well with no exceptions at all.
But a sleep(5000) instead of the Dialog does not help - still get the exception. So it at least seems like its not a race condition.
I have try-catches wrapped around all of the potentially offending code, and have NOT been able to isolate this. It always gets caught by the CrashReport, and only when running on the Android device.
Any ideas?
The symptoms to this one are pretty strange (i.e. workaround with a Dialog), and it would be interesting to have an explanation. However, since deprecated cn1 (Map) code has seemingly been implicated, I'm going to let this one go and replace this code with the latest Google native maps code. If it recurs, I will post another question then.

Exception: The Calling thread cannot access this object because a different thread owns it

I'm converting a UI from windows forms to WPF. I'm getting the following exception "The Calling thread cannot access this object because a different thread owns it" whenever I try to call anything on this new WPF window I created.
I referred stack overflow and many websites to find out that I should use Dispatcher.CheckAccess() or somethings similar to dispatcher and check access. I tried many such things
This is one of the things that I have used
Private Delegate Sub ShowSkinInvoked()
If (Dispatcher.CheckAccess()) Then
Me.Show()
Else
Dim d As ShowSkinInvoked = New ShowSkinInvoked(AddressOf ShowSkin)
Dispatcher.Invoke(d)
End If
This has removed the exception and while debugging the error is gone but it freezes the application and I cannot do anything other than terminate it. It doesn't even show the window after "Me.Show".
Also, if I compile the program and then make the calling module use this compiled exe by specifying path to exe then for some reason it works perfect.
If this sounds confusing then what I mean is, I have multiple forms. If I call the code in module A to load and display module B then it gives me the exception but if I call the code in module A to run the compiled exe of module B then it runs perfectly.
Any suggestions?
When WPF creates a user interface it created a thread that is responsible for handling all the user interaction events and scheduling the rendering. This is called the dispatcher thread. Many of the objects that it creates are sub classes of DispatcherObject.
You can't call methods on a DispatcherObject from threads other then the Dispatcher thread that created them. The reasons why are complicated but relate to COM interop.
When you are in a Buttons click event you are running on dispatcher thread.
If you are coming from another thread you must get your work to be performed on the dispatcher thread. It can typically be found by accessing the static current dispatcher Dispatcher.CurrentDispatcher, unless your creating multiple dispatcher threads.
However I would suggest explaining your problem in terms of what work your trying to do with regards to having one form show ui on another. There are multiple ways like an EventAggregator to communicate between ui that might be more appropriate.

Nunit unit test for WPF application gives NullReference exception

While i was writing unit test(NUnit) for wpf application. Got an error 'object reference not set to an instance'
at the bottom level i can see the line which raise exception
Application.Current.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() => loc.ResolveLocalizedValue(out ui)));
this code is used in helper class library.
Thanks in advance. Guys please suggest how can i resolve this.
Application.Current will be null in your case. It returns application object for the current AppDomain which gets set only when application is run.
Test cases are to test business logic i.e. to test ViewModel layer. Code which you want to test should not contain any reference to UI component.
More details can be found here - Application.Current is null in unit test cases. Workaround is provided in the link but I strongly discourage the use of Application.Current in your testcases or usage of it in ViewModel layer which needs to be unit tested.

Resharper truncates unit test messages in the output window and also shows parent exception message

I'm running R# 7.1.2 and trying to test a SL project using the MS unit testing framework. My tests run fine in R#, but the output messages include the parent exception (not just my custom message) and they are truncated:
e.g. instead of
"No user context set"
I get
Test method Some.Quite.Long.Namespace.And.Test_Method_Name threw exception: Microsoft.VisualStudio.T
I wouldn't even mind if the message included the assembly qualified method name...
Test method Some.Quite.Long.Namespace.And.Test_Method_Name threw exception: Microsoft.VisualStudio.Blah.Blah.Blah, No user context set
but instead it's truncated!
Obviously, it's difficult to tell if the unit test failed due to an expected assertion or some other issue (like a config issue)
I can't find anything in the R# options which relates to this...
Does anyone else have this problem or know what the issue might be? I suspect it's something to do with the reference to the unit testing framework as I also can't use the [ClassInitialize] attribute (it complains about wrong type for arg #1 in signature even though the type expected is the correct one!)

"The calling thread must be STA, because many UI components require this." Error in WPF?

I am creating a xps document as below.
Assembly assembly = Assembly.GetExecutingAssembly();
//read embedded xpsDocument file
Stream helpStream = assembly.GetManifestResourceStream(resourceNameOfContext);
if (helpStream != null)
{
Package package = Package.Open(helpStream);
string inMemoryPackageName = "memorystream://" + topicName + ".xps";
Uri packageUri = new Uri(inMemoryPackageName);
//Add package to PackageStore
PackageStore.AddPackage(packageUri, package);
docXps = new XpsDocument(package, CompressionOption.Maximum, inMemoryPackageName);
}
return docXps;
When i am trying to get docXps.GetFixedDocumentSequence();
I am getting the above error. Can anyone help?
Thanks,
Your problem has nothing to do with the code surrounding the creation or use of the XPS document. It has everything to do with what thread you are running under.
You will receive the The calling thread must be STA, because many UI components require this error whenever any of the following are attempted on a MTA thread:
You construct any object derived from FrameworkElement (including Controls and Panels)
You construct any object derived from BitmapEffect
You construct any object derived from TextComposition
You construct any object derived from HwndSource
You access the current InputManager
You access the primary KeyboardDevice, StylusDevice, or TabletDevice
You attempt to change the focus on a FrameworkContentElement
You provide mouse, keyboard or IME input to any control that accepts text input
You make WPF content visible or update its layout
You manipulate the visual tree in such a way as to cause a re-evaluation for rendering
Several other changes, mostly having to do with display and input
For example, I received this error last year when I tried to deserialize some XAML that contained <Button> and other WPF objects from within a WCF service. The problem was simple to solve: I just switch to a STA thread to do the processing.
Obviously most work with XPS documents will trigger one or more of the above conditions. In your case I suspect that GetFixedDocumentSequence ends up using TextComposition or one of its subclasses.
No doubt the my solution of switching to a STA thread will also work for you, but first you need to figure out how your code that works with XpsDocuments is getting executed run from a MTA thread. Normally any code from from the GUI (eg a button press) is automatically run in a STA thread.
Is it possible that your code that manipulates XPS Documents may be being executed without a GUI? From a user-created thread? From a WCF service or a web service? From an ASPX page? Track that down and you'll probably find your solution.
If that doesn't work, let us know the details of the path through which GetFixedDocumentSequence is called, so we can diagnose it. The directly surrounding code isn't nearly as important as the call stack and how it is originally being invoked. If it is hard to explain you probably should add a call stack to prevent misunderstandings and help us diagnose the problem further, or tell you how to start a STA thread in your particular situation.
Is your code trying to access the xps doc from a background thread? If this is the case, you'll want to use the dispatcher. Info on that here.
If this doesn't help, could you post the code where you're actually calling GetFixedDocumentSequence()?

Resources