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

C# 中的自定义范围选择器控件(带有一点动画滑块)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.27/5 (25投票s)

2008年8月21日

CPOL

8分钟阅读

viewsIcon

192011

downloadIcon

12451

有时,业务需求非常独特,以至于我们需要编写新的控件,而不是仅使用现有的工具箱提供的控件。本文将介绍如何编写这样一个名为 Range Control 的独特控件。控件源代码和示例应用程序已包含在内。

新版本 1.5:轻微动画和吸附功能

旧版本 1.0

第一个链接包含用于观看快速演示的二进制文件。第二个链接包含 Range 控件的源代码以及一个使用该控件的测试应用程序源代码。

RangeControlTestApp.JPG

引言

这是一个美好的早晨。你的老板 Alice 说:“嘿 Bob!如果我们能有一个符合我们业务需求的独特控件,而不是展示一个旧的,那就太好了。 bla bla bla……”。然后你心想,“好吧,这 *曾经* 是一个美好的早晨……”。然而,现在你必须设计一个符合你独特业务需求的独特自定义控件。

本文详细介绍了如何编写你自己的独特自定义控件。此外,本文还包含一个示例动画范围选择控件(包含二进制文件和源代码)。本文的初始部分解释了如何使用范围控件。如果你是高级程序员,在看到演示后,可以直接跳转到“控件代码详解”部分。

背景

业务需求并非总是非常通用的。大多数时候,业务需求非常独特,需要编写自定义控件。

使用代码

查看演示的步骤

步骤 1:将二进制文件(通过本文顶部的链接下载)复制到一个单独的文件夹。

步骤 2:这是一个压缩文件。选择一个临时文件夹,并将所有文件复制到该文件夹中。

步骤 3:运行 TestApplication.exe

步骤 4:您应该能看到一个如图所示的应用程序。

您可以使用鼠标向两个方向滑动滑块的拇指。我选择使用平滑滚动滑块而不是块状移动滑块。但是,代码可以修改以支持两者。此外,我添加了轻微的动画以提供良好的用户体验。

使用程序集的步骤

假设您已经看过演示并想使用它。要将 Range Selector 自定义控件用于您的项目,可以按照以下步骤操作:

步骤 1:使用 Visual Studio 2005 或更高版本创建一个 Windows Forms 应用程序。之所以需要 VS2005,是因为我是在 VS2005 中编译的程序集。但是,您可以非常轻松地将其移植到任何 Visual Studio 版本。

步骤 2:在工具箱中,右键单击,然后选择“选择项”。然后,单击“浏览”按钮选择程序集。选择程序集后,单击“确定”按钮。

步骤 3:现在,您将在工具箱中看到一个名为“RangeSelectorControl”的条目。将其拖放到 Windows 窗体上的任何位置即可使用该控件。

步骤 4:选择控件。然后,右键单击并选择“属性”菜单选项。

步骤 5:添加/修改/删除所需的属性以满足您的需求。

步骤 6:现在,是时候编写代码了。但这非常少。您所要做的就是将您的方法注册到控件程序集中——这样,当用户更改其选择时(通过滑动范围控件),控件程序集将调用您的方法。另外,您也可以调用控件方法 QueryRange 来查询用户的选择。下面的简单代码片段解释了这两种选择。

private void button1_Click(object sender, System.EventArgs e)
{
    string strRange1;
    string strRange2;

    rangeSelectorControl1.QueryRange(out strRange1 , out strRange2);
    textBox1.Text = strRange1 + " - " + strRange2;
    textBox1.Update();
    object obj = strRange1;
    strRange1 = "100";
}

在上面的代码片段中,第一行声明了两个字符串变量 strRange1strRange2。下一行查询控件程序集以获取当前用户选择。之后,代码更新窗体上的 TextBox 控件以显示用户选择。

现在,另一个选项是当用户修改其选项时,从控件接收连续更新。请看下面的代码片段:

