KB Article T972647
Visible to All Users

Best Practices - Avoid memory and resource leaks in WinForms Application

Other DevExpress WinForms Cheat Sheets


This article explains how to avoid memory and resource leaks. Among many reasons that cause memory leaks, the most common is not calling the Dispose method to destroy objects that are no longer needed. The list below explains how to avoid this and other common issues:

  • Classes that implement the IDisposable interface (Stream, Graphics, File, etc.) typically use unmanaged resources. To release these resources, call the Dispose method before you release your last reference to an object of such a class. If the object is declared within a method, wrap its declaration in the "using" statement - the Dispose method will be called automatically when the object is not needed.

  • Unsubscribe from events when a handler is no longer required. The publisher of the event holds a reference to the subscriber via the event handler delegate when an event handler is subscribed. If the publisher lifetime is longer than the subscriber lifetime, the publisher will keep the subscriber alive even if there are no references to it. So, you need to unsubscribe an event handler from the event to remove a reference and avoid a possible leak. However, if the publisher and subscriber have equal lifetimes, you do not need to worry about unsubscribing from an event.
    Refer to the following articles to learn more about event handling:
    How to subscribe to and unsubscribe from events (C# Programming Guide)
    Events (Visual Basic)

  • Dispose of closed modal forms that are no longer needed. Unlike non-modal forms that are destroyed when users click the Close button, modal forms become hidden but still exist. This allows you to restore hidden modal forms without making new instances of them. If that is not needed, call the Dispose method for hidden modal forms.
    Refer to the MSDN article for more details:
    Form.ShowDialog Method

  • Be aware of static and long-live objects' lifetime. Static objects are GC Roots and are never collected by the garbage collector. Thus, an object itself and everything it references will never be collected. Objects rooted at the level of a long-live object also may be not garbage collected for a long time (their lifetime is equal to the lifetime of this long-live object). For example, a variable at the main form level will not be collected until an application is closed.


Best Practices

Change the control/element font

Font is the IDisposable object that should be disposed of before you release your last reference to it. When our components are used, most often it is required to change the font in an event handler that is raised many times (for example, GridView.RowCellStyle). Therefore, creating a new font each time an event is raised leads to a memory leak.
However, our AppearanceObject has the FontStyleDelta and FontSizeDelta properties for modifying the font size and style. These properties will allow you to modify an existing font instead of creating a new one, so you won't have to worry about disposing resources in this case:

C#
private void GridView_RowCellStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowCellStyleEventArgs e) { if (e.RowHandle % 2 == 0) { e.Appearance.FontStyleDelta = FontStyle.Italic; e.Appearance.FontSizeDelta = 5; } }

Correct creation of a new font in specific cases

For some specific cases, the FontStyleDelta and FontSizeDelta properties are not enough and a new font should be set (for example, you need to change FontFamily). In this case, do not create a font multiple times (when you set it in a method that can be called multiple times, it might happen unintentionally). Create a font once and use it in the required methods:

C#
public Form1() { InitializeComponent(); myFont = new Font("Arial", 15); ... } ... Font myFont; private void GridView_RowCellStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowCellStyleEventArgs e) { if (e.RowHandle % 2 == 0) e.Appearance.Font = myFont; }

Creation of Drawing objects

Most often, when working with our components, you need to create Pens and Brushes to custom draw a control's elements. In this case, obtain a Drawing object from AppearanceObject by using the following methods:
GetBackBrush(GraphicsCache)
GetForeBrush(GraphicsCache)
GetBackPen(GraphicsCache)
GetForePen(GraphicsCache)
With this approach, you don't have to worry about disposing of resources, and you will also get consistent colors in custom painting, since these methods return Pens and Brushes of the current control appearance settings:

C#
private void GridView_CustomDrawGroupPanel(object sender, DevExpress.XtraGrid.Views.Base.CustomDrawEventArgs e) { e.Cache.FillRectangle(e.Appearance.GetBackBrush(e.Cache), e.Bounds); ... }

Creation of Drawing/Graphics objects other than those contained in AppearanceObject or outside of the painting methods (for example, how to correctly create GraphicsCache to calculate text size)

In this case, you need to create such an object by using the 'using' statement:

C#
using (GraphicsCache cache = grid.CreateGraphicsCache()) { view.Appearance.Row.CalcTextSizeInt(cache, "text", 75); }

Correct subscription to an event in a method that can be called many times

In this case, you need to unsubscribe from the event before calling the method again. For example, you subscribe to a popup form event in PopupBaseEdit.Popup that is raised after the editor's popup window has been opened. So, you need to unsubscribe from a popup form event when a popup is closed:

C#
private void SearchLookUpEdit1_Popup(object sender, EventArgs e) { var edit = sender as SearchLookUpEdit; var popupForm = edit.GetPopupEditForm(); popupForm.KeyUp += PopupForm_KeyUp; } private void SearchLookUpEdit1_Closed(object sender, DevExpress.XtraEditors.Controls.ClosedEventArgs e) { var edit = sender as SearchLookUpEdit; var popupForm = edit.GetPopupEditForm(); popupForm.KeyUp -= PopupForm_KeyUp; }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.