C# WinForm控件美化扩展系列之ImageComboBox_C# 视角_CS程序员之窗

来源:互联网 发布:虎视眈眈mmd动作数据 编辑:程序博客网 时间:2024/05/11 18:36

前面介绍了两篇关于ComboBox扩展和美化的文章:C# WinForm控件美化扩展系列之ComboBox组合框控件C# WinForm控件美化扩展系列之给ComboBox加水印今天将在前两篇的基础上实现一个ImageComboBox控件,ImageComboBox控件拥有以下功能:

(1)  美化下拉按钮和边框,前面的文章已经实现。

(2)  ComboBox没有控件选择项和没有焦点时显示提示用户操作信息,前面的也文章已经实现。

(3)  在下拉列表框的项中显示图标,项可以缩进。

(4)  ComboBox控件中也显示图标。

 

来看看最终需要实现的效果:

 

1 ImageComboBox DropDownList效果
 


 

2 ImageComboBox DropDown效果
 




这篇文章中我们重点需要实现的是(3)(4)两项功能,下面我们来介绍具体实现的方法。

 

第一步,实现ImageComboBoxItem类。

要实现显示图标,当然要给每个项添加与图标相关的信息了,ImageComboBoxItem类应该包括以下内容:文本(Text)、缩进的级别(Level)、图标的索引(ImageIndexImageKey),用户数据(Tag)ImageComboBoxItem类实现了ISerializable接口,实现自定义序列化。ImageComboBoxItem类的类视图如下:

 

3 ImageComboxItem类视图


 