private CustomRangeSelectorControl.NotifyClient objNotifyClient;

private void Form1_Load(object sender, System.EventArgs e)
{
    objNotifyClient = new CustomRangeSelectorControl.NotifyClient();
    
    rangeSelectorControl1.RegisterForChangeEvent(ref objNotifyClient);

}

在上面的代码中,第一行声明了一个类级变量 objNotifyClient。该变量在 Form_Load 事件中初始化。接下来,该对象被注册到控件程序集以进行连续通知。

不过,我包含了一个详细的示例测试应用程序,供那些广泛使用此控件程序集的用户使用。样本源代码和控件源代码可以从本文顶部的链接下载。

一如既往,这并不是专业级别的代码。因此,我在代码中没有进行极致的验证检查。但是,代码可以很快地进行改进。

关注点

好的,现在,关于编写控件程序集。这很简单。您所要做的就是创建一个新的控件项目。如果项目创建正确,您的主类将派生自 System.Windows.Forms.UserControl。您所要做的就是通过重写 OnPaint(PaintEventArgs e) 方法来管理控件的视觉活动。您可以查看范围选择器控件的源代码。它非常简单,只有一些数学计算。根据收到的评论,我将对此进行更详细的解释。

以下是范围选择器控件中我做的一些有趣的事情:

  • 范围选择器控件公开尽可能多的属性,以使用户有机会在设计时而不是在运行时查看控件。
  • 范围可以输入使用 XML 文件。这将有助于以更通用化的方式加载控件。
  • 范围选择器控件接受位图。
  • 范围选择器控件在移动滑块时避免任何闪烁。
  • 范围选择器控件自动在底部显示范围值。
  • 我还添加了一个轻微的动画,这将在下一节(控件代码详解 - 第七部分/区域)中进行解释。

控件代码详解

请参阅本文顶部链接可下载的范围选择器控件源代码(以及演示)。

我将 CustomRangeSelectorControl.RangeSelectorControl 命名空间中的控件源代码分为不同的区域。让我们逐个区域地进行介绍。

第一个区域是“设计时控件变量……”。这些是私有变量,将保存对用户公开的属性的值。例如,strXMLFileName 用于存储公开属性 XMLFileName 的值。

#region Design Time Control Variables -- Private Variables
    private string strXMLFileName;         // XML File Name that is used for
                                           // picking up the Label Values
    private string strRangeString;         // The String that is displayed
                                           // at the bottom of the control. 
    private string strRange;               // An alternate to the XML File Name where
                                           // the Range Label values are stored
    private Font fntLabelFont;             // Font of the Label
    private FontStyle fntLabelFontStyle;   // Font Style of the Label 
    private float fLabelFontSize;          // Size of the Label 
    private FontFamily fntLabelFontFamily; // Font Family of the Label 
    private string strLeftImagePath;       // Left Thumb Image Path
    private string strRightImagePath;      // Right Thumb Image Path
    private float fHeightOfThumb;          // Height Of the Thumb
    private float fWidthOfThumb;           // Width of the Thumb
    private Color clrThumbColor;           // Color of the Thumb, If not Image
    private Color clrInFocusBarColor;      // In Focus Bar Colour
    private Color clrDisabledBarColor;     // Disabled Bar Color
    private Color clrInFocusRangeLabelColor; // In Focus Range Label Color
    private Color clrDisabledRangeLabelColor;// Disabled Range label Color
    private uint unSizeOfMiddleBar;        // Thickness of the Middle bar
    private uint unGapFromLeftMargin;      // Gap from the Left Margin to draw the Bar
    private uint unGapFromRightMargin;     // Gap from the Right Margin to draw the Bar
    private string strDelimiter;           // Delimiter used to seperate
                                           // the Labels in strRange variable
    private string strRange1;              // Thumb 1 Position bar
    private string strRange2;              // Thumb 2 Position in the bar
    private Font fntRangeOutputStringFont; // Range Output string font
    private float fStringOutputFontSize;   // String Output Font Size
    private Color clrStringOutputFontColor;// Color of the Output Font 
    private FontFamily fntStringOutputFontFamily; // Font Family to display the Range string
