Custom draw dropdown panel outside Control bounds(控制边界外的自定义绘制下拉面板)
问题描述
我似乎拿到了一张"不清楚我在问什么"的选票。我想自定义绘制一个组合框样式的控件。弹出打开部分需要在控件本身的边界之外绘制。我不能使用组合框-想象一下类似于Word功能区中的图库控件。
我想了两种方法:
- 将弹出的打开面板向上传递到要呈现的窗体。
 - 使用无边框、无框架窗体或NativeWindow。
 
后者还允许下拉菜单脱离窗口的边界,这可能有用,但不是绝对必要的。
有没有其他方法可以解决这个问题?您认为哪种方法最好?
谢谢。
原问题:
我正在为一个小项目编写一个基于WinForms的自定义绘图UI库。大体上一切都很顺利,但我在离开Graphics对象边界的下拉列表中遇到了一些轻微的结构问题。
大多数控件使用纯自定义绘制和重绘事件模型完成,但总体界面使用WinFormsDock、Width、Height等进行布局。
(我本希望在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处理程序就不起作用了。
现在更烦人了,但非常欢迎您提出解决这个问题的建议!
这篇关于控制边界外的自定义绘制下拉面板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:控制边界外的自定义绘制下拉面板
				
        
 
            
        基础教程推荐
- 将事件 TextChanged 分配给表单中的所有文本框 2022-01-01
 - 首先创建代码,多对多,关联表中的附加字段 2022-01-01
 - 从 VS 2017 .NET Core 项目的发布目录中排除文件 2022-01-01
 - 经典 Asp 中的 ResolveUrl/Url.Content 等效项 2022-01-01
 - 在 VS2010 中的 Post Build 事件中将 bin 文件复制到物 2022-01-01
 - 错误“此流不支持搜索操作"在 C# 中 2022-01-01
 - 是否可以在 asp classic 和 asp.net 之间共享会话状态 2022-01-01
 - 如何动态获取文本框中datagridview列的总和 2022-01-01
 - JSON.NET 中基于属性的类型解析 2022-01-01
 - 全局 ASAX - 获取服务器名称 2022-01-01
 
    	
    	
    	
    	
    	
    	
    	
    	
						
						
						
						
						
				
				
				
				