C# .NET 2.0 缩放滚动条

3
我正在寻找一个控件,允许用户同时缩放和滚动。它基本上需要像Sony Sound Forge一样,如下图所示: enter image description here 它看起来和行为类似于普通的滚动条,但额外增加了拖动边缘到左右使图表放大/缩小的功能。即使用户可以通过其他方法进行缩放和滚动(例如通过在图表本身上拖动区域),我仍然认为这种组件很有用,因为它直接向用户提供了数据位置和应用缩放量的直观反馈。
是否存在这样的组件,还是我需要自己创建?任何帮助都受欢迎。

你在那个容器中使用了哪些控件? - Mert Susur
我打算使用一个类似于上面截图中的图形显示。这回答了你的问题吗?因为我认为这并不相关。缩放滚动条必须简单地提供两个浮点值,例如0到1之间的每次用户缩放或滚动时,这些值表示栏的起始和结束位置。(上面的屏幕截图是来自Sony的音频编辑工具Sound Forge,而不是来自我的软件) - Bart Gijssens
我认为你可以通过你的组件解决这个问题,我认为在每次缩放时你需要增加精度。 - Mert Susur
@lazycider:我觉得我们的理解有些偏差,至少我完全没听懂你的回答。我正在寻找像截图中那样的UI控件。当然,这样一个控件背后的逻辑并不是什么高深的科学。如我所描述的,它只需要在用户操作控件时返回2个值。我只是想知道是否存在这样的UI组件。当然,我也可以自己创建一个,但要让它看起来和 Windows 一致,那将是相当繁琐的工作。 - Bart Gijssens
1个回答

2

我认为这是非常特定的控件,我不认为它存在。此外,我认为自己创建它会更快,而不是寻找它,甚至等待别人回答这个问题。

要绘制类似于Sound Forge中的自定义按钮,您可以使用WinAPI函数DrawThemeBackground

我做了一个简短的例子:

enter image description here

public partial class Form1 : Form
{
    readonly Int32 ScrollBarWidth;
    readonly Int32 ScrollBarHeight;



    public Form1()
    {
        InitializeComponent();

        ScrollBarWidth  = GetSystemMetrics(SM_CYVSCROLL);
        ScrollBarHeight = GetSystemMetrics(SM_CYHSCROLL);
    }



    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        Int32 ZoomMarkerSize = 6;

        Graphics G      = e.Graphics;
        Int32 SBWidth   = ScrollBarWidth;
        Int32 SBHeight  = ScrollBarHeight;

        DrawCustomScrollButton(G, 0, 0, SBWidth, SBHeight, Resources.Plus,
            (int) SCROLLBARSTYLESTATES.SCRBS_NORMAL);
        DrawCustomScrollButton(G, SBWidth, 0, ZoomMarkerSize, SBHeight, null,
            (int) SCROLLBARSTYLESTATES.SCRBS_NORMAL);
        DrawCustomScrollButton(G, SBWidth + ZoomMarkerSize, 0, SBWidth, SBHeight, Resources.Minus,
            (int) SCROLLBARSTYLESTATES.SCRBS_HOT);
    }



    public void DrawCustomScrollButton (Graphics aG, Int32 aX, Int32 aY, Int32 aWidth, Int32 aHeight,
        Image aImage, Int32 aState)
    {
        RECT R = new RECT () { left = aX, top = aY, right = aX + aWidth, bottom = aY + aHeight };
        RECT NotUsed = R;

        IntPtr ThemeHandle  = OpenThemeData(this.Handle, "SCROLLBAR");
        IntPtr HDC          = aG.GetHdc();

        DrawThemeBackground
        (
            ThemeHandle, HDC,
            (int) SCROLLBARPARTS.SBP_THUMBBTNHORZ,
            aState,
            ref R, ref NotUsed
        );

        aG.ReleaseHdc(HDC);
        CloseThemeData(ThemeHandle);

        if (aImage != null)
        {
            aG.DrawImage(aImage,
                aX + ((ScrollBarHeight - aImage.Width   ) / 2),
                aY + ((ScrollBarHeight - aImage.Height  ) / 2));
        }
    }



    public struct RECT
    {
        public Int32 left; 
        public Int32 top; 
        public Int32 right; 
        public Int32 bottom; 
    }

    [DllImport("user32.dll")]
    public static extern int GetSystemMetrics(int smIndex);

    [DllImport("uxtheme.dll", ExactSpelling=true)]
    public extern static Int32 DrawThemeBackground(IntPtr hTheme, IntPtr hdc, int iPartId,
       int iStateId, ref RECT pRect, ref RECT pClipRect);

    [DllImport("uxtheme.dll", ExactSpelling=true, CharSet=CharSet.Unicode)]
    public static extern IntPtr OpenThemeData(IntPtr hWnd, String classList);

    [DllImport("uxtheme.dll", ExactSpelling=true)]
    public extern static Int32 CloseThemeData(IntPtr hTheme);

    public int SM_CYHSCROLL = 3;
    public int SM_CYVSCROLL = 20;

    public int SBP_ARROWBTN = 1;

    public int ABS_UPNORMAL = 1;
    public int ABS_UPHOT = 2;
    public int ABS_UPHOVER = 17;

    public enum ARROWBTNSTATES {
        ABS_UPNORMAL = 1,
        ABS_UPHOT = 2,
        ABS_UPPRESSED = 3,
        ABS_UPDISABLED = 4,
        ABS_DOWNNORMAL = 5,
        ABS_DOWNHOT = 6,
        ABS_DOWNPRESSED = 7,
        ABS_DOWNDISABLED = 8,
        ABS_LEFTNORMAL = 9,
        ABS_LEFTHOT = 10,
        ABS_LEFTPRESSED = 11,
        ABS_LEFTDISABLED = 12,
        ABS_RIGHTNORMAL = 13,
        ABS_RIGHTHOT = 14,
        ABS_RIGHTPRESSED = 15,
        ABS_RIGHTDISABLED = 16,
        ABS_UPHOVER = 17,
        ABS_DOWNHOVER = 18,
        ABS_LEFTHOVER = 19,
        ABS_RIGHTHOVER = 20,
    };

    public enum SCROLLBARSTYLESTATES {
        SCRBS_NORMAL = 1,
        SCRBS_HOT = 2,
        SCRBS_PRESSED = 3,
        SCRBS_DISABLED = 4,
        SCRBS_HOVER = 5,
    };

    public enum SCROLLBARPARTS {
        SBP_ARROWBTN = 1,
        SBP_THUMBBTNHORZ = 2,
        SBP_THUMBBTNVERT = 3,
        SBP_LOWERTRACKHORZ = 4,
        SBP_UPPERTRACKHORZ = 5,
        SBP_LOWERTRACKVERT = 6,
        SBP_UPPERTRACKVERT = 7,
        SBP_GRIPPERHORZ = 8,
        SBP_GRIPPERVERT = 9,
        SBP_SIZEBOX = 10,
    };
}

