Download Source - 24 KB
This article describles my experiences and tips in using many of the DevExpress controls to create an effective UI.
Using the Code
The code consists mostly of Setup functions for most of the types of controls mentioned. The project also includes some code to enhance the behavior of the controls where I found they didn't work exactly the way I wanted them to. The code was developed using the Visual Studio 2015 Community edition in C# and Windows Forms and the DevExpress WinForms controls version 15.2.4.
I had a need for traditional controls like text boxes, check boxes, combo boxes, push buttons, date edit controls, numeric edit controls, and menus and toolbars. These are all available, although lacking in features, in the free set of controls in the System.Windows.Forms namespace. These free controls are still what I use for applicaitons with very simple UIs. Anything beyond very simple and I recommend using a 3rd party control suite.
In addition to these simple controls I used the DevExpress GridControl, LookUpEdit control (which is a multi-column combo box), multi-column TreeList control, Dockable Windows, and SpreadsheetControl.
Learning the best way to use the controls can be difficult, especially with all of the different properties available. Below I describe how I used these controls effectively in the applications I built.
There are many skins available with the DevExpress WinForms controls. The ones I like to use are VS2010 and VS2013 Blue. They look good, are not too flashy, and they are very similar to the look of most other Windows controls so that users have some familiarity. The VS2010 skin has a slight 3D effect which looks more like the traditional older style, where the VS2013 Blue is completely flat and looks more modern. In the screenshots in this article, I use VS2013 Blue. The DefaultLookAndFeel control makes it easy to apply a skin to all controls in an application.
One thing that should be mentioned is when you start using DevExpress controls for an application, you probably want to use all DevExpress controls and don't try to mix and match because the DevExpress controls using the same skin have a visual consistency and other controls wouldn't look quite right. The exceptions to this rule are using non-visual or very simple visual controls like Panel or SplitContainer.
Starting with the simple controls, the following Form shows some of the differences between the DevExpress controls and the Windows.Forms counterparts.
Using ButtonEdit for File Name Entry
In the File name entry controls group box at the top, there are 3 examples of controls for file name entry.
The first is the more traditional TextBox with Browse button. The Browse button is used to bring up the standard OpenFileDialog or SaveFileDialog. This can be improved on by using a single DevExpress ButtonEdit control. Users can type file name text or click the ... button to bring up the standard dialogs. This is more compact and easielr to control sizing and anchoring and it looks nicer as well. Another drop down type of button could be added on the end for things like most file/folder entered.
Using LabelEdit and Separator Instead of GroupBox
To group related controls, usually a GroupBox or the DevExpress equivalent GroupControl is used. Although I still use these on occasion, I find that they aren't good in terms of conserving space. Instead I use the DevExpress LabelEdit controls, with a visible line and usually with centered text, and also vertical SeparatorControls. This allows me to section or group controls without wasting alot of space. You can bold the section text to make it stand out more.
NumericUpDown vs. DevExpress SpinEdit
These controls are used for entering numeric values. There are a number of things the SpinEdit does better than the NumericUpDown. First, it does a better job of handling the thousands separator. When typing values into NumericUpDown, I can type "12,3456.78". It doesn't fix my misplaced comma until after I exit the control. It also allows me to enter any number of digits and only changes this to be within the Minimum and Maximum values after exiting the control. I also find the spin buttons are typically useless and just take up space. They are hard to get rid of in a NumericUpDown, but very easy to do so for the SpinEdit bedcause they are part of a Buttons property and you can remove any default buttons that exist.
SpinEdit uses a Mask to determine its format. This allows for much greater flexibility than the NumericUpDown's Minimum, Maximum, and ThousandsSeparator properties. For one thing, the ThousandsSeparator is tied to the current culture of the program. It isn't always convenient to rely on this because sometimes the culture for the OS is setup contrary to a users preferred culture in order to keep some ill-behaved programs from malfunctioning. It's often better to use program configuration settings to control numer formats. Masks give programmers that ability. They can also be used to prevent users from entering to many digits before the decimal place or too many after it and then have that value chopped off, or rounded when they leave the control.
One odd bit of default behavior with the SpinEdit is typing a comma places the carat to the right of the decimal place. So if the user types "123,456" they would actually get the value 123.456. I can't imagine how this behavior makes sense, but thankfully there is an easy solution.
Control Setup Functions
I have a SetupSpinEdit function that sets up properties and events so that they exhibit good behavior. For the SpinEdit, I subscribe to the KeyPresss event and I set e.Handled = true when the thousands separator is pressed. This prevents moving the carat when this key is pressed. I find it good practice to create Setup... functions for all controls except for very simple ones like CheckEdit. This is because sometimes the default behavior of the controls is not what you want and it's hard to remember all of the properties to set to get the behavior you want. It's better to put this code in Setup... functions that you call for each control in FormLoad.
There is ofen a need to specify Null/blank in a control. This is useful in a numeric control to indicate that no value is entered. This is difficult in the NumericUpDown, but very easy in all of the DevExpress controls that I have used. They have an AllowNullInput property and a NullText property, which I tend to leave as blank or set to blank for the LookUpEdit where it defaults to something other than blank.
The DevExpress DateEdit is is similar to the System.Windows.Forms DateTimePicker. The way to allow Null values with DateTimePicker is with an ugly and confusing check box. For DateEdit, users can just delete the date value or keep it blank. Having a Mask property is also great for DateEdit (this is similar to the DateTimePicker's Format = Custom and then setting the CustomFormat string).
Another nice thing about DateEdit is users can select the entire date value to change it or copy and paste where they can only select the day, month, or year component for DateTimePicker. And a time saving feature of DateEdit is hitting the space bar when the entire value is selected or there is no value enters Today's date. As usual, the default behavior is sometimes not exactly what I want. For example, with a Mask of "dd-MMM-yy", I still like to be able to type using numbers for my date parts and / as a separator to advance to the next part. The / does nothing by default, you would have to enter - or space to advance to the next part. I added this additional behavior in my SetupDateEdit funciton so it gets applied to all DateEdit controls that call this Setup function. The DateTimePicker actually lets users use / in its default operation to advance in this scenario, so I feel like it's important to allow the same thing for DateEdit incase a user is used to that functionality.
The DevExpress ComboBoxEdit is similar to the ComboBox with one really nice additional feature. The ComboBoxEdit can allow users to start typing and it immediately shows the drop down list of items and highlights the item that matches what the user was typing. This is a very convenient feature and will save users alot of time vs. using a mouse to bring up the drop down and then scrolling and clicking to select. Again, I have a SetupComboBox function that gets called for every combo box to setup properties in the control to allow this.
This is a control I use alot and there is no equivalent in the free System.Windows.Forms controls. It is essentially a multicolumn combo box. Often there isn't a single value that defines or identifies something. For example, US states are often identified by both a postal code and their full name. It's nice to show both of these because otherwise it might be confusing to users (is AK the abbreviation for Alaska or Arkansas?) or inconvenient (I have to type or select Alabama instead of just typing AL?). My preferred method for dealing with this is to show both code and name as separate columns in the drop down, autosearch by code, and display both in the control as combined text. The columns can go beyond identifiers and show additional information. For example, along with state name and code, you could show timezone of the state, or whatever else might be useful info to users.
One of the nice things about DevExpress controls is the ability to have different types of datasources. Both LookUpEdit and GridControl allow binding to a List of class objects that you create with public properties. The public properties are displayed as columns in the control unless columns exist before setting the control's DataSource. If you use a List with public properties, be aware that any changes you make to the List aren't automatically shown in the grid and you have to call RefreshData (for LookUpEdit call RefreshEditValue). You can use other types of datasources as well, like an ADO.NET DataSet.
There is a bug in LookUpEdit for how I like to have it setup. If you try the topmost LookUpEdit in the sample code form, and give focus to the conrol and place the mouse cursor under the control then type HI for Hawaii, you end up focusing on ID. This is something I contacted DevExpress support about and they provided me with some code as a workaround to this issue. Instead of using LookUpEdit, use the CustomLookUpEdit, which inherits LookUpEdit and also has some code to workaround this issue.
The CheckedComboBoxEdit is a more compact version of a CheckedListBox with nice functionality to select all or clear all. The selected items are shown in a comma separated string in the control. I used this once to replace a button taking the user to a separate screen where they could drag and drop items between list boxes. The CheckedComboBoxEdit worked great as there wasn't much space available on the form.
Other Simple Controls
The ColorPickEdit control allows selecting a color similar to how this works in the Visual Studio Property view. It's noteworthy because there is no free equivalent in System.Windows.Forms, and even though it might not be used very often in business applications, it is very helpful when you do need it.
I used the DevExpress SpreadsheetControl in an application that needed Excel-like functionality but also highly customized. I simply couldn't come close to what I accomplished with this control with other methods. I thought about Excel automation with custom functions and an Add-in that provided a menu and toolbar. This was possible, but the list of problems with this are: an ugly COM Excel interface in C#, the known issues with a shared Excel engine sometimes throwing exceptions because it's busy, needing to do installs on everyones laptop, and it's difficult or impossible to control much of the UI in Excel.
I also tried doing this with a GridControl and an open-source formula parser. I had to write much of the formula logic myself and ended up scrapping this idea because the formulas weren't Excel compatible which made it difficult for the users to write them on their own since they are already familiar with Excel.
Being able to control nearly any part of the SpreadsheetControl with code while providing a great deal of built-in functionality was exactly what I needed.
The old method of using modal dialogs for most of the forms in an application is limiting to users. Users like to be able to look at some information without having to finish out of what they are currently working on. Since Visual Studio started using Dockable Forms in it's UI design, developers have seen how this can increase useability in an application. Dockable Forms improve on MDI modeless windows by managing the location and size of windows automatically so that it is difficult for one window to be completely hidden behind others (a common situation with plain modeless MDI windows).
DevExpress allows Visual Studio like Dockable Forms through a number of its controls. I found this to be very useful for the applications I developed, but there are some extra considerations to just using modal dialogs.
One is that since a tabbed form resizes to fit it's docking area, it's size can increase or shrink without users specifically resizing. This makes it more important to put all controls in an XtraScrollableControl and set the form's MaximumSize property to something reasonable. This way if the tabbed dialog takes up most of the screen you wont get text boxes that take some small amount of text, like a person's first name, and have it stretched all the way across the screen. This control will automatically show scrollbars when all of the controls don't fit. With a non-tabbed form, you would normally just set a MinimumSize so controls don't run into each other and if the user resizes the form too big you have to assume that's what they wanted to do.
I use the DevExpress GridControl alot in my applications, whenever I need to show data in a table of rows and columns. There are many properties and methods that control how a GridControl looks and its functionality.
Depending on the purpose of a specific grid, I like to turn features on and off so that users aren't overwhelmed with features that I don't think they will use. So I have SetupGridView and SetupGridViewColumns functions to setup both the GridView and the columns in the grid.
Dropping a GridControl on a form without changing any of its properties looks like the image below.
The default is to show a group panel at the top, and show Filter icons on the right side of columns when the mouse hovers over. Even though they can be useful, I like to turn both of these off by default.
The Group Panel takes up space and isn't that useful for many of my grids, especially the ones with 10 or less columns and a maximum of a few hundred rows. I also think it's easy for users to get caught up in how grouping works and I like to keep features like this more hidden for experienced users to find and inexperienced users to not be confused by.
Filtering can be useful also, but I like to have the entire column header to click on for sorting. I do keep the filtering on when I know the grids can have alot of rows.
In the grid below, you can see that the columns take up the available horizontal space and don't allow a horizontal scroll bar to appear. This is nice for grids without alot of columns that automatically resize on their resizable dialogs, but I don't like it for tabbed dialogs because the user often doesn't have the option to resize these. For these reasons, my SetupGridView function turns this off by default.
Although I usually will allow users to move columns, I typically don't want them to be able to drag columns off the grid to hide them.
I also turn off the grid's column header right click menu because it has menu items for filtering, grouping, and hiding columns, which are usually turned off in other methods in the grid.
The code for SetupGridView is below. The default option is 0, so any GridOptions enum flag that begins with Prevent means the default is to turn this on, otherwise the default is to turn it off (ex. GridOptions.ShowGroupPanel does not begin with Prevent, so it is turned off by default).
public static void SetupGridView(GridView view, GridOptions gridFlags = 0)
view.OptionsFilter.UseNewCustomFilterDialog = true;
view.OptionsBehavior.AllowIncrementalSearch = (gridFlags & GridOptions.PreventIncrementalSearch) == 0;
view.OptionsNavigation.EnterMoveNextColumn = (gridFlags & GridOptions.PreventEnterMoveNextColumn) == 0;
view.OptionsView.ColumnAutoWidth = (gridFlags & GridOptions.ColumnAutoWidth) != 0;
view.OptionsView.ShowGroupPanel = (gridFlags & GridOptions.ShowGroupPanel) != 0;
view.OptionsCustomization.AllowGroup = (gridFlags & GridOptions.AllowGroup) != 0;
view.OptionsCustomization.AllowQuickHideColumns = (gridFlags & GridOptions.AllowColumnHiding) != 0;
view.OptionsCustomization.AllowColumnMoving = (gridFlags & GridOptions.PreventColumnMoving) == 0;
view.OptionsCustomization.AllowFilter = (gridFlags & GridOptions.AllowFilter) != 0;
view.OptionsCustomization.AllowSort = (gridFlags & GridOptions.PreventSort) == 0;
view.OptionsView.EnableAppearanceEvenRow = (gridFlags & GridOptions.EnableAppearanceEvenRow) != 0;
view.OptionsView.EnableAppearanceOddRow = (gridFlags & GridOptions.EnableAppearanceOddRow) != 0;
view.OptionsMenu.EnableColumnMenu = (gridFlags & GridOptions.EnableColumnMenu) != 0;
if ((gridFlags & GridOptions.ShowFilterPanel) != 0)
view.OptionsView.ShowFilterPanelMode = DevExpress.XtraGrid.Views.Base.ShowFilterPanelMode.Default;
view.OptionsView.ShowFilterPanelMode = DevExpress.XtraGrid.Views.Base.ShowFilterPanelMode.Never;
If you are evaluating control suites and looking for reviews, like I was a few years ago, hopefully this article has been informative and will help you choose the right product. If you already use DevExpress WinForms controls, hopefully this article includes some ideas that will help you with your UI development.
1/23/2016 - Fixed images and download link.