ImageComboBoxItem类的代码如下: 

    [Serializable]


    [DefaultProperty(
"Text")]


    [TypeConverter(


       
typeof(ExpandableObjectConverter))]


   
public class ImageComboBoxItem :


        IDisposable, ISerializable


    ...{


        Fields
#region Fields



       
private ImageComboBox _imageComboBox;


       
private string _text = "ImageComboBoxItem";


       
private ImageComboBoxItemImageIndexer _imageIndexer;


       
private object _tag;


       
private int _level;



       
#endregion



        Constructors
#region Constructors



       
public ImageComboBoxItem()


        ...{


        }



       
public ImageComboBoxItem(string text)


            :
this(text, -1, 0)


        ...{


        }



       
public ImageComboBoxItem(


           
string text, int imageIndex)


            :
this(text, imageIndex, 0)


        ...{


        }



       
public ImageComboBoxItem(


           
string text, string imageKey)


            :
this(text, imageKey, 0)


        ...{


        }



       
public ImageComboBoxItem(


           
string text, int imageIndex, int level)


            :
this()


        ...{


            _text = text;


            ImageIndexer.Index = imageIndex;


            _level = level;


        }



       
public ImageComboBoxItem(


          
string text, string imageKey, int level)


            :
this()


        ...{


            _text = text;


            ImageIndexer.Key = imageKey;


            _level = level;


        }



       
protected ImageComboBoxItem(


            SerializationInfo info,


            StreamingContext context)


            :
this()


        ...{


            Deserialize(info, context);


        }



       
#endregion



        Properties
#region Properties



        [Localizable(
true)]


       
public string Text


        ...{


           
get


            ...{


               
if (_text != null)


                ...{


                   
return _text;


                }


               
return "";


            }


           
set


            ...{


                _text = value;


            }


        }



        [Bindable(
true)]


        [Localizable(
false)]


        [DefaultValue(
"")]


        [TypeConverter(
typeof(StringConverter))]


        [DesignerSerializationVisibility(


            DesignerSerializationVisibility.Hidden)]


       
public object Tag


        ...{


           
get ...{ return _tag; }


           
set ...{ _tag = value; }


        }



        [DefaultValue(
0)]


        [Localizable(
true)]


        [RefreshProperties(RefreshProperties.Repaint)]


        [DesignerSerializationVisibility(


           DesignerSerializationVisibility.Hidden)]


       
public int Level


        ...{


           
get ...{ return _level; }


           
set


            ...{


               
if (_level < 0)


                ...{


                   
throw new ArgumentOutOfRangeException("level");


                }


                _level = value;


            }


        }



        [DefaultValue(-
1)]


        [Localizable(
true)]


        [RelatedImageList(
"ImageComboBox.ImageList")]


        [Editor(


            EditorAssemblyName.ImageIndexEditor,


            
typeof(UITypeEditor))]


        [RefreshProperties(RefreshProperties.Repaint)]


        [DesignerSerializationVisibility(


            DesignerSerializationVisibility.Hidden)]


        [TypeConverter(
typeof(NoneExcludedImageIndexConverter))]


       
public int ImageIndex


        ...{


           
get


            ...{


               
if (((ImageIndexer.Index != -1) &&


                    (ImageList !=
null)) &&


                    (ImageIndexer.Index >= ImageList.Images.Count))


                ...{


                    
return ImageList.Images.Count - 1;


                }


               
return ImageIndexer.Index;


            }


           
set


            ...{


               
if (value < -1)


                ...{


                   
throw new ArgumentOutOfRangeException("ImageIndex");


                }


                ImageIndexer.Index = value;


            }


        }



        [DefaultValue(
"")]


        [Localizable(
true)]


        [RelatedImageList(
"ImageComboBox.ImageList")]


        [RefreshProperties(RefreshProperties.Repaint)]


        [Editor(


            EditorAssemblyName.ImageIndexEditor,


           
typeof(UITypeEditor))]


        [DesignerSerializationVisibility(


            DesignerSerializationVisibility.Hidden)]


        [TypeConverter(
typeof(ImageKeyConverter))]


       
public string ImageKey


        ...{


           
get


            ...{


               
return ImageIndexer.Key;


            }


           
set


            ...{


                ImageIndexer.Key = value;


            }


        }



        [Browsable(
false)]


       
public ImageComboBox ImageComboBox


        ...{


           
get ...{ return _imageComboBox; }


        }



       
internal Image Image


        ...{


            
get


            ...{


               
int actualIndex = ImageIndexer.ActualIndex;


               
if (ImageList != null &&


                    ImageList.Images.Count >
0 &&


                    actualIndex != -
1)


                ...{


                   
return ImageList.Images[actualIndex];


                }


               
return null;


            }


        }



        [Browsable(
false)]


       
internal ImageList ImageList


        ...{


           
get


            ...{


               
if (ImageComboBox != null)


                ...{


                   
return ImageComboBox.ImageList;


                }


                
return null;


            }


        }



       
internal ImageComboBoxItemImageIndexer ImageIndexer


        ...{


           
get


            ...{


               
if (_imageIndexer == null)


                ...{


                    _imageIndexer =


                       
new ImageComboBoxItemImageIndexer(this);


                }


               
return _imageIndexer;


            }


        }



       
#endregion



        Methods
#region Methods



       
public override string ToString()


        ...{


           
return _text;


        }



       
internal void Host(ImageComboBox parent)


        ...{


            _imageComboBox = parent;


        }



        [SecurityPermission(


            SecurityAction.Demand,


            Flags = SecurityPermissionFlag.SerializationFormatter),


        SecurityPermission(


            SecurityAction.InheritanceDemand,


            Flags = SecurityPermissionFlag.SerializationFormatter)]


       
protected virtual void Serialize(


            SerializationInfo info, StreamingContext context)


        ...{


            info.AddValue(
"Text", Text);


            info.AddValue(
"Level", Level);


            info.AddValue(
"ImageIndex", ImageIndexer.Index);


           
if (!string.IsNullOrEmpty(ImageIndexer.Key))


            ...{


                info.AddValue(
"ImageKey", ImageIndexer.Key);


            }


        }



       
protected virtual void Deserialize(


            SerializationInfo info,


            StreamingContext context)


        ...{


            
string imageKey = null;


           
int imageIndex = -1;


            SerializationInfoEnumerator enumerator = info.GetEnumerator();


           
while (enumerator.MoveNext())


            ...{


                SerializationEntry current = enumerator.Current;


               
if (current.Name == "Text")


                ...{


                    Text = info.GetString(current.Name);


                }


               
else if (current.Name == "Level")


                ...{


                    Level = info.GetInt32(current.Name);


                }


               
else


                ...{


                   
if (current.Name == "ImageIndex")


                    ...{


                        imageIndex = info.GetInt32(current.Name);


                       
continue;


                    }


                   
if (current.Name == "ImageKey")


                    ...{


                        imageKey = info.GetString(current.Name);


                       
continue;


                    }


                }


            }


           
if (imageKey != null)


            ...{


                ImageKey = imageKey;


            }


           
else if (imageIndex != -1)


            ...{


                ImageIndex = imageIndex;


            }


        }



       
#endregion



        ISerializable
成员#region ISerializable 成员



        [SecurityPermission(


            SecurityAction.LinkDemand,


            Flags = SecurityPermissionFlag.SerializationFormatter)]


       
void ISerializable.GetObjectData(


            SerializationInfo info, StreamingContext context)


        ...{


            Serialize(info, context);


        }



       
#endregion



        IDisposable
成员#region IDisposable 成员



       
public void Dispose()


        ...{


            _imageComboBox =
null;


            _imageIndexer =
null;


            _tag =
null;


        }



       
#endregion



        ImageComboBoxItemImageIndexer Class


        
#region ImageComboBoxItemImageIndexer Class


       
internal class ImageComboBoxItemImageIndexer


            : ImageIndexer


        ...{


           
private ImageComboBoxItem _owner;



           
public ImageComboBoxItemImageIndexer(


                ImageComboBoxItem owner)


            ...{


                _owner = owner;


            }



           
public override ImageList ImageList


            ...{


               
get


                ...{


                   
if (_owner != null)


                    ...{


                       
return _owner.ImageList;


                    }


                   
return null;


                }


               
set


                ...{


                }


            }


        }



       
#endregion


    }

 

