扩展 ImageIndexConverter 和 ImageIndexEditor。






4.20/5 (7投票s)
2003 年 5 月 18 日
4分钟阅读

56206

382
为用户控件扩展的 ImageIndexConverter 和 ImageIndexEditor。
引言
组件中的 ImageIndex
和 ImageList
是成对工作的。当在一个控件中同时拥有这两个属性时,您可以使用 System.Windows.Forms.Design
命名空间中的 ImageIndexConverter
和私有编辑器 ImageIndexEditor
,将图像列表显示为下拉组合框。
[Category("Appearance"),
Description("..."), DefaultValue(-1)]
[TypeConverter(typeof(ImageIndexConverter)),
Editor("System.Windows.Forms.Design.ImageIndexEditor", typeof(UITypeEditor))]
public int ImageIndex
{
get
{
return _ImageIndex;
}
set
{
if (_ImageIndex = value)
{
_ImageIndex=value;
}
}
然而,使用这两个类存在一些限制。
- Framework Class Library (FCL) 中的
ImageIndexConverter
和ImageIndexEditor
仅在控件同时拥有imageindex
和ImageList
属性时才有效。我们如何才能在没有ImageList
的子控件(例如TabPage
类)上获得相同的行为?TabPage
没有ImageList
。正常的ImageIndexConverter
和ImageIndexEditor
不适用于这种情况。 - 一种方法是在子类中添加
ImageList
属性。需要额外的代码来将父控件的ImageList
属性的内容复制到子控件。尽管您可以将ImageList
属性的BrowsableAttribute
设置为 false,以避免通过可视化 IDE 产生意外的用户交互,但用户仍然可以通过代码在子类中设置ImageList
。这可能会引起混淆。这绝对不是一个好的解决方案。 ImageIndexEditor
是System.Windows.Forms.Design
命名空间中的一个私有类。要使用此编辑器,您需要在ImageIndex
属性的EditorAttribute
中传递标识ImageIndexEditor
完全引用的字符串。Visual Studio 设计器对象(即设计时对象的 Site 属性)将为属性解析编辑器。使用此编辑器的问题在于与未来 .NET 版本的兼容性。Microsoft 对维护此编辑器或此编辑器功能的保证并不存在,因为它属于私有。
通用实现
ExtImageIndexConverter
和 ExtImageIndexEditor
适用于两种不同的场景。典型情况是类拥有自己的 ImageList
和 ImageIndex
。TypeDescriptor
类用于确定 ImageList
是否在上下文实例的属性列表中。
public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
...
PropertyDescriptorCollection PropertyCollection
= TypeDescriptor.GetProperties(context.Instance);
PropertyDescriptor Property;
if ((Property = PropertyCollection.Find("ImageList", false)) != null)
ImageList = (ImageList) Property.GetValue(context.Instance);
...
如果类拥有自己的 ImageList
,则图像列表将从此 ImageList
构建。另一种场景是 ImageList
存在于 Parent
对象中,就像 TabPages
和 TabControl
类一样。子对象的 Parent
属性用于定位 ImageList
。只有继承自 Control
类的对象才具有 Parent
属性。Parent
属性的值仅在对象添加到容器的 Controls
集合时才会被赋值。否则,Parent
的值为 null。
PropertyDescriptorCollection PropertyCollection
= TypeDescriptor.GetProperties(context.Instance);
PropertyDescriptor Property;
if ((Property = PropertyCollection.Find("ImageList", false)) != null)
ImageList = (ImageList) Property.GetValue(context.Instance);
else if ((Property = PropertyCollection.Find("Parent", false)) != null)
{
object Parent = Property.GetValue(context.Instance);
PropertyDescriptorCollection ParentPropertyCollection
= TypeDescriptor.GetProperties(Parent);
if (ParentPropertyCollection != null)
{
PropertyDescriptor ParentProperty
= ParentPropertyCollection.Find("ImageList", false);
if (ParentProperty != null)
ImageList = (ImageList) ParentProperty.GetValue(Parent);
...
当您将 TabPage
添加到 TabControl
对象的 TabPages
集合时,集合事件处理程序会将该 TabPage
对象添加到 TabControl
对象的 Control's
集合中。这也是我们在 ImageListBrowser
中所做的。
public class ImageItemCollection : CollectionBase
{
private Control _Parent;
public ImageItemCollection(Control Parent)
{
_Parent = Parent;
}
}
protected override void OnRemoveComplete(int index,object value)
{
if (_Parent.Controls.Contains((Control) value))
{
_Parent.Controls.Remove((Control) value);
OnCollectionChanged();
}
}
protected override void OnInsertComplete(int index,object value)
{
if (!_Parent.Controls.Contains((Control) value))
{
_Parent.Controls.Add((Control) value);
OnCollectionChanged();
}
}
protected override void OnClearComplete()
{
_Parent.Controls.Clear();
OnCollectionChanged();
}
protected virtual void OnCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, EventArgs.Empty);
}
我们还希望在对 Items
集合所做的任何更改时刷新父控件。OnCollectionChanged
事件用于此目的。
专有实现
我们可能不总是处理容器模型。在某些情况下,我们可能只有一个继承自 Component
类的 Items
集合,并且其中有一个 ImageIndex
属性。这些项目不存储在父控件的 Controls
集合中。在这种情况下,我们需要一个从子控件到父控件的引用,就像 Control
类的 Parent
属性一样。我们可以在 Collection
类中实现这一点。ImageBrowser2
展示了如何为非容器类型的父控件获得相同的结果。在这种情况下,ExtImageIndexConverter
和 ExtImageIndexEditor
只作用于 ImageItem
类。
public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
...
ImageList ImageList = null;
if (context.Instance != null && context.Instance is ImageItem)
{
// Step 1 - Determine who has the imagelist.
if (((ImageItem)context.Instance).ReferenceParent != null)
ImageList = ((ImageItem)context.Instance).ReferenceParent.ImageList;
// Step 2 - Construct list of index for the images in the ImageList if any.
if (ImageList != null)
...
其他注意事项
- 由于
Controls
集合由Items
集合维护,因此父控件中的Controls
集合不应由设计器序列化。定义了一个新的Controls
集合,并将其设置为在DesignerSerializeVisibility
中隐藏。[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Control.ControlCollection Controls { get { return base.Controls; } }
- 为了让设计器为
Item
生成代码,Item
必须继承自Component
或其派生类之一。否则,您需要开发自己的序列化程序来处理代码生成。为了获得一个轻量级的类,您应该这样做。 - 在
ImageListBrowser2
中,我们不希望在 Windows 窗体的组件托盘中显示ImageItem
对象的图标。DesignTimeVisibleAttribute
用于使其不可见。[DesignTimeVisible(false)] public class ImageItem : System.ComponentModel.Component
结论
ImageListBrowser
中的 ExtImageIndexConverter
和 ExtImageIndexEditor
适用于大多数与 ImageList
和 ImageIndex
相关的场景。事实上,我在我的项目中使用了这两个类。
您还可以查看 ImageListBrowser2
中的 ExtImageIndexConverter
和 ExtImageIndexEditor
以了解专有实现。