#endregion

第二个区域是“设计时控件属性……”。这些是控件在设计时暴露给用户使用的属性。例如,看看第一个属性 XMLFileName。在 Visual Studio 中打开演示。右键单击以查看此控件的属性。如果您逐项浏览,就会发现 XMLFileName 属性已暴露给用户。用户可以通过 Visual Studio 设计器的属性窗口输入值。您可以在此部分找到所有属性,如 RangeStringRangeValuesLabelFont 等。下面的代码只是此区域的一个小片段。

#region Design Time Control Properties -- Public -- 
        Design Time User properites - Can also be changed runtime
/// <XMLFileName>
/// XMLFileName is a property that can be used to set the Range Labels
/// For Example:
/// <?xml version="1.0" encoding="utf-8" ?>
/// <RangeController>
/// <Values>
/// <Value> Excellent</Value>
/// <Value> Good</Value>
/// <Value> Fair</Value>
/// <Value> Poor</Value>
/// </Values>
/// </RangeController>
/// 
/// Here the values Excellent, Good, Fair and Poor
/// will be taken as Labels for the 
/// Control. 
/// </XMLFileName>
/// 
public string XMLFileName
{
    set
    {
        try
        {
            strXMLFileName = value;
            if (null != strXMLFileName)
            {
                xmlTextReader = 
                   new System.Xml.XmlTextReader(strXMLFileName);
                strRange = null;
                while(xmlTextReader.Read())
                {
                    switch(xmlTextReader.NodeType)
                    {
                    case System.Xml.XmlNodeType.Text:
                    strRange += xmlTextReader.Value.Trim();
                    strRange += strDelimiter;
                    break;
                    }
                }
                strRange = strRange.Remove(strRange.Length - 
                             strDelimiter.Length, strDelimiter.Length);
                CalculateValues();
                this.Refresh();
                OnPaint(ePaintArgs);
            }
        }
        catch
        {
            strXMLFileName = null;
            System.Windows.Forms.MessageBox.Show("The XML Path entered" + 
                   " may be invalid (or) The XML file is not well formed", 
                   "Error!");
        }
    }



    get
    {
        return strXMLFileName;
    }
}

/// <ControlProperties>
/// The Above are Design time (Also Runtime) Control
/// Variable properties. These variables 
/// can be used by the client to change the appearance of the control.
/// </ControlProperties>
/// 
#endregion

第三个区域是“用于计算的变量”。这些变量用于在用户移动滑块时进行数学计算。此外,这些变量还用于在正确的位置和正确的时间绘制位图、字体、线条、滑块和条形。

第四个区域是此控件的构造函数。该构造函数包含一个“变量初始化”区域。该区域包含将类中所有声明的变量初始化为默认值的代码。

第五个区域是“运行时暴露给客户端的方法”。范围控件的用户需要接收来自控件的用户活动的反馈。假设应用程序的最终用户在范围选择控件中滑动条形,那么应用程序需要接收这些事件的输入以采取行动。为此提供了两种方法:一种是被动的,另一种是主动的。被动方法是一个查询方法。该控件的用户可以在任何给定时间查询控件,以获取应用程序最终用户当前选择的范围值。否则,应用程序可以注册以在用户滑动控件时发送通知。QueryRange 方法执行被动任务,RegisterForChangeEvent 方法执行主动任务。

#region Methods Exposed to client at runtime
/// <QueryRange>
/// The client can query this method to get the range
/// </QueryRange>
/// 
public void QueryRange(out string strGetRange1, out string strGetRange2)
{
    strGetRange1 = strRange1.ToString();
    strGetRange2 = strRange2.ToString();
}