第二步,实现ImageComboBoxItemCollection类。

ImageComboBoxItemCollection类实现跟ComboBox.ObjectCollection类一样的功能,用来代替ComboBox控件中ComboBox.ObjectCollection类,定义一个新的Items来存储ImageComboBoxItem对象,来实现ImageComboBox控件设计时可以支持ImageComboBoxItem对象的设计。

 

第三步,给ImageComboBox控件添加一些属性。

ImageComboBox控件主要需要添加几个属性:图标集合(ImageList)、没有选择项时ComboBox中显示的默认图标(DefaultImage)、缩进值(Indent)、提示信息(EmptyTextTip)、提示信息的文本颜色(EmptyTextTipColor)。还需要覆盖一些属性,这里不一一列出了,看下面的ImageComboBox控件的类视图:

 

4 ImageComboBox类视图

第四步,实现EditorNativeWimdow类。

EditorNativeWimdow类的主要功能是实现当ImageComboBox控件的列表模式设为非DropDownList的时候,即DropDownStyle不是ComboBoxStyle.DropDownList的时候,实现在Editor中绘制图标。EditorNativeWimdow类的代码如下: 

private class EditorNativeWimdow


    : NativeWindow, IDisposable


...{


    Fields
#region Fields



   
private ImageComboBox _owner;



   
private const int EC_LEFTMARGIN = 0x1;


   
private const int EC_RIGHTMARGIN = 0x2;


   
private const int EC_USEFONTINFO = 0xFFFF;


   
private const int EM_SETMARGINS = 0xD3;


   
private const int EM_GETMARGINS = 0xD4;



   
#endregion



    Constructors
#region Constructors



   
public EditorNativeWimdow(


        ImageComboBox owner)


        :
base()


    ...{


        _owner = owner;


        Attach();


    }



   
#endregion



    Private Methods
#region Private Methods



   
private void Attach()


    ...{


       
if (!Handle.Equals(IntPtr.Zero))


        ...{


            ReleaseHandle();


        }


        AssignHandle(_owner.EditHandle);


        SetMargin();


    }



    
protected override void WndProc(


       
ref Message m)


    ...{


       
base.WndProc(ref m);



       
switch (m.Msg)


        ...{


           
case (int)NativeMethods.WindowsMessage.WM_SETFONT:


                SetMargin();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_PAINT:


                RePaint();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_SETFOCUS:


           
case (int)NativeMethods.WindowsMessage.WM_KILLFOCUS:


                RePaint();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_LBUTTONDOWN:


           
case (int)NativeMethods.WindowsMessage.WM_RBUTTONDOWN:


           
case (int)NativeMethods.WindowsMessage.WM_MBUTTONDOWN:


                RePaint();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_LBUTTONUP:


           
case (int)NativeMethods.WindowsMessage.WM_RBUTTONUP:


           
case (int)NativeMethods.WindowsMessage.WM_MBUTTONUP:


                RePaint();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_LBUTTONDBLCLK:


           
case (int)NativeMethods.WindowsMessage.WM_RBUTTONDBLCLK:


           
case (int)NativeMethods.WindowsMessage.WM_MBUTTONDBLCLK:


                RePaint();


               
break;


            
case (int)NativeMethods.WindowsMessage.WM_KEYDOWN:


           
case (int)NativeMethods.WindowsMessage.WM_CHAR:


           
case (int)NativeMethods.WindowsMessage.WM_KEYUP:


                RePaint();


               
break;


           
case (int)NativeMethods.WindowsMessage.WM_MOUSEMOVE:


               
if (!m.WParam.Equals(IntPtr.Zero))


                ...{


                    RePaint();


                }


               
break;


        }


    }



   
internal void SetMargin()


    ...{


        NearMargin(Handle, _owner.ItemHeight +
5);


    }



   
private static bool IsRightToLeft(


        IntPtr handle)


    ...{


       
int style = NativeMethods.GetWindowLong(


            handle, (
int)NativeMethods.GWL.GWL_EXSTYLE);


       
return (


            ((style & (
int)NativeMethods.WS_EX.WS_EX_RIGHT)


            == (
int)NativeMethods.WS_EX.WS_EX_RIGHT) ||


            ((style & (
int)NativeMethods.WS_EX.WS_EX_RTLREADING)


            == (
int)NativeMethods.WS_EX.WS_EX_RTLREADING) ||


            ((style & (
int)NativeMethods.WS_EX.WS_EX_LEFTSCROLLBAR)


            == (
int)NativeMethods.WS_EX.WS_EX_LEFTSCROLLBAR));


    }



   
private static void FarMargin(


        IntPtr handle,
int margin)


    ...{


       
int message = IsRightToLeft(handle) ?


        EC_LEFTMARGIN : EC_RIGHTMARGIN;


       
if (message == EC_LEFTMARGIN)


        ...{


            margin = margin &
0xFFFF;


        }


       
else


        ...{


            margin = margin *
0x10000;


        }


        NativeMethods.SendMessage(


            handle,


            EM_SETMARGINS,


            message,


            margin);


    }



   
internal static void NearMargin(


        IntPtr handle,
int margin)


    ...{


       
int message = IsRightToLeft(handle) ?


        EC_RIGHTMARGIN : EC_LEFTMARGIN;


       
if (message == EC_LEFTMARGIN)


        ...{


            margin = margin &
0xFFFF;


        }


       
else


        ...{


            margin = margin *
0x10000;


        }


        NativeMethods.SendMessage(


            handle,


            EM_SETMARGINS,


            message,


            margin);


    }



   
private void RePaint()


    ...{


        ImageComboBoxItem item = _owner.SelectedItem;



        NativeMethods.RECT rcClient =
new NativeMethods.RECT();


        NativeMethods.GetClientRect(Handle,
ref rcClient);


       
bool rightToLeft = IsRightToLeft(Handle);



        IntPtr handle = Handle;


        IntPtr hdc = NativeMethods.GetDC(handle);


       
if (hdc == IntPtr.Zero)


        ...{


           
return;


        }


       
try


        ...{


           
using (Graphics g = Graphics.FromHdc(hdc))


            ...{


                
int itemSize = _owner.ItemHeight;


                Rectangle imageRect =
new Rectangle(


                   
0,


                    rcClient.Top + (rcClient.Bottom - itemSize) /
2,


                    itemSize,


                    itemSize);


                Rectangle textRect =
new Rectangle(


                   
0,


                   
0,


                    rcClient.Right - itemSize -
6,


                    rcClient.Bottom);



               
if (rightToLeft)


                ...{


                    imageRect.X = rcClient.Right - itemSize -
2;


                    textRect.X =
2;


                }


               
else


                ...{


                    imageRect.X =
2;


                    textRect.X = imageRect.Right +
2;


                }



               
if (_owner.Text.Length == 0)


                ...{


                    DrawImage(


                       g,


                       imageRect,


                       _owner.DefaultImage,


                       _owner.DefaultImageList,


                      
0,


                       _owner.Focused);



                   
if (_owner.Text.Length == 0 &&


                        !
string.IsNullOrEmpty(_owner.EmptyTextTip) &&


                        !_owner.Focused)


                    ...{


                        TextFormatFlags format =


                        TextFormatFlags.EndEllipsis |


                        TextFormatFlags.VerticalCenter;



                       
if (_owner.RightToLeft == RightToLeft.Yes)


                        ...{


                            format |=


                                (TextFormatFlags.RightToLeft |


                                TextFormatFlags.Right);


                        }



                        TextRenderer.DrawText(


                            g,


                            _owner.EmptyTextTip,


                            _owner.Font,


                            textRect,


                            _owner.EmptyTextTipColor,


                            format);


                    }


                   
return;


                }



               
if (_owner.Text.Length > 0)


                ...{


                   
using (SolidBrush brush =


                       
new SolidBrush(_owner.BackColor))


                    ...{


                        g.FillRectangle(brush, imageRect);


                    }


                }



               
if (_owner.Items.Count == 0)


                ...{


                    DrawImage(


                       g,


                       imageRect,


                       _owner.DefaultImage,


                       _owner.DefaultImageList,


                      
0,


                       _owner.Focused);


                   
return;


                }



               
if (item == null)


                ...{


                   
return;


                }



                DrawImage(


                    g,


                    imageRect,


                    item.Image,


                    _owner.ImageList,


                    item.ImageIndexer.ActualIndex,


                    _owner.Focused);


            }


        }


       
finally


        ...{


            NativeMethods.ReleaseDC(handle, hdc);


        }


    }



   
private void DrawImage(


        Graphics g,


        Rectangle imageRect,


        Image image,


        ImageList imageList,


       
int imageIndex,


       
bool focus)


    ...{


       
using (SolidBrush brush =


           
new SolidBrush(_owner.BackColor))


        ...{


            g.FillRectangle(brush, imageRect);


        }



       
if (image == null)


        ...{


           
return;


        }



       
using (InterpolationModeGraphics graphics =


              
new InterpolationModeGraphics(


               g, InterpolationMode.HighQualityBicubic))


        ...{


           
if (focus)


            ...{


                IntPtr hIcon = NativeMethods.ImageList_GetIcon(


                   imageList.Handle,


                   imageIndex,


                   (
int)NativeMethods.ImageListDrawFlags.ILD_SELECTED);


                g.DrawIcon(Icon.FromHandle(hIcon), imageRect);


                NativeMethods.DestroyIcon(hIcon);


            }


           
else


            ...{


                g.DrawImage(


                    image,


                    imageRect,


                    
0,


                   
0,


                    image.Width,


                    image.Height,


                    GraphicsUnit.Pixel);


            }


        }


    }



   
#endregion



    IDisposable
成员#region IDisposable 成员



   
public void Dispose()


    ...{


        _owner =
null;


       
base.ReleaseHandle();


    }



   
#endregion


}

