I have a WPF app with some winforms integration. Occasionally I will open a legacy winforms Modal Form, lets call it "UfpProductPopupForWpf".
In some circumstances which I haven't been able to identify, the UltraTextEditor seems to create and dispose a whole ton of Windows Forms in the background, for no obvious reason. See the Form disposal callstack below. My theory at this time is that it happens when no other winforms cruft is created in the app. However, if there is a WindowsFormsHost element visible somewhere on the screen already, I don't seem to get all the weird activity (creation and disposal of Windows Forms). The difference in performance is around 30 seconds.
Here is a callstack:
> System.Windows.Forms.dll!System.Windows.Forms.Form.Dispose(bool disposing) Unknown
System.dll!System.ComponentModel.Component.Dispose() Unknown
Infragistics4.Win.v16.1.dll!Infragistics.Win.DrawUtility.DecrementReferenceFormCounter() Unknown
Infragistics4.Win.v16.1.dll!Infragistics.Win.DrawUtility.ReleaseCachedGraphics(System.Drawing.Graphics g) Unknown
Infragistics4.Win.UltraWinEditors.v16.1.dll!Infragistics.Win.UltraWinEditors.TextEditorControlBase.ShouldAutoSizeHeight(out int height) Unknown
Infragistics4.Win.UltraWinEditors.v16.1.dll!Infragistics.Win.UltraWinEditors.TextEditorControlBase.SetBoundsCore(int x, int y, int width, int height, System.Windows.Forms.BoundsSpecified specified) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ScaleControl(System.Drawing.SizeF factor, System.Windows.Forms.BoundsSpecified specified) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ScaleControl(System.Drawing.SizeF includedFactor, System.Drawing.SizeF excludedFactor, System.Windows.Forms.Control requestingControl) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.Scale(System.Drawing.SizeF includedFactor, System.Drawing.SizeF excludedFactor, System.Windows.Forms.Control requestingControl) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ScaleChildControls(System.Drawing.SizeF includedFactor, System.Drawing.SizeF excludedFactor, System.Windows.Forms.Control requestingControl) Unknown
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.Scale(System.Drawing.SizeF includedFactor, System.Drawing.SizeF excludedFactor, System.Windows.Forms.Control requestingControl) Unknown
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.PerformAutoScale(bool includedBounds, bool excludedBounds) Unknown
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout() Unknown
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.OnLayoutResuming(bool performLayout) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ResumeLayout(bool performLayout) Unknown
UFP.LumberTrack.Shared.Windows.dll!UFP.LumberTrack.Shared.Windows.LookupControls.UfpBaseLookup.InitializeComponent() Line 96 C#
Notice the call to Infragistics.Win.DrawUtility.DecrementReferenceFormCounter which does seem to dispose of a form (which form? I do not know). It gets called over and over for every UltraTextEditor.
There is no obvious impact to the behavior of the app other than a massive 30 second delay to open "UfpProductPopupForWpf" in some cases, and instant responses to open that form in others.
Any theories? How can I get this thing to open fast in every case?
OK, here is what works. If I open some random winforms modeless window and put it off to the side, with some random IG controls inside it, then all the performance problems simply go away! From that point onwards, my WPF app can open and close a legacy winforms form (call it "UfpProductPopupForWpf") *without* a massive 30 second delay. But if I neglect to have that random winforms window open, I get the delay.
Taking it one step further I googled Infragistics winforms graphics caching and came up with a static method like so:
Infragistics.Win.DrawUtility.BeginGraphicsCaching();
It turns out that if I run this magical static method, it has the same effect as opening a random winforms window. It makes my legacy winforms stuff open without any massive delays.
So my strategy is now that I'm going to sprinkle this static method around my app like magical pixie dust and hope that it solves all my Infragistics winforms performance problems. its a wonder that Infragistics doesn't just go ahead and do this on my behalf. The overhead of running this is nothing compared to the massive delays that I was seeing before. If anyone has used a better strategy for wpf integration, please let me know.
Thanks, David
Hi David,Thank you for contacting Infragistics Developer Support.Our WinForms framework creates a thread static form, which is used to create graphics objects for the various Infragistics WinForms controls, before their handle is created (as otherwise the graphics object is created from the control itself). These graphics objects have several purposes like drawing and in your case measuring the control size. We cache this form and dispose it, only if all cached graphics are no longer in use and all UltraControlBase-derived objects (i.e. most Infragistics WinForms controls) have their handles destroyed. This is done to ensure that we keep the form as long as we need it, in order to prevent performance issues.However in your case this seems to be not working properly and that the application thinks that every UltraControlBase control has had its handle destroyed. This is obviously not the case since the UltraTextEditor is an UltraControlBase derived control. My assumption is that this is all caused by the environment that the control is used in – in a WPF application, inside a WindowsFormsHost. This code hasn’t been modified since 2009 and it is used extensively throughout our WinForms framework and, to my knowledge, this is the first time such issue is reported. This make it extremely likely that the issue happens, because of the WindowsFormsHost.Are you able to isolate this issue in a separate sample and provide it to us? This way I will be able to see exactly what is going on in your application and see why the form caching doesn’t work as it is supposed to.Note that while your workaround, prevents the performance issue it is possible that it will lead to memory leaks since calling the BeginMethodChaching method may prevent the resources from ever getting collected. So maybe at the end of your application you may still want to call ReleaseCahcedGraphics.I am looking forward to hearing from you.
Attached is the repro that you produced, with some changes.
If you find and uncomment the line that says "SprinkleInfragisticsPixieDust" you will notice, when by pressing ESCAPE for each iteration, that the winforms stuff has a lot less of a performance problem (faster by many 100's of milliseconds).
Let me know if you don't see the same behavior. Again, sorry for the late responses but this legacy winforms stuff continues to be a problem in modern applications. I'm hoping it is still supported.
Hi,
Thank you for the provided sample. I have investigated it and here is what I found.
Because the form that is opened contains our WinForms controls they create and cache a graphics object. Again in some circumstances this may be from a fallback form, because the control handles still haven’t been created. Then, when you close the form, all the Infragistics WinForms controls that you have created are destroyed. At this point we have no way to know if the application would be using our controls anymore, so we are forced to dispose the cached graphics, as otherwise we can create memory leak. This cycle happens each time you open and close a form.
However, when you add the call to BeginGraphicsCaching, it basically has the effect of saying don’t dispose the graphics object until I call the corresponding EndGraphicsCaching method. In that case we don’t dispose the cached graphics after the form is closed, because someone else has cached the graphics and it is now their responsibility to release it. The end result is that the first time the form shows the time is the same as without calling BeginGraphicsCaching. However, after that, since we don’t dispose and recreate anything, the process is much faster.
So because in your application each time all our WinForms controls are created and then destroyed numerous times, it is best to call the BeginGraphicsCaching method in order to notify our framework that you will keep using our controls. Also make sure to have the corresponding EndGraphicsCaching method call in order to dispose the cached graphics once you are done with our controls.
Also while the issue that you have linked looks similar to this one, it isn’t the same. It is related to improvements in our AutoSizing logic. While they are in place, again the main issue here is that our controls are created and destroyed multiple times.
Hopefully my explanation is clear enough. Let me know if you have any additional questions.
So what I'm hearing is that you fully endorse the "pixie dust" work-around (calling Infragistics.Win.DrawUtility.BeginGraphicsCaching();).
Your explanation is somewhat helpful. Every developer has experience with software that runs slower on the first pass and subsequently better afterwards. Two things that are still unclear and unconvincing in your response are :
(1) Why is it my responsibility to optimize your winforms control performances by calling obscure static methods - can't your own stuff do that? Is there an IG component I can add to the form, or a base class that my form can be derived from which will put this responsibility back on you?, and
(2) You vastly under-state the impact of not calling BeginGraphicsCaching(). Your response seems to imply that the *same* negative impact on performance is seen whether I call it manually or whether you call it implicitly. The fact of the matter is, if it is left up to you, your IG controls seem to create and destroy large numbers (dozens/hundreds) of "graphics caching" objects during the initialization of the form.
IE. if I totally eliminate my one-time initializing call to SprinkleInfragisticsPixieDust() and change the loop to call it repeatedly as follows:
for(int i = 0; i < 50; i++)
{
OpenEditRunWindow();
Infragistics.Win.DrawUtility.EndGraphicsCaching();
}
... then this will still run far faster than a loop that omits Begin/End graphics caching. Presumably I'm doing the same thing explicitly that you would be doing implicitly. This really doesn't make sense to me. It is more reasonable to expect that the performance overhead should have been the same for each time the winforms Form is displayed, whether the responsibility for calling BeginGraphicsCaching is left up to me (to be done explicittly) or to you (to be done implicitly). This goes back to question (1), is there something that can be done with this form in order to make your implicit initialization of BeginGraphicsCaching work better than it is now (or at least as well as if I am calling these static methods explicitly).
I'm not a big fan of my current approach (SprinkleInfragisticsPixieDust) and would like some more guidance.
I'll try to ask my question in a more straight-forward way.
Why is it that a run-of-the-mill winforms application doesn't need to manually call the Infragristics BeginGraphicsCaching() method? At what point does this IG method get called in an implicit way from a winforms application, and what is the reason why that it is *not* happening in the example I attached? (Thereby making it my responsibility.)
I apologize for the delay in my response, our offices were closed due to holidays this Monday and Tuesday. This also required comprehensive debugging in order to discover exactly what is going on.
This issue appears in your case due to the combination of two factors:
1. You only use our WinForms controls in select few dialogs and only when they are opened, Infragistics controls are created. Normally, when our WinForms controls are used, they are used across the whole application. Having at least one Infragistics WinForms control (a control with its handle created) has the effect of calling BeginGraphicsCaching (as far as creating and disposing the forms is concerned), since as I have explained in my previous replies, the ThreadStatic fallback form will never be destroyed and we will never need to recreate it.
2. After debugging the provided application further, I found another issue. It has to do with the large number of UltraTextEditor controls (180) the form has and that they all have their AutoSize property set to True. Because of this, when the designer sets a size for the UltraTextEditor, we need to determine the size that the editor needs in order to display its contents. In order to do this we need a graphics object and when we are done with the graphics object, we release it. However, at this point (it’s in the InitializeComponent method) the control doesn’t have a handle yet and we end up creating the fallback form and we get the graphics object from it. Upon releasing the graphics object, there is no way to know if we will need the form for anything else so we end up disposing it. This happens for all the 180 UltraTextEditor controls on the form and causes the slowdown.
So the large number of AutoSize controls and the fact that our controls are simply used in very few places are the reasons for this issue. The caching of objects versus the risk of memory leak is a fine balance and in our case we have decided to make sure that we don’t leak anything. This works for the vast majority of our customers, but in your case it requires the Begin/EndGraphicsCaching workaround.
However, I do believe that we can still improve the behavior of our controls in your specific case. We can probably delay the auto-sizing logic until the control has a handle. I will need to discuss if this is possible with the more senior members of our team, so I have created a private case for you (CAS-176937-F7G4M9 ) and I have logged a development issue in our internal issue tracking system. You can view your currently active cases from the following link:
https://ko.infragistics.com/my-account/support-activity
Thanks for all the work on this. I think I am starting to understand why this is impacting a WPF application that only uses IG/winforms in a limited way (to bring up stand-alone winforms forms).
The "AutoSize" stuff you had mentioned is not something that we did deliberately. A lot of our legacy IG/winforms must have that property set to the default, which is why we see the performance issues.
In a WPF app there are always number of initialization/bootstrapping steps, and I suppose that this "BeginGraphicsCaching" stuff will need to be added in there somewhere as well. However, I wish there would have been an IG blog or something to like that to point the way. It doesn't seem that this issue is as well-known as it should be. On my end, I'll probably end up creating a class that I can introduce in my prism bootstrapping or in the app.xaml resource dictionary.