/// <RegisterForChangeEvent>
/// The client can Register for automatic
/// update whenever the values are changing
/// </RegisterForChangeEvent>
/// 
public void RegisterForChangeEvent(ref NotifyClient refNotifyClient)
{
    // If there's a valid object, the values are copied.
    try
    {
        if (null != refNotifyClient)
        {
            objNotifyClient = refNotifyClient;
            objNotifyClient.Range1 = strRange1;
            objNotifyClient.Range2 = strRange2;
        }
    }
    catch
    {
        System.Windows.Forms.MessageBox.Show("The Registered Event object has " + 
                             "a Bad memory. Please correct it", "Error!");
    }
}
#endregion

第六个区域是“这是一个计算值的私有方法……”。此部分只有一个私有方法 CalculateValues。此方法计算绘制控件各个组件所需的值。控件的各个组件是条形、滑块、范围值等。此处的代码有很好的文档记录。它获取 Graphics 对象,然后计算每个组件的位置。

#region This is a Private method that calculates 
             the values to be placed while painting
private void CalculateValues()
{
    try
    {
        // Creating the Graphics object
        System.Drawing.Graphics myGraphics = this.CreateGraphics();
        // Split the Labels to be displayed below the Bar
        strSplitLabels = strRange.Split(strDelimiter.ToCharArray(), 1024);
        nNumberOfLabels = strSplitLabels.Length;
        // If there's an image load the Image from the file
        if (null != strLeftImagePath)
        {
            imImageLeft = System.Drawing.Image.FromFile(strLeftImagePath);
        }
        if (null != strRightImagePath)
        {
            imImageRight = System.Drawing.Image.FromFile(strRightImagePath);
        }
        // Calculate the Left, Right values based on the Clip region bounds
        RectangleF recRegion = myGraphics.VisibleClipBounds;
        fLeftCol = unGapFromLeftMargin;
        fLeftRow = recRegion.Height / 2.0f; // To display the Bar in the middle
        fRightCol = recRegion.Width - unGapFromRightMargin;
        fRightRow = fLeftRow;
        fThumb1Point = fLeftCol;
        fThumb2Point = fRightCol;
        fTotalWidth = recRegion.Width - (unGapFromRightMargin + unGapFromLeftMargin);
        fDividedWidth = fTotalWidth / (float)(nNumberOfLabels - 1);
        // This is used to calculate the Thumb Point from the Range1, Range2 Value
        for(int nIndexer = 0;nIndexer < nNumberOfLabels;nIndexer++)
        {
            if (strRange1.Equals(strSplitLabels[nIndexer]))
            {
                fThumb1Point = fLeftCol + fDividedWidth * nIndexer;
            }
            else if (strRange2.Equals(strSplitLabels[nIndexer]))
            {
                fThumb2Point = fLeftCol + fDividedWidth * nIndexer;
            }
        }
        // This is for Calculating the final Thumb points
        ptThumbPoints1[0].X = fThumb1Point;
        ptThumbPoints1[0].Y = fLeftRow - 3.0f;
        ptThumbPoints1[1].X = fThumb1Point;
        ptThumbPoints1[1].Y = fLeftRow - 3.0f - fHeightOfThumb;
        ptThumbPoints1[2].X = (fThumb1Point + fWidthOfThumb);
        ptThumbPoints1[2].Y = fLeftRow - 3.0f - fHeightOfThumb/2.0f;
        ptThumbPoints2[0].X = fThumb2Point;
        ptThumbPoints2[0].Y = fRightRow - 3.0f;
        ptThumbPoints2[1].X = fThumb2Point;
        ptThumbPoints2[1].Y = fRightRow - 3.0f - fHeightOfThumb;
        ptThumbPoints2[2].X = fThumb2Point - fWidthOfThumb;
        ptThumbPoints2[2].Y = fRightRow - 3.0f - fHeightOfThumb/2.0f;
    }
    catch
    {
        // In a more professional code, a proper error message is required here

    }
}
/// <CalculateValues>
/// The below is the method that calculates the values to be place while painting
/// </CalculateValues>
/// 
#endregion

