c# - 自定义绘制下拉面板在控制范围之外

标签 c# winforms graphics gdi+ gdi

我似乎获得了“不清楚我在问什么”的投票。我想自定义绘制一个组合框样式控件。弹出打开部分需要在控件本身的边界之外绘制。我无法使用组合框 - 想想类似于 Word 功能区中的库控件的东西。

我想到了两种方法:

  • 将弹出打开的面板传递回表单以进行渲染。
  • 使用无边框、无框架 Form 或 NativeWindow。

后者还允许下拉菜单脱离窗口的边界,这可能有用,但不是绝对必要的。

还有其他方法吗?您认为哪种方法最好?

谢谢。


原始问题:

我正在为一个小项目编写一个基于 winforms 的自定义绘制 UI 库。一切都基本上进展顺利,但我有一个轻微的结构问题,下拉菜单离开了 Graphics 对象的边界。

大多数控件都是使用纯自定义绘制和重绘事件模型完成的,但整体界面是使用 winforms DockWidthHeight 进行布局的 等等

我添加了一个下拉菜单,但显然当它的下拉部分的边界超出布局Panel的图形对象的边界时,它会被切断。

(我本来希望在 SO 上找到类似的东西,但没有成功。)

我已经通过让表单控制下拉覆盖的绘制来解决这个问题,但是使用自定义鼠标处理程序和其他所有内容,表单开始感觉负担过重。

我尝试存储对 Graphics 对象的引用,但发现在引发的 OnPaint 之外使用它们......脾气暴躁。

当前模型的简化代码示例如下。此代码不会单独以任何有用的方式运行,但显示了用于显示叠加层的方法。

public interface IDropDownOverlay
{
    DropDown DropDown { get; }

    /// <summary>can only link to a single form at once - not a problem.</summary>
    DropDownDrawForm Form { get; set; }

    void MouseUpdate(MouseEventArgs e);

    void Render(Graphics gfx);

    void Show();
}

public class DropDown
{
    private DropDownOverlay overlay;
}

public class DropDownDrawForm : Form
{
    /* lots of other things... */

    private List<IDropDownOverlay> overlays;

    public void HideOverlay(IDropDownOverlay overlay)
    {
        if (this.overlays.Contains(overlay))
        {
            this.overlays.Remove(overlay);
            this.Invalidate();
        }
    }

    public void ShowOverlay(IDropDownOverlay overlay)
    {
        if (!this.overlays.Contains(overlay))
        {
            overlay.Form = this;
            this.overlays.Add(overlay);
            this.Invalidate();
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        foreach (IDropDownOverlay overlay in this.overlays)
        {
            overlay.Render(e.Graphics);
        }
    }

    private void MouseUpdate(MouseEventArgs e)
    {
        foreach (IDropDownOverlay overlay in this.overlays)
        {
            overlay.MouseUpdate(e);
        }
    }
}

public class DropDownOverlay : IDropDownOverlay
{
    public DropDown DropDown { get; }

    public DropDownDrawForm Form { get; set; }

    public void Hide()
    {
        this.Form.HideOverlay(this);
    }

    public void MouseUpdate(MouseEventArgs e)
    {
        // Informs the form to redraw the area of the control.

        if (stateChanged)
        {
            this.Invalidate(); // (method to invalidate just this area)
        }
    }

    public void Show()
    {
        this.Form.ShowOverlay(this);
    }

    public void Render(Graphics gfx)
    {
    }
}

显然,其中缺少很多内容,但它至少应该显示我正在使用的方法。

有什么建议可以防止我在表单之间来回传递此信息吗?

谢谢


更新:

为了绝对清楚,问题是绘制下拉列表的“弹出”部分,而不是下拉列表本身。 (这里使用ComboBox来演示)

DropDown Control Areas

我还记得小窗口会迫使 ComboBox 超出窗口的范围。

ComboBox outside window bounds

对我来说,它上面的阴影看起来很像来自 CreateParamsCS_DROPSHADOW - 我可以使用 NativeWindow 子类来处理这个问题吗?

最佳答案

我想我已经选择了第二个选项,即使用第二个表单来显示下拉面板。我使用了扩展 Form 类而不是 NativeWindow。只是觉得我应该分享结果,以防其他人尝试同样的事情并发现这个。

当选择下拉列表时,我使用PointToScreen设置表单来获取坐标。它还设置了以下属性:

            this.ShowIcon = false;
            this.ControlBox = false;
            this.MinimizeBox = false;
            this.MaximizeBox = false;
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;

只是为了确保它不会出现在任何地方。我还添加了以下事件处理程序:

            this.LostFocus += delegate
            {
                this.dropdown.BlockReopen(200);
                this.dropdown.Close();
            };

这意味着一旦失去焦点它就会关闭,并且还会调用一个方法来阻止下拉菜单重新打开 200 毫秒。我对此并不完全满意,但解决了很多问题,它可能会持续一段时间。我还通过重写 CreateParams 添加阴影:

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams createParams = base.CreateParams;
                createParams.ClassStyle |= Win32Message.CS_DROPSHADOW;
                return createParams;
            }
        }

我的快速测试台应用程序的最终结果:

Ribbon Dropdown Prototype

通过这种方式,我也可以使下拉菜单脱离窗口的边界:

Ribbon Dropdown Prototype

我现在唯一的问题 - 当你打开每个窗口时,窗框会失去焦点,这有点刺耳。我可以通过覆盖 ShowWithoutActivation 返回 true 来解决这个问题,但是 LostFocus 处理程序不起作用。

现在更烦人了,但是非常欢迎任何解决这个问题的建议!

关于c# - 自定义绘制下拉面板在控制范围之外,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28142305/

相关文章:

linux - 侏儒图标包

c# - 在打印作业期间更换打印机托盘

c# - 是否可以在 ASP.NET Core MVC 应用程序中运行 Blazor 客户端页面?

.net - Winforms ComboBox SelectedValueChange 事件

python - 在pyglet(python)中按像素绘制

php - 将渲染的 HTML 保存为图像

c# - 尝试从 C# 在事件目录中设置 "User Cannot Change Password"时出现约束冲突

c# - 输入数据库时​​在文本框中拆分文本

c# - C#如何使用操作系统的复制粘贴功能

html - 使用图像标签和 CSS 显示本地 HTML 文件的 C# 控件/代码