C# WinForm 滚动条换肤

滚动条换肤是C#WinForm中的难点,因为很多控件的滚动条是由系统来进行绘制的,所以滚动条的绘制就不得不使用大量的API函数来进行绘制。如果对API函数不熟悉的话,就很难达到自己想要的效果,而这部分本身就不是C#的强项,所以网上使用C++重绘滚动条的例子很多,但用C#写的确很少。

       在看过这篇文章后,如果您有什么好的意见和建议,请在下面留言。

       先看一下效果图:

 

     上图实现的是一个仿FoxMail的换肤控件的截图,其中实现了TreeView、Panel、DataGridView和ListBox的滚动条的换肤,下面介绍一下滚动条换肤的原来。

      在网上搜了一下,滚动条换肤一般有两种方法实现。第一种方式比较变态,直接拦截系统绘制滚动条的消息,将绘制过程接管过来,这种方式显然不是C#的强项,不过网上已经有了C++的实现版本,如果对C++比较熟悉的话,可以研究一下,如果哪位用空改成C#的版本,希望可以给我发一份。

      C++版本的滚动条换肤示例代码下载:/Files/liutao409/SkinSB.rar(这个例子实现的效果相当的不错,并且没有什么Bug,很值得研究一下)

     第二种方法相对来说比较简单,就是将自己绘制的滚动条覆盖在控件的滚动条上面,然后让自己绘制的滚动条和控件自身的滚动条进行联动就可以了。关于如何自绘滚动条,网上应该可以找得到,我这里就不详细说了,只提供一个滚动条绘制的源代码给大家下载。

     C#版自绘垂直滚动条示例代码下载:/Files/liutao409/customscrollbar_src.rar

     上面介绍了滚动条绘制需要处理的一些问题,下面的就是本文的重点,如何让自定义滚动条和控件的滚动条进行联动。下面我用DataGridview的滚动条的例子来说明。

     其它地方我就不多说了,代码里面写得很明白,关键的水平和垂直滚动条的相关参数的说明给大家讲解一下,这个参数的算法绝对是原创,并且可以跟系统自带的滚动条高度的同步:

 

    this._hScrollBarEx.Maximum = 需要滚动的区域的宽度;(包括显示区域和未显示的滚动区域的宽度,注意不包括行标题列)

    this._hScrollBarEx.LargeChange = _hScrollBarEx.Maximum / _hScrollBarEx.Width + 显示区域的宽度(可显示的区域的宽度,注意不包括行标题列);

    this._vScrollBarEx.Maximum = 显示区域包含的数据的行数 + _vScrollBarEx.Minimum + _vScrollBarEx.LargeChange(默认值为100) - 1;

 

