控制边界外的自定义绘制下拉面板

Custom draw dropdown panel outside Control bounds(控制边界外的自定义绘制下拉面板)
本文介绍了控制边界外的自定义绘制下拉面板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我似乎拿到了一张"不清楚我在问什么"的选票。我想自定义绘制一个组合框样式的控件。弹出打开部分需要在控件本身的边界之外绘制。我不能使用组合框-想象一下类似于Word功能区中的图库控件。

我想了两种方法:

  • 将弹出的打开面板向上传递到要呈现的窗体。
  • 使用无边框、无框架窗体或NativeWindow。

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

有没有其他方法可以解决这个问题?您认为哪种方法最好?

谢谢。


原问题:

我正在为一个小项目编写一个基于WinForms的自定义绘图UI库。大体上一切都很顺利,但我在离开Graphics对象边界的下拉列表中遇到了一些轻微的结构问题。

大多数控件使用纯自定义绘制和重绘事件模型完成,但总体界面使用WinFormsDockWidthHeight等进行布局。

我已经添加了一个下拉列表,但很明显,当它的下拉列表的边界超过布局的图形对象的边界时,它就会被切断。

(我本希望在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)
    {
    }
}

很明显,这里遗漏了很多内容,但它至少应该显示我正在使用的方法。

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

谢谢


更新:

明确地说,问题是绘制下拉列表的"Popup"部分,而不是下拉列表本身。(此处使用组合框进行演示)

我还记得,小窗口会强制组合框超出窗口的边界。

其上的投影在我看来与CreateParams中的CS_DROPSHADOW可疑-我是否可以使用NativeWindow子类来处理此问题?

推荐答案

我想我已经确定了第二个选项,它使用第二个表单来显示下拉面板。我使用的是扩展的窗体类,而不是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;
            }
        }

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

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

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

现在更烦人了,但非常欢迎您提出解决这个问题的建议!

这篇关于控制边界外的自定义绘制下拉面板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)