第五步,重写OnCreateControlOnHandleDestroyed方法。

重写这两个方法主要是为了ImageComboBox控件的DropDownStyle为不同的值时,控制是否需要在Editor中绘制图标,这两个方法的代码如下: 

protected override void OnCreateControl()


...{


   
base.OnCreateControl();


   
if (DropDownStyle != ComboBoxStyle.DropDownList &&


        !DesignMode)


    ...{


       
if (_nativeWimdow == null)


        ...{


            _nativeWimdow =
new EditorNativeWimdow(this);


        }


    }


}



protected override void OnHandleDestroyed(EventArgs e)


...{


   
if (_nativeWimdow != null)


    ...{


        _nativeWimdow.Dispose();


        _nativeWimdow =
null;


    }


   
base.OnHandleDestroyed(e);


}

第六步,重写OnDropDown方法。

重写这个方法是为了实现调节下拉列表框显示的大小,因为画了图标,以免项显示不完全。OnDropDown方法代码如下: 

protected override void OnDropDown(


   EventArgs e)


...{


   
base.OnDropDown(e);



   
int ddWidth = 0;


   
int textWidth = 0;


   
int itemWidth = 0;


   
int scrollBarWidth =


        Items.Count > MaxDropDownItems ?


           SystemInformation.VerticalScrollBarWidth :


          
0;


    Graphics g = CreateGraphics();



   
foreach (ImageComboBoxItem item in Items)


    ...{


        textWidth = g.MeasureString(


            item.Text, Font).ToSize().Width;


        itemWidth =


            textWidth +


            ItemHeight +
8 +


            _indent * item.Level +


            scrollBarWidth;



       
if (itemWidth > ddWidth)


            ddWidth = itemWidth;


    }



    DropDownWidth = (ddWidth > Width) ?


        ddWidth : Width;


    g.Dispose();


}

 

