对TabControl的简单优化

时间:2025-01-13 10:35:26

  之前由于忙于赶项目进度而忽视了软件的用户体验,界面挺难看,有一天看见组长优化了某个窗体,让人感觉完全不一样,我也不甘示弱,要把我的程序做顺眼一点才行。我的程序是一个以TabControl为主要容器的窗体,这样的程序窗体在目前广泛使用,谷歌浏览器Chrome,360安全卫士,QQ,鲁大师等。

对TabControl的简单优化对TabControl的简单优化

对TabControl的简单优化

重点是头部的TabItem的变迁,从文字到图标结合文字和单纯图标,让TabControl以一种比较友好的形式融入到界面中去。先看看控件的效果

对TabControl的简单优化对TabControl的简单优化

对TabControl的简单优化

为了让新的TabControl能适应三种情况(文字,图标下衬文字,图标),就定义了如下枚举,

         public enum TabTypeEnum
{
ImageText,
Text,
Image,
}

同时在新的TabControl类里面定义了对应的属性TabType和私有字段_tabType

        private TabTypeEnum _tabType;
public TabTypeEnum TabType
{
get { return _tabType; }
set
{
_tabType = value;
if (TabType != TabTypeEnum.Text)
{
SetStyle(ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor,
true);
base.UpdateStyles(); this.SizeMode = TabSizeMode.Fixed; }
else
{ SizeMode = defaultSizeModel;
this.Size = defaultSize;
}
}
}

在改变Tab的类型时要额外加一些处理逻辑,如果是Tab包含图标的,肯定要对控件的Style进行设置

SetStyle(ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor, true);
base.UpdateStyles();

这里设置的都与重绘控件时有关:双缓冲,改变大小则重绘控件。对于单纯图标还有图标+文字,单纯文字这三种方式的Tab大小会有所不同,这个Tab的大小通过ItemSize设置,这里我默认设置了纯文字则按回它初始值的大小,这个初始值在构造函数里获取;图标+文字和纯图标这两种方式在重绘时计算设置。

  描绘控件又是去重写OnPaint方法,这样又用回疏远了很久的GDI+去描绘这个TabItem。这里有三种Tab方式,但着重介绍图标+文字这种方式,Tab选中会有阴影的,阴影可以自己PS一个圆角矩形,我这个是网上抠别人的,这个图片已添加“已有项”的形式添加到项目中,然后生成操作选“嵌入资源”。

对TabControl的简单优化对TabControl的简单优化

然后在构造函数里面以下面的形式获取阴影图片资源

backImage = new Bitmap(this.GetType(), "select_background.jpg");

在绘图时,先绘阴影,再绘文字,最后绘图标。

获取当前Tab的矩形主要通过TabControl的GetTabRect(int index)方法,通过判断当前的Tab是不是被选中的,来决定绘不绘制阴影

                if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
}

然后根据Tab文字的Size来决定TabSize,

                if (this.ItemSize.Width < (textSize.Width + this.Padding.X * ))
this.ItemSize =
new System.Drawing.Size((int)textSize.Width + this.Padding.X * ,
this.ItemSize.Height);
if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * )
new System.Drawing.Size(this.ItemSize.Width,
(int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * );

然后按照文字的Size还有Tab矩形的位置大小计算出文字的位置,描绘出文字

                textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Bottom - textSize.Height - this.Padding.Y;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText,
textPoint.X,
textPoint.Y);

最后描绘图标也是结合了图标的Size和Tab的位置与大小来决定图标所在的位置,

                    e.Graphics.DrawImage(
icon,
bounds.X + (bounds.Width - icon.Width) / ,
bounds.Top + this.Padding.Y);

加上了这行代码,能让描绘出来的文字少点锯齿

e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;  

Tab的描绘就完成了,其余两种Tab只是省去了文字部分或者图标部分的描绘而已,两部分的代码都会在最后列举整个控件源码时顺带列举出来。