第七个区域是“Paint 方法重写”。这是将控件绘制在屏幕上的重要部分。这里的代码非常简单。第一个 for 循环在屏幕上绘制 Label。下一个循环在屏幕上绘制范围值。接下来的绘制 Slider1(在代码中称为 Thumb1)和 Slider2。此方法中的后续行用于根据上一节计算的值正确绘制焦点和禁用的颜色。

我在这个 paint 方法中实现了一个非常简单的动画。这不是非常专业的动画,但它提供了对动画的基本理解。一旦鼠标抬起,我们就将 bAnimateSlider 设置为 true。这在 paint 方法中用于反复调用 Draw 方法进行动画。但是,如果动画花费的时间超过一秒(1000 毫秒),那么我们就丢弃该动画。

private void OnPaintDrawSliderAndBar(System.Drawing.Graphics myGraphics, PaintEventArgs e)
{
    System.Drawing.Brush brSolidBrush;
    System.Drawing.Pen myPen;
// If Interesting mouse event happened on the Thumb1 Draw Thumb1
    if (bMouseEventThumb1)
    {
        brSolidBrush = new System.Drawing.SolidBrush(this.BackColor);
        if (null != strLeftImagePath)
        {
            myGraphics.FillRectangle(brSolidBrush, ptThumbPoints1[0].X, 
                        ptThumbPoints1[1].Y, fWidthOfThumb, fHeightOfThumb);
        }
        else
        {
            myGraphics.FillClosedCurve(brSolidBrush, ptThumbPoints1, 
                       System.Drawing.Drawing2D.FillMode.Winding, 0f);
        }
    }
    //if interesting mouse event happened on Thumb2 draw thumb2
    if (bMouseEventThumb2)
    {
        brSolidBrush = new System.Drawing.SolidBrush(this.BackColor);
        if (null != strRightImagePath)
        {
            myGraphics.FillRectangle(brSolidBrush, ptThumbPoints2[2].X, 
                ptThumbPoints2[1].Y, fWidthOfThumb, fHeightOfThumb);
        }
        else
        {
            myGraphics.FillClosedCurve(brSolidBrush, ptThumbPoints2, 
            System.Drawing.Drawing2D.FillMode.Winding, 0f);
        }
    }
    // The Below lines are to draw the Thumb and the Lines 
    // The Infocus and the Disabled colors are drawn properly based
    // onthe calculated values
    brSolidBrush = new System.Drawing.SolidBrush(clrInFocusRangeLabelColor);
    myPen = new System.Drawing.Pen(clrInFocusRangeLabelColor, unSizeOfMiddleBar);
    ptThumbPoints1[0].X = fThumb1Point;
    ptThumbPoints1[1].X = fThumb1Point;
    ptThumbPoints1[2].X = fThumb1Point + fWidthOfThumb;
    ptThumbPoints2[0].X = fThumb2Point;
    ptThumbPoints2[1].X = fThumb2Point;
    ptThumbPoints2[2].X = fThumb2Point - fWidthOfThumb;

    myPen = new System.Drawing.Pen(clrDisabledBarColor, unSizeOfMiddleBar);
    myGraphics.DrawLine(myPen, fLeftCol, ptThumbPoints1[2].Y, 
                        fThumb1Point, ptThumbPoints1[2].Y);
    myGraphics.DrawLine(myPen, fLeftCol, ptThumbPoints1[2].Y, fLeftCol, 
            ptThumbPoints1[2].Y + fntLabelFont.SizeInPoints);
    myGraphics.DrawLine(myPen, fRightCol, ptThumbPoints1[2].Y, fRightCol, 
            ptThumbPoints1[2].Y + fntLabelFont.SizeInPoints);
    brSolidBrush = new System.Drawing.SolidBrush(clrStringOutputFontColor);
    myGraphics.DrawString(strRangeString, fntRangeOutputStringFont, 
                          brSolidBrush, fLeftCol, 
                          fLeftRow * 2 - fntRangeOutputStringFont.Size - 3);
    myPen = new System.Drawing.Pen(clrInFocusBarColor, unSizeOfMiddleBar);
    myGraphics.DrawLine(myPen, ptThumbPoints1[2].X, ptThumbPoints1[2].Y, 
            fThumb2Point,ptThumbPoints1[2].Y);
    myPen = new System.Drawing.Pen(clrDisabledBarColor, unSizeOfMiddleBar);
    myGraphic    s.DrawLine(myPen, fThumb2Point, ptThumbPoints2[2].Y, fRightCol, 
                ptThumbPoints2[2].Y);
    // If the Thumb is an Image it draws the Image or else it draws the Thumb
    if (null != strLeftImagePath)
    {
        myGraphics.DrawImage(imImageLeft, ptThumbPoints1[0].X, 
                    ptThumbPoints1[1].Y, fWidthOfThumb, fHeightOfThumb);
    }
    else
    {
        brSolidBrush = new System.Drawing.SolidBrush(clrThumbColor);
        myGraphics.FillClosedCurve(brSolidBrush, ptThumbPoints1, 
                   System.Drawing.Drawing2D.FillMode.Winding, 0f);
    }
    // If the Thumb is an Image it draws the Image or else it draws the Thumb
    if (null != strRightImagePath)
    {
        myGraphics.DrawImage(imImageRight, ptThumbPoints2[2].X, ptThumbPoints2[1].Y, 
                    fWidthOfThumb, fHeightOfThumb);
    }
    else
    {
        brSolidBrush = new System.Drawing.SolidBrush(clrThumbColor);
        myGraphics.FillClosedCurve(brSolidBrush, ptThumbPoints2, 
                   System.Drawing.Drawing2D.FillMode.Winding, 0f);
    }
}
protected override void OnPaint(PaintEventArgs e)
{
     
    try
    {
        // Declaration of the local variables that are used.
        System.Drawing.Brush brSolidBrush;
        float fDividerCounter;
        float fIsThumb1Crossed, fIsThumb2Crossed;
        string strRangeOutput;
        string strNewRange1, strNewRange2;
        
        // Initialization of the local variables.
        System.Drawing.Graphics myGraphics = this.CreateGraphics();
        ePaintArgs = e;
        fDividerCounter = 0;
        brSolidBrush = new System.Drawing.SolidBrush(clrDisabledRangeLabelColor);
        strNewRange1 = null;
        strNewRange2 = null;

        
        // This loop is to draw the Labels on the screen.
        for(int nIndexer = 0;nIndexer < nNumberOfLabels;nIndexer++)
        {
            fDividerCounter = fLeftCol + fDividedWidth * nIndexer ;
            fIsThumb1Crossed = fDividerCounter + strSplitLabels[nIndexer].Length * 
                    fntLabelFont.SizeInPoints/2;
            fIsThumb2Crossed = fDividerCounter - 
                 (strSplitLabels[nIndexer].Length - 1) * fntLabelFont.SizeInPoints/2;
            if (fIsThumb1Crossed >= fThumb1Point && strNewRange1 == null)
            {
                // If Thumb1 Crossed this Label Make it in Focus color
                brSolidBrush = 
                  new System.Drawing.SolidBrush(clrInFocusRangeLabelColor);
                strNewRange1 = strSplitLabels[nIndexer];
            }
            if (fIsThumb2Crossed > fThumb2Point)
            {
                // If Thumb2 crossed this draw the labes
                // following this in disabled color
                brSolidBrush = 
                  new System.Drawing.SolidBrush(clrDisabledRangeLabelColor);
                //strNewRange2 = strSplitLabels[nIndexer];
            }
            else 
            {
                strNewRange2 = strSplitLabels[nIndexer];
            }
            myGraphics.DrawString(strSplitLabels[nIndexer], fntLabelFont, brSolidBrush, 
                                  fDividerCounter - ((fntLabelFont.SizeInPoints) * 
                                  strSplitLabels[nIndexer].Length)/2, fLeftRow);
        }
        // This is to draw exactly the Range String like "Range 10 to 100" 
        // This will draw the information only if there is a change. 
        if (strNewRange1 != null && strNewRange2 != null && 
        (!strRange1.Equals(strNewRange1) || !strRange2.Equals(strNewRange2)) ||
        (!bMouseEventThumb1 && !bMouseEventThumb2))
        {
            brSolidBrush = new System.Drawing.SolidBrush(this.BackColor);
            strRangeOutput = strRange1 + " - " + strRange2;
            myGraphics.DrawString(strRangeOutput , fntRangeOutputStringFont, brSolidBrush, 
                                  fLeftCol + fntRangeOutputStringFont.Size * 
                                  strRangeString.Length , 
                                  fLeftRow * 2 - fntRangeOutputStringFont.Size - 3);
            brSolidBrush = new System.Drawing.SolidBrush(clrStringOutputFontColor);
            strRangeOutput = strNewRange1 + " - " + strNewRange2;
            myGraphics.DrawString(strRangeOutput , fntRangeOutputStringFont, brSolidBrush, 
                        fLeftCol + fntRangeOutputStringFont.Size * strRangeString.Length , 
                        fLeftRow * 2 - fntRangeOutputStringFont.Size - 3);
            strRange1 = strNewRange1;
            strRange2 = strNewRange2;
    }

    if (bAnimateTheSlider)
    {
        float fTempThumb1Point = fThumb1Point;
        float fTempThumb2Point = fThumb2Point;
        int nToMakeItTimely = System.Environment.TickCount;
        for (fThumb1Point = fThumbPoint1Prev, fThumb2Point = fThumbPoint2Prev; 
        fThumb1Point <= fTempThumb1Point || fThumb2Point >= fTempThumb2Point; 
        fThumb1Point += 3.0f, fThumb2Point -= 3.0f)
        {
            bMouseEventThumb1 = true; 
            bMouseEventThumb2 = true;
            if (fThumb1Point > fTempThumb1Point)
            {
                fThumb1Point = fTempThumb1Point;
            }    


            if (fThumb2Point < fTempThumb2Point)
            {
                fThumb2Point = fTempThumb2Point;
            }
            OnPaintDrawSliderAndBar(myGraphics, e);
            if (System.Environment.TickCount - nToMakeItTimely >= 1000)
            {
                // Hey its not worth having animation for more than 1 sec. 
                break;
            }
            System.Threading.Thread.Sleep(1);
        }
        fThumb1Point = fTempThumb1Point;
        fThumb2Point = fTempThumb2Point;
        bMouseEventThumb1 = true;
        bMouseEventThumb2 = true;
        OnPaintDrawSliderAndBar(myGraphics, e);
        bAnimateTheSlider = false;
        bMouseEventThumb1 = false;
        bMouseEventThumb2 = false;
        OnPaintDrawSliderAndBar(myGraphics, e);
    }
    else
    {
        OnPaintDrawSliderAndBar(myGraphics, e);
    }
    // calling the base class.
    base.OnPaint (e);
}
catch
{
    //System.Windows.Forms.MessageBox.Show("An Unexpected Error occured. " + 
    //           "Please contact the tool vendor", "Error!");
    //throw;
}
}
}
/// <Paint >
/// The Above is the method that draws the control on the screen
/// </Paint >
#endregion

