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

扩展 ImageIndexConverter 和 ImageIndexEditor。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (7投票s)

2003 年 5 月 18 日

4分钟阅读

viewsIcon

56206

downloadIcon

382

为用户控件扩展的 ImageIndexConverter 和 ImageIndexEditor。

引言

组件中的 ImageIndexImageList 是成对工作的。当在一个控件中同时拥有这两个属性时,您可以使用 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;
     }
  }

然而,使用这两个类存在一些限制。

  1. Framework Class Library (FCL) 中的 ImageIndexConverterImageIndexEditor 仅在控件同时拥有 imageindexImageList 属性时才有效。我们如何才能在没有 ImageList 的子控件(例如 TabPage 类)上获得相同的行为?TabPage 没有 ImageList。正常的 ImageIndexConverterImageIndexEditor 不适用于这种情况。
  2. 一种方法是在子类中添加 ImageList 属性。需要额外的代码来将父控件的 ImageList 属性的内容复制到子控件。尽管您可以将 ImageList 属性的 BrowsableAttribute 设置为 false,以避免通过可视化 IDE 产生意外的用户交互,但用户仍然可以通过代码在子类中设置 ImageList。这可能会引起混淆。这绝对不是一个好的解决方案。
  3. ImageIndexEditorSystem.Windows.Forms.Design 命名空间中的一个私有类。要使用此编辑器,您需要在 ImageIndex 属性的 EditorAttribute 中传递标识 ImageIndexEditor 完全引用的字符串。Visual Studio 设计器对象(即设计时对象的 Site 属性)将为属性解析编辑器。使用此编辑器的问题在于与未来 .NET 版本的兼容性。Microsoft 对维护此编辑器或此编辑器功能的保证并不存在,因为它属于私有。

通用实现

ExtImageIndexConverterExtImageIndexEditor 适用于两种不同的场景。典型情况是类拥有自己的 ImageListImageIndexTypeDescriptor 类用于确定 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 对象中,就像 TabPagesTabControl 类一样。子对象的 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 展示了如何为非容器类型的父控件获得相同的结果。在这种情况下,ExtImageIndexConverterExtImageIndexEditor 只作用于 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)
      ...

其他注意事项

  1. 由于 Controls 集合由 Items 集合维护,因此父控件中的 Controls 集合不应由设计器序列化。定义了一个新的 Controls 集合,并将其设置为在 DesignerSerializeVisibility 中隐藏。
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new Control.ControlCollection Controls
    {
       get
       {
           return base.Controls;
       }
    }
  2. 为了让设计器为 Item 生成代码,Item 必须继承自 Component 或其派生类之一。否则,您需要开发自己的序列化程序来处理代码生成。为了获得一个轻量级的类,您应该这样做。
  3. ImageListBrowser2 中,我们不希望在 Windows 窗体的组件托盘中显示 ImageItem 对象的图标。DesignTimeVisibleAttribute 用于使其不可见。
    [DesignTimeVisible(false)]
    public class ImageItem : System.ComponentModel.Component

结论

ImageListBrowser 中的 ExtImageIndexConverterExtImageIndexEditor 适用于大多数与 ImageListImageIndex 相关的场景。事实上,我在我的项目中使用了这两个类。

您还可以查看 ImageListBrowser2 中的 ExtImageIndexConverterExtImageIndexEditor 以了解专有实现。

© . All rights reserved.