这里有表单源文件和两个测试PNG图像资源:
scrollbar-with-zoom-example.zip

请查看MSDN获取有关此API的其他信息,并在PINVOKE.NET网站上查看函数签名。

编辑

更好的解决方案是使用VisualStyleRenderer类。对于第一个复杂的示例,我很抱歉。我以前不知道这个类。因此,应该替换我的DrawCustomScrollButton函数:

public void DrawCustomScrollButton (Graphics aG, Int32 aX, Int32 aY, Int32 aWidth, Int32 aHeight,
    Image aImage, Int32 aState)
{
    Rectangle R = new Rectangle(aX, aY, aX + aWidth, aY + aHeight);

    VisualStyleRenderer Renderer = new VisualStyleRenderer
    (
        VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Normal
    );

    Renderer.DrawBackground(aG, R);

    if (aImage != null)
    {
        aG.DrawImage(aImage,
            aX + ((ScrollBarHeight - aImage.Width   ) / 2),
            aY + ((ScrollBarHeight - aImage.Height  ) / 2));
    }
}

编辑2

考虑到您的评论,我尝试重新创建这个自定义控件。主要思路是使用标准的ScrollBar,并在其上方放置缩放标记(两个Panel控件)。乍一看似乎很容易。但是将这些组件组合在一起比我想象的更困难。

主要问题是难以改变系统ScrollBar控件的行为。我发现它不允许处理我想要覆盖的Windows消息队列中的某些事件。例如MouseUp事件和其他一些事件。

最终,我认为唯一正确的方法是从头开始重新创建ScrollBar控件的副本,其中不会有这些限制。因此,最初我走了错误的路线,不幸地没有解决您的任务。

无论如何,这是我的当前结果(完整解决方案): scrollbar-with-zoom-3.zip


我不介意+和-按钮。实际上,我只需要两个只有+和-文本的按钮就可以了。它们可以与滚动条集成到自定义控件中。但这并不是主要问题。主要问题是滚动条本身必须使用户能够允许缩放和滚动。滚动将像任何正常的滚动条一样进行,缩放将通过将栏(或拇指,某些人称之为)的一侧向另一侧拖近或远离来完成。 - Bart Gijssens
如果你仔细观察这个例子,你会发现这个滚动条每边有两个夹爪,而不是像普通的滚动条一样只有一个在中间。 - Bart Gijssens
现在我明白了。过去我使用的是这个程序的旧版本,没有完全理解你的问题。很遗憾你没有在截图上标注出你需要的内容。今天我安装了Sound Forge的试用版,并看到了新的滚动条是如何工作的。所以当我有时间时,我会尝试找到更正确的答案来回答你的问题。如果我成功了,我会进行更新。 - Dima Zorin
1
今天我做了一个新的例子,但它还没有完成并且有一些错误。如果你想看看我的当前结果,这是我的解决方案:scrollbar-with-zoom-2.zip - Dima Zorin
Dmitry,这看起来绝对精彩,正是我想要的。 - Bart Gijssens
首先,感谢您的赞赏。不幸的是,我没有完全完成您的任务。我在我的解决方案中整理了事情,并使用UserControl类创建了自定义控件(请参见HScrollBarZ控件)。做出了一些改进,但仍有一些问题没有解决。无论如何,请检查更新后答案中的下载链接。是否有取消“已接受答案”的能力?如果是的话,我认为您应该这样做。也许会有人为您提供更正确的答案。 - Dima Zorin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接