这个控件很大程度上参考了****网友的源码,原本的博文一下子找不出来,要是哪位园友知道的顺带告诉我,我作为参考链接附在文中,谢谢!

     class ImageTabControl:TabControl
{
public enum TabTypeEnum
{
ImageText,
Text,
Image,
} Image backImage;
Size defaultSize;
TabSizeMode defaultSizeModel; public ImageTabControl()
{
defaultSize = this.ItemSize;
defaultSizeModel = this.SizeMode; this.TabType = TabTypeEnum.ImageText;
backImage = new Bitmap(this.GetType(), "select_background.jpg");
} private TabTypeEnum _tabType;
public TabTypeEnum TabType
{
get { return _tabType; }
set
{
_tabType = value;
if (TabType != TabTypeEnum.Text)
{
SetStyle(ControlStyles.UserPaint | // 控件将自行绘制,而不是通过操作系统来绘制
ControlStyles.OptimizedDoubleBuffer | // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁
ControlStyles.AllPaintingInWmPaint | // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁
ControlStyles.ResizeRedraw | // 在调整控件大小时重绘控件
ControlStyles.SupportsTransparentBackColor, // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明
true);
base.UpdateStyles(); this.SizeMode = TabSizeMode.Fixed;
}
else
{ SizeMode = defaultSizeModel;
this.Size = defaultSize;
}
}
} protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e); if (TabType == TabTypeEnum.ImageText)
DrawImageTextItem(e);
else if (TabType == TabTypeEnum.Image)
DrawImageItem(e);
else if (TabType == TabTypeEnum.Text)
DrawTextItem(e);
} protected virtual void DrawImageTextItem(PaintEventArgs e)
{
for (int i = ; i < this.TabCount; i++)
{
//e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));
if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} // Calculate text position
Rectangle bounds = this.GetTabRect(i);
PointF textPoint = new PointF();
SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); if (this.ItemSize.Width < (textSize.Width + this.Padding.X * ))
this.ItemSize =
new System.Drawing.Size((int)textSize.Width + this.Padding.X * ,
this.ItemSize.Height);
if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * )
new System.Drawing.Size(this.ItemSize.Width,
(int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * ); // 注意要加上每个标签的左偏移量X
textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Bottom - textSize.Height - this.Padding.Y; // Draw highlights
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlLightLight, // 高光颜色
textPoint.X,
textPoint.Y); // 绘制正常文字
textPoint.Y--;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText, // 正常颜色
textPoint.X,
textPoint.Y); if (this.ImageList != null)
{
int index = this.TabPages[i].ImageIndex;
string key = this.TabPages[i].ImageKey;
Image icon = new Bitmap(, ); if (index > -)
{
icon = this.ImageList.Images[index];
}
if (!string.IsNullOrEmpty(key))
{
icon = this.ImageList.Images[key];
}
e.Graphics.DrawImage(
icon,
bounds.X + (bounds.Width - icon.Width) / ,
bounds.Top + this.Padding.Y);
}
} e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
} protected virtual void DrawImageItem(PaintEventArgs e)
{
for (int i = ; i < this.TabPages.Count; i++)
{
if (i == this.SelectedIndex)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} RectangleF itemRec = this.GetTabRect(i); if (ImageList != null)
{
int imageIndex=this.TabPages[i].ImageIndex;
string imageKey=this.TabPages[i].ImageKey;
Image ico=new Bitmap(,);
if (imageIndex >= )
ico = this.ImageList.Images[i];
if (!string.IsNullOrEmpty(imageKey))
ico = this.ImageList.Images[imageKey]; if (this.ItemSize.Height < ImageList.ImageSize.Height + this.Padding.Y * )
this.ItemSize = new System.Drawing.Size(this.ItemSize.Width,
ImageList.ImageSize.Height + this.Padding.Y * );
if (this.ItemSize.Width < ImageList.ImageSize.Width + this.Padding.X * )
this.ItemSize = new System.Drawing.Size(ImageList.ImageSize.Width + this.Padding.X * ,
this.ItemSize.Height); e.Graphics.DrawImage(ico, itemRec.X + (itemRec.Width - ico.Width) / , itemRec.Y + this.Padding.Y);
}
}
} protected virtual void DrawTextItem(PaintEventArgs e)
{
for (int i = ; i < this.TabCount; i++)
{
//e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));
if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} // Calculate text position
Rectangle bounds = this.GetTabRect(i);
PointF textPoint = new PointF();
SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); // 注意要加上每个标签的左偏移量X
textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Y+(bounds.Height-textSize.Height)/; // Draw highlights
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlLightLight, // 高光颜色
textPoint.X,
textPoint.Y); // 绘制正常文字
textPoint.Y--;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText, // 正常颜色
textPoint.X,
textPoint.Y); }
}
}

ImageTabControl

相关文章