How to: Implement a Custom WinForms List Editor 如何:实现自定义 WinForms 列表编辑器

The eXpressApp Framework is shipped with a number of built-in List Editors. However, in certain scenarios you may need to implement a custom List Editor, to display object collections in a particular way. This topic demonstrates how to implement a custom WinCustomListEditor List Editor that uses a control from the .NET Framework library. This List Editor is designed to display objects, implementing a custom IPictureItem interface as a list of images, one for each object. It can be used, for instance, to display DVD covers.

eXpressApp 框架附带了许多内置列表编辑器。但是,在某些情况下,您可能需要实现自定义列表编辑器,以便以特定方式显示对象集合。本主题演示如何实现自定义 WinCustomList 编辑器列表编辑器,该编辑器使用 .NET Framework 库中的控件。此列表编辑器旨在显示对象,实现自定义 IPictureItem 接口作为图像列表,每个对象一个。例如,它可用于显示 DVD 封面。

The following image demonstrates the implemented List Editor in an Album List View:

下图演示了相册列表视图中实现的列表编辑器:

WinThumbnailEditor

Note 注意
You can see the code implemented here in the FeatureCenter Demo installed with XAF. This demo is located in the %PUBLIC%\Documents\DevExpress Demos 19.2\Components\eXpressApp Framework\FeatureCenter folder, by default.
您可以在使用 XAF 安装的功能中心演示中看到此处实现的代码。此演示位于%PUBLIC%\Documents\DevExpress Demos 19.2\Components\eXpressApp Framework\FeatureCenter folder, by default.
Tip 提示
To learn how to support a context menu for the WinCustomListEditor, refer to the How to: Support a Context Menu for a Custom WinForms List Editor topic.
要了解如何支持 WinCustomListEditor 的上下文菜单,请参阅"如何:支持自定义 WinForms 列表编辑器的上下文菜单"主题。

When implementing a custom List Editor that works with specific data, you can design it for a particular class. However, in this example, an interface will be introduced containing the properties required by the List Editor. Then, the List Editor will be designed to display objects implementing the interface. This approach allows you to simultaneously use that same List Editor for different classes. List Views displayed using the WinCustomListEditor will have two columns: Image and Text. The special interface has an additional ID property that represents a unique object identifier.

实现适用于特定数据的自定义列表编辑器时,可以为特定类设计它。但是,在此示例中,将引入一个包含列表编辑器所需的属性的接口。然后,列表编辑器将设计为显示实现接口的对象。此方法允许您同时对不同的类使用相同的列表编辑器。使用 WinCustomListEditor 显示的列表视图将有两列:图像和文本。特殊接口具有表示唯一对象标识符的附加 ID 属性。

using System.Drawing;
//...
interface IPictureItem {
    Image Image { get; }
    string Text { get; }
    string ID { get; }
}

 

Start implementing the List Editor by inheriting its class from the ListEditor class, and implement basic functionality by overriding the following members. Note that your editor should be public.

  • CreateControlsCore method, that instantiates the List Editor's control. Override it to create and configure the control.
  • AssignDataSourceToControl method, that assigns the List Editor's data source to its control. Override it to support object change notification, when the data source implements the IBindingList interface.
  • ListEditor.Refresh method, that refreshes data in the List Editor's control. Override it to make the control reload all objects from its data source.
  • ListEditor.Dispose method, that disposes of a manually allocated controlDataSource property .
  • To specify that List Views displaying IPictureItem, objects should use the WinCustomListEditor, decorate the List Editor class with the ListEditorAttribute.

通过从 ListEditor 类继承其类来开始实现列表编辑器,并通过重写以下成员来实现基本功能。请注意,您的编辑器应该是公开的。

  • 创建 ControlsCore 方法,该方法实例化列表编辑器的控件。覆盖它以创建和配置控件。
  • 分配数据源控制方法,该方法将列表编辑器的数据源分配给其控件。当数据源实现 IBindingList 接口时,重写它以支持对象更改通知。
  • 列表编辑器.刷新方法,用于刷新列表编辑器控件中的数据。覆盖它以使控件从数据源重新加载所有对象。
  • 列表编辑器.Dispose 方法,用于释放手动分配的控件DataSource 属性。
  • 要指定显示 IPictureItem 的列表视图,对象应使用 WinCustomListEditor,使用 ListEditor 属性装饰列表编辑器类。

 