public class DataGridViewEx : DataGridView

    {  

        private int _displayRowCount = 0;

        private int _displayWidth = 0;

        private ScrollBarEx.HScrollBarEx _hScrollBarEx;

        private ScrollBarEx.VScrollBarEx _vScrollBarEx;

        public DataGridViewEx():base()

        {

            _hScrollBarEx = new HScrollBarEx();

            _vScrollBarEx = new VScrollBarEx();

            this.Controls.Add(_hScrollBarEx);

            this.Controls.Add(_vScrollBarEx);

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

            this.HorizontalScrollBar.VisibleChanged += new EventHandler(ScrollBar_VisibleChanged);

            this.VerticalScrollBar.VisibleChanged += new EventHandler(ScrollBar_VisibleChanged);

            this.SizeChanged += new EventHandler(ScrollBar_VisibleChanged);

            this._vScrollBarEx.ValueChanged += new EventHandler(VScrollBarEx_ValueChanged);

            this._hScrollBarEx.ValueChanged += new EventHandler(HScrollBarEx_ValueChanged);

            this.Scroll += new ScrollEventHandler(DataGridViewEx_Scroll);

            this.ColumnHeadersHeightChanged += new EventHandler(ScrollBar_VisibleChanged);

            this.ColumnWidthChanged += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);

            this.RowHeadersWidthChanged += new EventHandler(ScrollBar_VisibleChanged);

            this.RowHeightChanged += new DataGridViewRowEventHandler(ScrollBar_VisibleChanged);

            this.RowsAdded += new DataGridViewRowsAddedEventHandler(ScrollBar_VisibleChanged);

            this.RowsRemoved += new DataGridViewRowsRemovedEventHandler(ScrollBar_VisibleChanged);

            this.ColumnAdded += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);

            this.ColumnRemoved += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);

            this.DataSourceChanged += new EventHandler(ScrollBar_VisibleChanged);

            SetScrollBarEx();

        }

 

        private void VScrollBarEx_ValueChanged(object sender, EventArgs e)

        {

            if (!this.dgvScroll)

            {

                this.FirstDisplayedScrollingRowIndex = _vScrollBarEx.Value;

                Application.DoEvents();

            }

        }

 

        private void HScrollBarEx_ValueChanged(object sender, EventArgs e)

        {

            if (!this.dgvScroll)

            {

                this.HorizontalScrollingOffset = _hScrollBarEx.Value;

                GetDisplayWidth();

                Application.DoEvents();

            }

        }

 

        private void DataGridViewEx_Scroll(object sender, ScrollEventArgs e)

        {

            this.dgvScroll = true;

            if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)

            {

                _vScrollBarEx.Value = this.FirstDisplayedScrollingRowIndex;

            }

            else

            {

                _hScrollBarEx.Value = this.HorizontalScrollingOffset;

            }

            this.dgvScroll = false;

        }

 

        private void ScrollBar_VisibleChanged(object sender, EventArgs e)

        {

            SetScrollBarEx();

        }

 

        private void SetScrollBarEx()

        {

            if (this.VerticalScrollBar.Visible)

            {

                _vScrollBarEx.Visible = true;

                _vScrollBarEx.Location = new Point(this.DisplayRectangle.Width, 0);

                this.VerticalScrollBar.SendToBack();

                _vScrollBarEx.Height = this.DisplayRectangle.Height;

 

                GetDisplayRowCount();

            }

            else

            {

                _vScrollBarEx.Visible = false;

            }

 

            if (this.HorizontalScrollBar.Visible)

            {

               _hScrollBarEx.Visible = true;

                _hScrollBarEx.Location = new Point(0, this.DisplayRectangle.Height);

                this.HorizontalScrollBar.SendToBack();

                _hScrollBarEx.Width = this.DisplayRectangle.Width ;

 

                GetDisplayWidth();

                _hScrollBarEx.Value = this.HorizontalScrollingOffset;

            }

            else

            {

                _hScrollBarEx.Visible = false;

            }

        }

 

        public int GetDisplayWidth()

        {

            int width = 0;

            for (int i = 0; i < this.Columns.Count; i++)

            {

                width += this.Columns[i].Width;

            }

            _displayWidth = width;

            this._hScrollBarEx.Maximum = width;

            this._hScrollBarEx.LargeChange = _hScrollBarEx.Maximum / _hScrollBarEx.Width + this.DisplayRectangle.Width - this.RowHeadersWidth;

            return width;

        }

 

        public int GetDisplayRowCount()

        {

            int j = 0;

           int height = 0;

            int i = this.FirstDisplayedScrollingRowIndex;

            if (i < 0)

            {

                i = 0;

            }

            for (; i < this.Rows.Count; i++)

            {

                height += this.Rows[i].Height;

                if (height < this.DisplayRectangle.Height - this.ColumnHeadersHeight)

                {

                    j++;

                }

                else

                {

                    break;

                }

 

            }

            j = this.Rows.Count - j;

            if (j < 0)

            {

                j = 0;

            }

            if (_displayRowCount != j)

            {

                _displayRowCount = j;

                _vScrollBarEx.Maximum = j + _vScrollBarEx.Minimum + _vScrollBarEx.LargeChange - 1;

                if (this.FirstDisplayedScrollingRowIndex < 0)

                {

                    _vScrollBarEx.Value = 0;

                }

                else if (this.FirstDisplayedScrollingRowIndex > _vScrollBarEx.Maximum)

                {

                    _vScrollBarEx.Value = _vScrollBarEx.Maximum;

                }

                else

                {

                    _vScrollBarEx.Value = this.FirstDisplayedScrollingRowIndex;

                }

            }

            return j;

        }

   }

      DataGridview的滚动条不是由系统进行绘制的,所以滚动条的很多参数都是可以取到的,但是像Listbox或者Textbox这样的控件,滚动条的信息很多都不好获取,这个时候,就要使用API函数来进行处理了,但整个滚动条的覆盖方案还是可以参照上面的来进行,只是把一些参数的获取和设置改由API函数来完成就行了,当然在一些细节的地方会有所不同。

      关于滚动条的API函数的说明,可以参考这一篇博客:http://www.cnblogs.com/yellowyu/archive/2009/02/15/1390926.html

特别要注意的是下面两句:

      我原来以为只要用SetScrollInfo设置控件的滚动条参数后控件就会自动滚动到指定的位置,但实际结果却不是这样的,必须还要调用PostMessage发送一个让控件滚动的消息控件才会滚动。

Win32API.SetScrollInfo(tvImageList.Handle, (int)ScrollBarDirection.SB_VERT, ref info, true);

Win32API.PostMessage(tvImageList.Handle, Win32API.WM_VSCROLL, Win32API.MakeLong((short)Win32API.SB_THUMBTRACK, (short)(info.nPos)), 0);

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值