第七步,重绘列表项,让其缩进和显示图标。

重绘列表项,需要把ImageComboBox控件的DrawMode设为DrawMode.OwnerDrawFixed,然后通过重写OnDrawItem方法实现,具体代码如下: 

protected override void OnDrawItem(DrawItemEventArgs e)


...{


   
if (e.Index != -1)


    ...{


        ImageComboBoxItem item = Items[e.Index];


        Graphics g = e.Graphics;


        Rectangle bounds = e.Bounds;



       
int indentOffset = Indent * item.Level;



       
if ((e.State & DrawItemState.ComboBoxEdit) ==


            DrawItemState.ComboBoxEdit)


        ...{


            indentOffset =
0;


        }



       
int imageWidth = bounds.Height;


        Rectangle imageRect;


        Rectangle textRect;


        TextFormatFlags format =


            TextFormatFlags.VerticalCenter |


            TextFormatFlags.SingleLine |


            TextFormatFlags.WordBreak;



        imageRect =
new Rectangle(


            bounds.Left + indentOffset +
2,


            bounds.Top,


            imageWidth,


            imageWidth);


        textRect =
new Rectangle(


            imageRect.Right +
3,


            bounds.Y,


            bounds.Width - imageRect.Width - indentOffset -
5,


            bounds.Height);



        Rectangle backRect =
new Rectangle(


           textRect.X,


           textRect.Y +
1,


           textRect.Width,


           textRect.Height -
2);



        backRect.Width = TextRenderer.MeasureText(


            item.Text, e.Font, textRect.Size, format).Width;



       
if (base.RightToLeft == RightToLeft.Yes)


        ...{


            imageRect.X = bounds.Right - imageRect.Right;


            textRect.X = bounds.Right - textRect.Right;


            backRect.X = textRect.Right - backRect.Width;


        }



        
bool selected = ((e.State & DrawItemState.Selected) ==


            DrawItemState.Selected);



        Color backColor = selected ?


            SystemColors.Highlight :
base.BackColor;



       
using (Brush backBrush = new SolidBrush(backColor))


        ...{


            g.FillRectangle(backBrush, backRect);


        }



       
if (selected)


        ...{


            ControlPaint.DrawFocusRectangle(


                g,


                backRect);


        }



        Image image = item.Image;


       
if (image != null)


        ...{


           
using (InterpolationModeGraphics graphics =


               
new InterpolationModeGraphics(


                g, InterpolationMode.HighQualityBicubic))


            ...{


               
if (selected)


                ...{


                    IntPtr hIcon = NativeMethods.ImageList_GetIcon(


                       ImageList.Handle,


                       item.ImageIndexer.ActualIndex,


                       (
int)NativeMethods.ImageListDrawFlags.ILD_SELECTED);


                    g.DrawIcon(Icon.FromHandle(hIcon), imageRect);


                    NativeMethods.DestroyIcon(hIcon);


                }


               
else


                ...{


                    g.DrawImage(


                        image,


                        imageRect,


                       
0,


                       
0,


                        image.Width,


                        image.Height,


                        GraphicsUnit.Pixel);


                }


            }


        }



        TextRenderer.DrawText(


            g,


            item.Text,


            e.Font,


            textRect,


           
base.ForeColor,


            format);


    }


}

 

到此为止,ImageComboBox控件需要实现的功能就完成了。

 

 

原创粉丝点击