You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何旋转/翻转自定义ToggleButton控件?解决垂直显示异常

自定义ToggleButton垂直旋转失效问题解决方法

问题描述

我基于CheckBox自定义了ToggleButton控件,添加了IsVertical属性用于切换水平/垂直显示:

  • 当IsVertical=false时,控件默认水平显示
  • 当IsVertical=true时,控件及内部绘制内容应旋转90度

但设置IsVertical=true后,控件仍保持水平状态,内部图形变为方形,旋转效果未达到预期。相关代码如下:

控件定义

public class ToggleButton : CheckBox
{
    private bool _isVertical = false;

    [Category("Custom Appearance")]
    public bool IsVertical
    {
        get => _isVertical;
        set
        {
            _isVertical = value;
            Invalidate();
        }
    }
}

OnPaint方法

protected override void OnPaint(PaintEventArgs pevent)
{
    base.OnPaint(pevent);
    Graphics g = pevent.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    // Handle rotation if vertical
    if (_isVertical)
    {
        g.TranslateTransform(Width / 2, Height / 2);
        g.RotateTransform(90);
        g.TranslateTransform(-Width / 2, -Height / 2);
    }

    // Draw the background
    Rectangle backgroundRect = new Rectangle(0, 0, Width, Height);
    g.FillRectangle(new SolidBrush(currentBackColor), backgroundRect);

    // Draw the toggle
    int toggleSize = Math.Min(Width, Height) - 4; // Toggle size
    int toggleX = (int)currentPosition;
    int toggleY = (Height - toggleSize) / 2; // Center vertically
    g.FillEllipse(new SolidBrush(currentToggleColor), toggleX, toggleY, toggleSize, toggleSize);

    // Reset transformations if vertical
    if (_isVertical)
    {
        g.ResetTransform();
    }

    // Optional: Draw border
    using (Pen borderPen = new Pen(Color.Black, 1))
    {
        g.DrawRectangle(borderPen, 0, 0, Width - 1, Height - 1);
    }
}

问题根源与解决方案

问题核心

  1. base.OnPaint在变换前调用,导致默认CheckBox内容未被旋转,干扰自定义绘制
  2. 提前重置变换,使边框绘制未应用旋转效果
  3. 旋转后未适配宽高互换的坐标系,导致绘制区域计算错误
  4. Toggle的位置逻辑未针对垂直模式调整

修正步骤

1. 调整base.OnPaint调用(或移除)

如果完全自定义绘制,直接移除base.OnPaint(pevent)避免默认内容干扰;若需保留CheckBox默认行为,将其移到变换逻辑之后调用。

2. 修正旋转变换逻辑

旋转90度后,控件宽高逻辑互换,需调整变换原点:

if (_isVertical)
{
    // 平移到控件中心,旋转后再平移到新坐标系原点
    g.TranslateTransform(Width / 2, Height / 2);
    g.RotateTransform(90);
    // 旋转后原Height变为Width,原Width变为Height,调整平移量
    g.TranslateTransform(-Height / 2, -Width / 2);
}

3. 适配旋转后的绘制区域

旋转后背景矩形的宽高需互换:

Rectangle backgroundRect = _isVertical 
    ? new Rectangle(0, 0, Height, Width) 
    : new Rectangle(0, 0, Width, Height);

4. 修正Toggle位置计算

垂直模式下,Toggle的移动方向变为垂直,调整坐标逻辑:

int toggleSize = Math.Min(Width, Height) - 4;
int toggleX, toggleY;

if (_isVertical)
{
    // 垂直模式:X坐标居中,Y坐标由currentPosition控制
    toggleX = (Height - toggleSize) / 2;
    toggleY = (int)currentPosition;
}
else
{
    // 水平模式:原逻辑不变
    toggleX = (int)currentPosition;
    toggleY = (Height - toggleSize) / 2;
}

5. 延迟重置变换

将ResetTransform移到所有绘制操作完成后,确保边框也应用旋转:

// 先完成所有绘制操作
// ...

// 最后重置变换
if (_isVertical)
{
    g.ResetTransform();
}

6. 同步控件尺寸(可选)

当IsVertical变化时,交换控件宽高让布局更合理:

[Category("Custom Appearance")]
public bool IsVertical
{
    get => _isVertical;
    set
    {
        if (_isVertical != value)
        {
            _isVertical = value;
            // 交换宽高
            var temp = Width;
            Width = Height;
            Height = temp;
            Invalidate();
        }
    }
}

修正后的完整OnPaint代码

protected override void OnPaint(PaintEventArgs pevent)
{
    // 移除base.OnPaint避免默认内容干扰,如需保留可移到变换之后
    // base.OnPaint(pevent);
    Graphics g = pevent.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    bool isVertical = _isVertical;
    if (isVertical)
    {
        g.TranslateTransform(Width / 2, Height / 2);
        g.RotateTransform(90);
        g.TranslateTransform(-Height / 2, -Width / 2);
    }

    // 绘制背景
    Rectangle backgroundRect = isVertical 
        ? new Rectangle(0, 0, Height, Width) 
        : new Rectangle(0, 0, Width, Height);
    using (var backBrush = new SolidBrush(currentBackColor))
    {
        g.FillRectangle(backBrush, backgroundRect);
    }

    // 绘制Toggle
    int toggleSize = Math.Min(Width, Height) - 4;
    int toggleX, toggleY;
    if (isVertical)
    {
        toggleX = (Height - toggleSize) / 2;
        toggleY = (int)currentPosition;
    }
    else
    {
        toggleX = (int)currentPosition;
        toggleY = (Height - toggleSize) / 2;
    }
    using (var toggleBrush = new SolidBrush(currentToggleColor))
    {
        g.FillEllipse(toggleBrush, toggleX, toggleY, toggleSize, toggleSize);
    }

    // 绘制边框
    using (Pen borderPen = new Pen(Color.Black, 1))
    {
        g.DrawRectangle(borderPen, backgroundRect.X, backgroundRect.Y, backgroundRect.Width - 1, backgroundRect.Height - 1);
    }

    // 重置变换
    if (isVertical)
    {
        g.ResetTransform();
    }
}

内容的提问来源于stack exchange,提问作者ShortyMelon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 18:08:11