65.9K
CodeProject 正在变化。 阅读更多。
Home

ImageListBox - 将本地化自定义对象集合公开为属性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (14投票s)

2002 年 5 月 31 日

8分钟阅读

viewsIcon

248267

downloadIcon

8042

一个控件,它公开一个自定义对象集合作为属性,该属性可由 VS.NET 窗体设计器编辑并支持本地化

目录

引言

在本文中,我将演示一种创建列表框控件的技术,该控件在每个项的文本左侧绘制一个小图像(参见图 1)。此控件公开 Items 集合属性,其中包含自定义项,这些项可以通过 Visual Studio 的集合编辑器进行编辑。

Test Form

图 1 运行时控件外观

有几篇关于同一问题的文章,例如 nhgiangListBox with Icons。但这些文章都没有实现一个具有 Items 属性的控件,该属性可以通过 Visual Studio 的 Windows 窗体设计器进行编辑。它们使用一些手动创建的代码片段,例如

以编程方式添加项 - 就像在 C++ 中一样,没有 Visual C# 的优势。

imageListBox.Items.Add(new ImageListBoxItem("Text 1", 0));
imageListBox.Items.Add(new ImageListBoxItem("Text 2", 1));
imageListBox.Items.Add(new ImageListBoxItem("Text 3", 2));

本文描述的控件实现提供了可视化编辑功能,可加快开发过程。但是,当然,它仍然支持以编程方式管理项。

可视化添加的项 - 您不必记住图像列表中的哪个索引与图像相关联。所有编辑都在集合编辑器中完成,您无需触摸代码(参见图 2)。

Collection Editor

图 2 设计时集合编辑

在接下来的段落中,我将简要描述我遇到的问题。为了实现,最好查看代码 - 代码本身说明了一切。

在项目中 Ya 永的控件

如果您想首先尝试演示项目,请不要忘记先编译它(至少是 ImageListBox 控件库),否则控件将不会出现在设计时的测试窗体中。

Property Browser

图 3 控件的主要属性

如果您想在窗体上 Ya 永 ImageListBox,请也将 ImageList 控件添加到窗体,用图像填充它,然后设置 ImageListBoxImageList 属性,然后开始向 Items 属性添加项(参见图 3)。

控件类

关于控件类没什么好说的。ImageListBox 继承自标准的 ListBox 控件。我们应该实现 ImageList 属性,它允许提供要绘制在列表框中的图像源。另一个步骤是重写 ListBoxItems 属性。唯一重要的是设置 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 属性,以告知设计器我们在设计时希望序列化集合的内容,而不是集合本身。DrawMode 属性设置为 OwnerDrawFixed 值,然后通过 [Browsable(false)] 属性隐藏在属性浏览器中。绘制在 OnDrawItem 受保护的方法中进行,没有什么复杂的需要描述。

集合类实现

有几种实现集合的方法,例如内部类或嵌套类,或者其他方式。在本演示中,我们将使用基类 Items 集合来存储我们集合的对象,因此最好将集合实现为嵌套类,以便访问控件的私有成员。集合类应实现 IListICollectionIEnumerable 接口。在这种情况下,我们不能使用 CollectionBase 继承,因为其行为比简单的项存储更复杂。我建议您查看代码以了解实现的详细信息。另一个重要问题是为集合类实现 this[int index] 属性,这将允许访问集合成员。

项类实现。方法 I

从头开始实现项类(无基类)

首先,我们需要为项类实现至少两个构造函数。第一个是无参构造函数,它允许集合编辑器创建“空”项,所有属性都设置为默认值。另一个构造函数是具有最多参数的构造函数,这些参数足以完全重建项对象的所有可编辑属性。在我们的情况下,它是 ImageListBoxItem(string Text, int ImageIndex)

项类实现了 ISerializableICloneable 接口(我不确定是否需要 ICloneable,但这只是复制了 ListViewItem 实现的接口集)。

ISerializable 接口由集合编辑器使用,以允许在编辑器中设置项属性。每个项都序列化两个属性:Text(字符串)和 ImageIndex(整数)。这些属性出现在集合编辑器右侧的属性网格中(参见图 4)。

Images Combobox

图 4 ImageListBoxItem 属性。方法 I。

