I have created a subclass for ValueListItemUIElement to handle some rare exceptions that occur in a mixed WinForms/WPF environment (I have an override for InitAppearance that calls the base class and catches exceptions that occur in this call chain). This all works pretty well so far.
However, this seems to have broken UI automation and this is probably related to the way how I create and initialize the new instances. I used a CreationFilter (IUIElementCreationFilter) and in there I have the following section:
public void AfterCreateChildElements(UIElement parent) { switch (parent) { ... case ValueListItemContainerUIElement container: { // replace standard ValueListItemUIElement with our custom class that catches // null reference exceptions var newChildElements = parent.ChildElements.Select( c => { if (c is ValueListItemUIElement valueListItemUIElement) { var newValueListItemUIElement = new SafeValueListItemUIElement( container, valueListItemUIElement.ValueListItem, valueListItemUIElement.IsMruItem, valueListItemUIElement.ListIndex) { Rect = valueListItemUIElement.Rect }; return newValueListItemUIElement; } return null; }) .Where(i => i != null) .ToList(); parent.ChildElements.Clear(); parent.ChildElements.AddRange(newChildElements); parent.DirtyChildElements(true); break; }
I assume something is missing in my initialization of the new instances. In the debugger I noticed that the AccessibilityInstance of the new entities has an empty (0,0,0,0) BoundingRectangle and Bounds property.
Does anyone have any clue what might be missing here?
I took a look at the ResolveValueListItemAppearance method and it has not changed a lot since 11.2. There are a number of places in this code where it might be conceivable that there would be a NullReferenceException. We could probably tighten it up a bit, but of course that would only happen in the latest release, so that wouldn't help you in v11.2. Sounds like maybe you are responding to the selection of a ValueList item and doing something (showing a dialog) that is forcing the ValueList to close while it's in the middle of painting or something like that. That's a pretty common scenario for exception like this to occur.But if that was the issue, then is seems like replacing the UIElement ONCE and re-using it should be a good solution and there's really no reason for you to keep replacing it every time like the sample here was doing. This will actually be more efficient, anyway.
At the time the original issue was reported I did not have a creation filter for the ValueListItemUIElements. I introduced this to allow me to inject a custom class that was catching this exception:
System.NullReferenceException: Object reference not set to an instance of an object. at Infragistics.Win.ValueListDropDownAppearanceManager.ResolveValueListItemAppearance(ValueListItemUIElement valueListItemElement, AppearanceData& appData, AppearancePropFlags requestedProps) at Infragistics.Win.ValueListItemUIElement.InitAppearance(AppearanceData& appearance, AppearancePropFlags& requestedProps) at Infragistics.Win.ValueListItemUIElement.PositionChildElements() at Infragistics.Win.UIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UIElement.VerifyChildElements(Boolean recursive) at Infragistics.Win.ControlUIElementBase.VerifyIfElementsChanged(Boolean verify, Boolean syncMouseEntered) at Infragistics.Win.ControlUIElementBase.ProcessMouseHover(Object sender, EventArgs e) at Infragistics.Win.Utilities.ProcessEvent(Control control, ProcessEvent eventToProcess, EventArgs e) at Infragistics.Win.UltraControlBase.OnMouseHover(EventArgs e) at System.Windows.Forms.Control.WndProc(Message& m) at Infragistics.Win.ValueListDropDownUnsafe.WndProc(Message& message) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
I saw a video recorded by the submitter of the issue and the issue occurred after the following sequence of events:
While this sounds like a series of reproducable steps it was never reproducable on any machine that I was using. So there must be an additional condition (or probably a whole set) that is necessary to reproduce this issue. Since you guys (and I understand this) are always asking for reproducible samples I didn't even dare to submit this issue here.
Uwe Nassal said:I checked with 11.2 (had to uncomment the UIAutomationForCodedUITestingEnabled toggle because that does not exist in this version) - and it also works - great!
Yes, 11.2 was before we implemented UIA, so that property (which turns OFF UIA) does not exist in the older version. I needed that so I could reproduce the issue in the latest version.
I'd be interested to know what the original issue was. I know there's something going on here where you are hiding/removing the UIElements for ValueItems whose DataValue is null. Not sure why you don't just remove those items from the list instead, rather than removing them from the display via a CreationFilter. Or maybe use UltraDropDown instead of a ValueList and actually hide those rows.
Puuh ... pretty complex sequence of events. I checked with 11.2 (had to uncomment the UIAutomationForCodedUITestingEnabled toggle because that does not exist in this version) - and it also works - great!
I currently do not think that this will re-introduce my WPF interaction problem. I basically solved this (kind of) by catching the NullReferenceException in the following overload:
protected override void InitAppearance(ref AppearanceData appearance, ref AppearancePropFlags requestedProps) { try { base.InitAppearance(ref appearance, ref requestedProps); } catch (NullReferenceException e) { TraceUI.Log.Error($"NullReferenceException in InitAppearance of {nameof(SafeValueListItemUIElement)}: {e}"); } }
I think this workaround should continue to work - I just avoid unnecessary replacement of UIElements using your suggestion. Unfortunately this issue cannot be reproduced easily - only one of our testers had a pretty good hit rate.
I will integrate your solution and ask him to try to reproduce.
Thanks a lot for your help in this matter!
Okay. Pretty sure I figured it out. The fact that it is actually showing the correct rectangle, but not showing the highlight is a clue. It's not really that the AccessibleObject is returning any incorrect information. But rather what's happening here is that they are getting stuck in a loop.
The sequence of events goes something like this...
- You mouse over an ValueListItem in the ValueList.
- The ValueList highlights/hottracks the item. The item background turns blue.
- A couple of things happen at this point. One is that we invalidate the UIElements for ValueListDropDown and it's UIElements so that it repaints. Another is that we send a Focus notification for Accessibility.
- This ends up triggering the CreationFilter again, at which point, you are removing the UIElement that mouse was over. Which ends up triggering MouseLeave, which dirties the child elements and ends up triggering the CreationFilter again, which creates a new UIElement which then triggers a MouseEnter and new Focus notification and on and on forever.
So I was able to fix this by re-using the same ValueListItemUIElements whenever possible. I had to create a new derived class in order to do this, so that it only replaces them the first time.
I have attached an updated version of the sample here that re-uses the UIElements so you can see what I did and test to see if it works. 0312.UltraGrid_ValueListItem_CreationFilter.zip
Of course, this solution might not be viable in your case. It's entirely possible and even likely, that the reason you are using the CreationFilter in the first place is that it was triggering MouseEnter and MouseLeave and working around whatever other issue you are having with the WPF components. So in that case, we have come full circle and it might be better to explore other solutions for THAT issue.