第八个区域是“用于处理鼠标事件的方法”。此部分包含捕获鼠标向上、向下和移动事件的方法。这些方法用于查看最终用户的鼠标活动是否足够吸引人。

#region Methods used for handling Mouse Events

private void RangeSelectorControl_MouseUp(object sender, 
             System.Windows.Forms.MouseEventArgs e)
{
    // If the Mouse is Up then set the Event to false
    bMouseEventThumb1 = false;
    bMouseEventThumb2 = false;
    // Storing these values for animating the slider
    fThumbPoint1Prev = fThumb1Point;
    fThumbPoint2Prev = fThumb2Point;

    CalculateValues();
    bAnimateTheSlider = true;
    this.Refresh();
}
private void RangeSelectorControl_MouseDown(object sender, 
             System.Windows.Forms.MouseEventArgs e)
{
    // If the Mouse is Down and also on the Thumb1
    if (e.X >= ptThumbPoints1[0].X && e.X <= ptThumbPoints1[2].X &&
        e.Y >= ptThumbPoints1[1].Y && e.Y <= ptThumbPoints1[0].Y)
    {
        bMouseEventThumb1 = true;
    }
    // Else If the Mouse is Down and also on the Thumb2
    else if (e.X >= ptThumbPoints2[2].X && e.X <= ptThumbPoints2[0].X &&
    e.Y >= ptThumbPoints2[1].Y && e.Y <= ptThumbPoints2[0].Y)
    {
        bMouseEventThumb2 = true;
    }
}
private void RangeSelectorControl_MouseMove(object sender, 
             System.Windows.Forms.MouseEventArgs e)
{
    // If the Mouse is moved pressing the left button on Thumb1
    if (bMouseEventThumb1 && e.Button == 
          System.Windows.Forms.MouseButtons.Left && e.X >= fLeftCol )
    {
        // The below code is for handlling the Thumb1 Point
        if (strRange1.Equals(strRange2))
        {
            if (e.X < fThumb1Point)
            {
                fThumb1Point = e.X;
                OnPaint(ePaintArgs);
            }
        }
        else if (fThumb2Point - fWidthOfThumb > e.X)
        {
            fThumb1Point = e.X;
            OnPaint(ePaintArgs);
        }
        else
        {
            bMouseEventThumb1 = false;
        }
    } 
    //Else If the Mouse is moved pressing the left button on Thumb2
    else if (bMouseEventThumb2 && e.Button == 
             System.Windows.Forms.MouseButtons.Left && e.X <= fRightCol)
    {
        // The below code is for handlling the Thumb1 Point
        if (strRange1.Equals(strRange2))
        {    
            if (e.X > fThumb2Point)
            {
                fThumb2Point = e.X;
                OnPaint(ePaintArgs);
            }
        }
        else if (fThumb1Point + fWidthOfThumb < e.X)
        {
            fThumb2Point = e.X;
            OnPaint(ePaintArgs);
        }
        else
        {
            bMouseEventThumb2 = false;
        }
    }
    // If there is an Object Notification
    if (null != objNotifyClient)
    {
        objNotifyClient.Range1 = strRange1;
        objNotifyClient.Range2 = strRange2;
    }
}

/// <MouseEvents>
/// The below are the methods used for handling Mouse Events
/// </Mouse Events>
/// 
#endregion

最后一个区域是“通知类……”。此部分包含一个小的类,用户可以将其传递以获得活动事件通知。

/// <RangeSelectorControl_Resize>
/// The below is the small Notification class that can be used by the client
/// </RangeSelectorControl_Resize>
/// 
#region Notification class for client to register with the control for changes
public class NotifyClient
{
    private string strRange1, strRange2;
    public string Range1
    {
        set
        {
            strRange1 = value;
        }
        get
        {
            return strRange1;
        }
    }

    public string Range2
    {
        set
        {
            strRange2 = value;
        }
        get
        {
            return strRange2;
        }
    }
}
/// <RangeSelectorControl_Resize>
/// The Above is the small Notification class that can be used by the client
/// </RangeSelectorControl_Resize>
/// 
#endregion

历史

  • 2008 年 8 月 21 日 - 第一个版本。在“控件代码详解”部分添加了源代码片段。根据审稿人的建议添加了吸附功能。添加了带轻微动画的另一个版本。
© . All rights reserved.