另一个值得注意的问题是如何让集合编辑器显示 ImageIndex 属性的正确值(以及缩略图)。我们将分三个步骤完成此操作。

  1. 向项类添加 ImageList 只读属性。通过 [Browsable(false)] 属性将其隐藏在属性浏览器中。此属性由分配给 ImageIndex 属性的 UITypeEditor 使用,它是此值编辑器从中获取图像以在组合框中显示的图像列表。
  • ImageIndex 属性实现之前添加 [TypeConverter(typeof(ImageIndexConverter))] 属性。这告诉编辑器它需要从与项关联的 ImageList 中获取索引。
  • ImageIndex 属性实现之前添加 [Editor("System.Windows.Forms.Design.ImageIndexEditor", typeof(UITypeEditor))] 属性。这告诉集合编辑器为图像索引属性使用标准的 UI 编辑器。
  • Images Combobox

    图 5 带有控件 ImageList 中图像的组合框

    瞧!这是所需的填充了图像的组合框(参见图 5)。

    项类型转换器类实现

    最后一步,我们需要 Windows 窗体设计器将以下代码自动添加到我们的窗体的 InitializeComponent() 函数中。

    this.listBox1.Items.AddRange(
        new Controls.Development.ImageListBoxItem[] {
        new Controls.Development.ImageListBoxItem("Granite", 0),
        new Controls.Development.ImageListBoxItem("Marble", 1),
        new Controls.Development.ImageListBoxItem("Stone", 2),
        new Controls.Development.ImageListBoxItem("Stucco", 3)
        }
    );

    这通过实现继承自 TypeConverter 的类型转换器类来完成。我们将使用 ExpandableTypeConverter(就像 Microsoft 对 ListViewItem 所做的那样)来减少需要重写的函数数量。我们唯一应该重写的函数是 CanConvertToConvertTo。我们应该只转换为 Windows 窗体设计器使用的 InstanceDescriptor 类型。ConvertTo 函数通过提供将出现在设计器添加的代码中的构造函数的 ConstructorInfo 来构建 InstanceDescriptor 对象(在我们的情况下是 ImageListBoxItem(string Text, int ImageIndex))。然后,我们通过 [TypeConverter(typeof(ImageListBoxItemConverter))] 属性将此转换器类与 ImageListBoxItem 关联起来。现在,控件已准备好由设计器进行编辑。

    方法 I 的优缺点

    优点

    • 该类没有任何基类(除了 System.Object)。您可以完全控制您创建的类。

    缺点

    • 本地化不直接且尚未实现
    • 您需要实现类型转换器类

    项类实现。方法 II(可本地化!)

    另一种解决方案是将集合项类派生自 System.ComponentModel.Component。在这种情况下,您将无需实现类型转换器类,也无需序列化。所有其他方法和属性都将相同。在这种情况下,集合编辑器外观将有所不同(参见图 6),生成的代码也将不同。

    Component properties

    图 6 ImageListBoxItem 属性。方法 II。

    自动添加的代码将分为两部分。首先,所有项的成员变量将被添加到您的窗体类中。然后,在窗体的 InitializeComponent 方法中,将为项调用构造函数,将项添加到列表框,然后设置它们的属性。

    // Members added to form class automatically 
    private Controls.Development.ImageListBoxItem graniteItem; 
    private Controls.Development.ImageListBoxItem marbleItem; 
    private Controls.Development.ImageListBoxItem stoneItem; 
    private Controls.Development.ImageListBoxItem stuccoItem; 
    ... 
    private void InitializeComponent()        
    { 
        ... 
        // Constructors first called for the class
        members this.graniteItem = new 
            Controls.Development.ImageListBoxItem();
             
        this.marbleItem = new 
            Controls.Development.ImageListBoxItem();
             
        this.stoneItem = new 
            Controls.Development.ImageListBoxItem(); 
             
        this.stuccoItem = new 
            Controls.Development.ImageListBoxItem();
        ... 
        // Items added to the ImageListBox 
        this.listBox1.Items.AddRange(
            new Controls.Development.ImageListBoxItem[] {
                this.graniteItem,
                this.marbleItem,
                this.stoneItem,
                this.stuccoItem});
        ... 
        // Member properties set
        ...
        // 
        // graniteItem
        // 
        this.graniteItem.ImageIndex = 0;
        this.graniteItem.Text = "Granite";
        ...
    }

    方法 II 的优缺点

    优点

    • 此方法的最显著优点是实现简单,并且现在可以通过 Windows 窗体设计器本地化控件。
    • 无需类型转换器类
    • 类实现比方法 I 简单

    缺点

    • 唯一的缺点是项由于派生自 Component 类而变得有点“重量级”。

    结论

    本文演示了将集合公开为属性的方法。此技术可能适用于许多其他控件(OutlookBar 的面板、自定义网格控件的列标题和行标题等)。如果您花费几天时间实现此技术中所述的一组类,这将为您使用设计的控件的项目节省时间。无需添加代码行,只需单击、键入即可享受。我已经做到了。

    问题

    仍然未解决的主要问题是方法 I 的本地化。我 Ya 永 [Localizable(true)] 属性用于所有我想要本地化的成员,但这似乎不起作用。当我将窗体的 Localizable 属性设置为 true,然后尝试在语言之间切换时,ImageListBox 的内容保持不变。如果有人已经解决了这个问题,请尽可能与我联系。我注意到 ListView 控件也遇到了几乎相同的问题。

    更新

    • 感谢 James T. Johnson,工具箱位图现在可以正确显示。我添加了一个嵌入的位图资源(ImageListbox.bmp),它出现在 Visual Studio 的工具箱中。资源名称必须与控件名称匹配(减去项目默认名称空间)。
    • 已添加另一种实现支持本地化的项类的方法。
    © . All rights reserved.