The demonstrated List Editor can display a collection of objects implementing the IPictureItem interface. However, it does not support items selection, because it cannot recognize what item is currently selected. To support selection, the following members must be modified:

  • In the CreateControlsCore method, subscribe to the control's SelectedIndexChanged and ItemSelectionChanged events. In the SelectedIndexChanged event handler, call the OnSelectionChanged method. In the ItemSelectionChanged event handler, call the OnSelectionChanged and OnFocusedObjectChanged methods.
  • Override the ListEditor.SelectionType property. Since the ListView control supports both single and multiple selections, this property must return SelectionType.Full.
  • Override the ListEditor.GetSelectedObjects method. This method must return a list of the selected objects.

演示的列表编辑器可以显示实现 IPictureItem 接口的对象的集合。但是,它不支持项目选择,因为它无法识别当前选择的项目。要支持选择,必须修改以下成员:

  • 在"创建控制核心"方法中,订阅控件的"选定索引更改"和"项目选择更改"事件。在"选定的索引更改事件"处理程序中,调用 OnSelectionChanged 方法。在"项目选择更改"事件处理程序中,调用 OnSelectionChanged 和 On 焦点对象更改方法。
  • 覆盖列表编辑器.选择类型属性。由于 ListView 控件同时支持单个和多个选择,因此此属性必须返回 SelectionType。
  • 覆盖列表编辑器.获取选定对象方法。此方法必须返回所选对象的列表。

 

In addition to selection, a List Editor should be able to invoke a Detail View for the focused object when an end-user presses ENTER, or double-clicks the object. For this purpose, modify the following members:

  • In the CreateControlsCore method, subscribe to the control's MouseDoubleClick and KeyDown events. In the event handlers, call the OnProcessSelectedItem method.
  • Override the ListEditor.FocusedObject property, to get and set the focused object. To do this, we implement an additional FindByTag helper method.
  • Modify the ListEditor.Refresh method, to make the List Editor retain focus when refreshing data in the control.

除了选择之外,列表编辑器还应能够在最终用户按下 ENTER 或双击对象时调用焦点对象的详细信息视图。为此,请修改以下成员:

  • 在"创建控制核心"方法中,订阅控件的鼠标双击和键关闭事件。在事件处理程序中,调用 OnProcess 选择项方法。
  • 覆盖 ListEditor.焦点对象属性,获取和设置焦点对象。为此,我们实现了一个额外的 FindByTag 帮助器方法。
  • 修改 ListEditor.Refresh 方法,以使列表编辑器在刷新控件中的数据时保持焦点。

If you need to store the editor settings in the Application Model, implement the ListEditor.SaveModel method. Otherwise, leave this method empty

如果需要在应用程序模型中存储编辑器设置,请实现 ListEditor.SaveModel 方法。否则,将此方法留空

This code snippet demonstrates the steps above.

此代码段演示了上述步骤。

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Templates;
using DevExpress.ExpressApp.Utils;
using DevExpress.ExpressApp.Win.Controls;
using DevExpress.ExpressApp.Win.SystemModule;
using DevExpress.Utils.Menu;
using DevExpress.XtraBars;
// ...
[ListEditor(typeof(IPictureItem))]
public class WinCustomListEditor : ListEditor {
    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, MouseEventArgs e) {
        if(e.Button == MouseButtons.Left) {
            OnProcessSelectedItem();
        }
    }
    private void control_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) {
        if(e.KeyCode == 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) {
        IPictureItem itemToSearch = (IPictureItem)tag;
        if(control != null && itemToSearch != null) {
            foreach(System.Windows.Forms.ListViewItem item in control.Items) {
                if(((IPictureItem)item.Tag).ID == itemToSearch.ID)
                    return item;
            }
        }
        return null;
    }
    protected override object CreateControlsCore() {
        control = new System.Windows.Forms.ListView();
        control.Sorting = SortOrder.Ascending;
        images = new System.Windows.Forms.ImageList();
        images.ImageSize = new System.Drawing.Size(104, 150);
        images.ColorDepth = 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 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() {
        controlDataSource = 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").Image);
                foreach(IPictureItem item in ListHelper.GetList(controlDataSource)) {
                    int imageIndex = 0;
                    if(item.Image != null) {
                        images.Images.Add(item.Image);
                        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;
            }
        }
    }
}

 

 
posted @ 2020-01-07 11:40  code first life  阅读(257)  评论(0编辑  收藏  举报