How to: Implement a Custom List Editor (WinForms)
- 6 minutes to read
XAF includes several built-in List Editors. You can also create a custom List Editor to display object collections in a specific way. This topic shows how to create a WinCustomListEditor that displays objects as image thumbnails (for example, DVD covers).
![]()
Note
The FeatureCenter demo includes the complete code from this example. The demo is located in the %PUBLIC%\Documents\DevExpress Demos 26.1\Components\XAF\FeatureCenter.NET.XPO folder.
Define an Interface for Displayed Objects
Instead of designing the List Editor for a specific class, define an interface with the required properties. Any class that implements this interface can then use the editor. TheWinCustomListEditor displays Image and Text columns. The ID property serves as a unique object identifier.
public interface IPictureItem {
string ID { get; }
byte[] Image { get; }
string Text { get; }
}
Implement the List Editor
Inherit your List Editor class from ListEditor and override the following members. Note that your editor should be public.
Basic Functionality
| Member | Description |
|---|---|
CreateControlsCore |
Instantiates the List Editor’s control. Override the method to create and configure the control. |
AssignDataSourceToControl |
Assigns the data source to the control. Override the method to subscribe to the IBindingList.ListChanged event to handle data change notifications. |
| Refresh() | Override the method to reload all objects from the data source into the control. |
| Dispose() | Override the method to dispose of the manually allocated controlDataSource field. |
| ListEditorAttribute | Decorate the WinCustomListEditor class with this attribute to specify that this editor should be used to display IPictureItem objects. |
Selection Support
To allow users to select items in the List Editor:
- In
CreateControlsCore, subscribe to the control’sSelectedIndexChangedandItemSelectionChangedevents. CallOnSelectionChangedandOnFocusedObjectChangedin the event handlers. - Override the SelectionType property to return SelectionType.Full because the
ListViewcontrol supports both single and multiple selections. - Override the GetSelectedObjects() method to return a list of selected objects.
Detail View Invocation
To open a Detail View when a user double-clicks an object or presses Enter:
- In the
CreateControlsCoremethod, subscribe to the control’sMouseDoubleClickandKeyDownevents. CallOnProcessSelectedItemin both handlers. - Override the FocusedObject property to get and set the focused object (use the helper
FindByTagmethod to locate items by their tag). - In the Refresh() method, preserve focus after reloading data.
Navigation Support
To support navigation (Previous/Next Object Actions) in non-client modes, implement the IControlOrderProvider interface.
Model Persistence
If you need to store editor settings in the Application Model, implement the SaveModel() method. Otherwise, leave the method empty.
Complete Code
The following code snippet implements all the steps described above.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using DevExpress.Drawing;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Utils;
using DevExpress.ExpressApp.Win.SystemModule;
using DevExpress.Utils.Menu;
using DevExpress.XtraBars;
namespace FeatureCenter.Module.Win.ListEditors {
[ListEditor(typeof(FeatureCenter.Module.ListEditors.IPictureItem))]
public class WinCustomListEditor : ListEditor, IRequireContextMenu, IRequireDXMenuManager, IControlOrderProvider {
private System.Windows.Forms.ListView control;
private System.Windows.Forms.ImageList images;
private Object controlDataSource;
private void dataSource_ListChanged(object sender, ListChangedEventArgs e) {
Refresh();
}
private void control_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) {
if(e.Button == System.Windows.Forms.MouseButtons.Left) {
OnProcessSelectedItem();
}
}
private void control_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) {
if(e.KeyCode == System.Windows.Forms.Keys.Enter) {
OnProcessSelectedItem();
}
}
private void control_ItemSelectionChanged(object sender, System.Windows.Forms.ListViewItemSelectionChangedEventArgs e) {
OnSelectionChanged();
}
private void control_SelectedIndexChanged(object sender, EventArgs e) {
OnSelectionChanged();
OnFocusedObjectChanged();
}
private System.Windows.Forms.ListViewItem FindByTag(object tag) {
FeatureCenter.Module.ListEditors.IPictureItem itemToSearch = (FeatureCenter.Module.ListEditors.IPictureItem)tag;
if(control != null && itemToSearch != null) {
foreach(System.Windows.Forms.ListViewItem item in control.Items) {
if(((FeatureCenter.Module.ListEditors.IPictureItem)item.Tag).ID == itemToSearch.ID)
return item;
}
}
return null;
}
protected override object CreateControlsCore() {
control = new System.Windows.Forms.ListView();
control.Sorting = System.Windows.Forms.SortOrder.Ascending;
images = new System.Windows.Forms.ImageList();
images.ImageSize = new System.Drawing.Size(104, 150);
images.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
control.LargeImageList = images;
control.HideSelection = false;
control.SelectedIndexChanged += new EventHandler(control_SelectedIndexChanged);
control.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(control_ItemSelectionChanged);
control.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(control_MouseDoubleClick);
control.KeyDown += new System.Windows.Forms.KeyEventHandler(control_KeyDown);
Refresh();
return control;
}
protected override void AssignDataSourceToControl(Object dataSource) {
if(dataSource is DevExpress.Xpo.XPServerCollectionSource) {
throw new Exception("The WinCustomListEditor doesn't support Server Mode and so cannot use an XPServerCollectionSource object as the data source.");
}
if(controlDataSource != dataSource) {
IBindingList oldBindable = controlDataSource as IBindingList;
if(oldBindable != null) {
oldBindable.ListChanged -= new ListChangedEventHandler(dataSource_ListChanged);
}
controlDataSource = dataSource;
IBindingList bindable = controlDataSource as IBindingList;
if(bindable != null) {
bindable.ListChanged += new ListChangedEventHandler(dataSource_ListChanged);
}
Refresh();
}
}
public WinCustomListEditor(IModelListView info)
: base(info) {
}
public override void Dispose() {
if(controlDataSource != null) {
IBindingList bindable = controlDataSource as IBindingList;
if(bindable != null) {
bindable.ListChanged -= dataSource_ListChanged;
}
controlDataSource = null;
}
if(_barManager != null) {
_barManager.QueryShowPopupMenu -= BarManager_QueryShowPopupMenu;
_barManager = null;
}
base.Dispose();
}
public override void Refresh() {
if(control == null)
return;
object focused = FocusedObject;
control.SelectedItems.Clear();
try {
control.BeginUpdate();
images.Images.Clear();
control.Items.Clear();
if(ListHelper.GetList(controlDataSource) != null) {
images.Images.Add(ImageLoader.Instance.GetImageInfo("NoImage").GetImage());
foreach(FeatureCenter.Module.ListEditors.IPictureItem item in ListHelper.GetList(controlDataSource)) {
int imageIndex = 0;
if(item.Image != null) {
using(var ms = new MemoryStream(item.Image)) {
images.Images.Add(Image.FromStream(ms));
}
imageIndex = images.Images.Count - 1;
}
System.Windows.Forms.ListViewItem lItem =
new System.Windows.Forms.ListViewItem(item.Text, imageIndex);
lItem.Tag = item;
control.Items.Add(lItem);
}
}
}
finally {
control.EndUpdate();
}
FocusedObject = focused;
if(FocusedObject == null && control.Items.Count > 1) {
FocusedObject = control.Items[0].Tag;
}
}
public override IList GetSelectedObjects() {
if(control == null)
return new object[0] { };
object[] result = new object[control.SelectedItems.Count];
for(int i = 0; i < control.SelectedItems.Count; i++) {
result[i] = control.SelectedItems[i].Tag;
}
return result;
}
public override void SaveModel() {
}
public override SelectionType SelectionType {
get { return SelectionType.Full; }
}
public override object FocusedObject {
get {
return (control != null) && (control.FocusedItem != null) ? control.FocusedItem.Tag : null;
}
set {
System.Windows.Forms.ListViewItem item = FindByTag(value);
if(item != null) {
control.SelectedItems.Clear();
item.Focused = true;
item.Selected = true;
}
}
}
#region IControlOrderProvider Members
public int GetIndexByObject(Object obj) {
int index = -1;
FeatureCenter.Module.ListEditors.IPictureItem itemToSearch = (FeatureCenter.Module.ListEditors.IPictureItem)obj;
if(control != null){
for(int i = 0; i < control.Items.Count; i++) {
if(((FeatureCenter.Module.ListEditors.IPictureItem)control.Items[i].Tag).ID == itemToSearch.ID)
return i;
}
}
return index;
}
public Object GetObjectByIndex(int index) {
if(control != null && control.Items.Count > index) {
return control.Items[index].Tag;
}
return null;
}
public IList GetOrderedObjects() {
List<Object> list = new List<Object>();
if(control != null) {
for(int i = 0; i < control.Items.Count; i++) {
list.Add(control.Items[i].Tag);
}
}
return list;
}
#endregion
#region IRequireContextMenu Members
private void BarManager_QueryShowPopupMenu(object sender, QueryShowPopupMenuEventArgs e) {
if (e.Control != control) {
e.Cancel = true;
e.BreakShowPopupMenu = false;
}
}
BarManager _barManager;
public void SetMenu(PopupMenu popupMenu, BarManager barManager) {
if(_barManager != null) {
_barManager.QueryShowPopupMenu -= BarManager_QueryShowPopupMenu;
}
_barManager = barManager;
_barManager.SetPopupContextMenu(control, popupMenu);
_barManager.QueryShowPopupMenu += BarManager_QueryShowPopupMenu;
}
#endregion
#region IRequireDXMenuManager Members
public void SetMenuManager(IDXMenuManager menuManager) { }
#endregion
}
}