【问题标题】:Keeping Adorner Glyphs On Top for Selected Control in Winforms Designer在 Winforms Designer 中为选定控件保留装饰字形
【发布时间】:2020-01-29 11:03:25
【问题描述】:

TLDR:我正在寻找正确的方法来为设计表面上的特定控件的并列图形的渲染计时,以便在选择该控件时始终在装饰字形之前绘制图形。

这个问题与 Winforms 的控件设计者有关:当用户将控件放置在设计图面上时,我想在控件的客户区上方显示一个图形。通过覆盖其 OnPaint 事件处理程序,然后使用可用于绘制桃色矩形的 e.Graphics 对象,我在某种程度上成功地为 TableLayoutPanel (TLP) 控件做到了这一点。下面是显示结果的图像:跨越控件宽度且高 35 像素的绘制图形 - 请记住,这是放置在设计图面上的控件的设计器实例(使用 BasicLoader 创建):

但是,在设计器中,如果我调整控件的大小,图形总是在调整大小的字形(上面有北/南和西/东箭头的字形)下方结束:

我尝试创建和维护各种布尔标志以在某些情况下抑制 OnPaint 消息。例如,我设置了一个标志来指示控件刚刚调整大小(要了解我是如何做到的,请参阅我最近的问题:BeginResize/EndResize Event for Control on WinForms Design Surface)以抑制图形的绘制,但这不起作用,因为在我清除标志后,不可避免地会引发 OnPaint 事件。我不想用我尝试使用/设置它们的所有标志和位置的详细信息来回答这个问题,但我只想说我煞费苦心地花了几个小时进行实验——但无济于事。我已经得出结论,一定有更好的方法。

当我绘制图形时,如何确保字形保持在顶部?

谢谢!

【问题讨论】:

  • 我的通灵调试器说 OnPaint() 不使用 e.Graphics 进行绘画。
  • 嗨,汉斯,我也是。作为记录,我使用的是 TLP 父级的 CreateGraphics 方法返回的 Graphics 对象——我没有使用 TLP 重写的 Paint 方法中直接可用的 e.Graphics。 TLP 图形需要知道 TLP 何时失效,以便它也可以重新绘制自己……我正在寻找一种更好的方法来确定绘制此图形的时间,以便在装饰显示在选定的子类 TLP 控件上之前触发它在设计器中。
  • 别这样。添加 this.ResizeRedraw = true;到构造函数。

标签: c# .net winforms windows-forms-designer design-surface


【解决方案1】:

我能想到几个解决方案,包括以下几种:

  • 使用TableLayoutPanel 的填充
  • 使用装饰器和字形
  • 创建具有标题和可编辑内容的自定义面板

我认为第一个解决方案很适合您,但其他解决方案也有一些要点。

我也可以想到一个基于NativeWindow 的解决方案,就像在ErrorProvider 中实现的那样,但是它使帖子太长了,而现有的选项已经足够好了。所以,如果你想追求这个想法,我把它留给你。

解决方案 1 - 使用 TableLayoutPanel 的填充

此解决方案适用于设计时和运行时

TableLayoutPanel 有一个Padding 属性,它的布局引擎尊重填充井。您可以使用填充区域来呈现您想要的任何内容:

public class MyTLP : TableLayoutPanel
{
    public MyTLP()
    {
        Padding = new Padding(0, 30, 0, 0);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.FillRectangle(Brushes.Orange,
            new Rectangle(0, 0, ClientRectangle.Width, Padding.Top));
    }
}

解决方案 2 - 使用 Adorner 和 Glyph

此解决方案仅用于设计时

对于设计时渲染,我将使用AdornerGlyph 处理它。

如果我在创建我的自定义设计器,所有代码都属于控件设计器,但是由于您不想为TableLayoutPanel创建新的控件设计器,那么和我injected a new custom action in the action lists一样,这里我'会得到BehaviorService,我会在设计时向控件的装饰器注入一个装饰器,这将是结果:

其行为与其他字形非常相似,它会在控件调整大小时自动调整大小,您无需在设计时执行任何特定操作来处理调整大小。

请注意:这是一个设计时的解决方案,而绘画只是在设计师那里完成。如果您需要运行时解决方案,则需要完全不同的解决方案。

代码如下:

using System;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
public class MyTLP : TableLayoutPanel
{
    private IDesignerHost designerHost;
    private BehaviorService behaviorService;
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if (DesignMode && Site != null)
        {
            designerHost = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
            behaviorService = designerHost?.GetService(typeof(BehaviorService))
                as BehaviorService;
            if (behaviorService != null)
            {
                var adorner = new Adorner();
                behaviorService.Adorners.Insert(0, adorner);
                adorner.Glyphs.Add(new MyTLPGlypg(behaviorService, this));
            }
        }
    }
}
class MyTLPGlypg : Glyph
{
    Control control;
    BehaviorService behaviorSvc;
    public MyTLPGlypg(BehaviorService behaviorSvc, Control control) :
        base(new MyBehavior())
    {
        this.behaviorSvc = behaviorSvc;
        this.control = control;
    }
    public override Rectangle Bounds
    {
        get
        {
            var edge = behaviorSvc.ControlToAdornerWindow(control);
            var h = 30;
            return new Rectangle(edge.X, edge.Y - h, control.Size.Width, h);
        }
    }
    public override Cursor GetHitTest(Point p)
    {
        //Uncomment if you want to attach a specific behavior
        //if (Bounds.Contains(p)) return Cursors.Hand;
        return null;
    }
    public override void Paint(PaintEventArgs pe)
    {
        pe.Graphics.FillRectangle(Brushes.Orange, Bounds);
    }
}
class MyBehavior : Behavior
{
    public override bool OnMouseUp(Glyph g, MouseButtons button)
    {
        //Do something and return true, meand eventhandled
        return true;
    }
}

注意:

要详细了解锚点、字形和行为,请查看以下链接:

解决方案 3 - 创建自定义面板,具有标题和可编辑内容

您可以创建具有标题和可编辑内容的自定义面板。然后在设计时禁止用户将内容放在标题部分:

为此,您需要创建一个新设计器,通过调用EnableDesignMode 方法在设计时启用内部面板。然后对于内部面板,您需要创建一个设计器,该设计器禁用移动、调整大小并从设计器中删除一些属性。

我已经在这里发布了详细的答案:UserControl with header and content - Allow dropping controls in content panel and Prevent dropping controls in header at design time

【讨论】:

  • 嗨,Reza,一如既往,非常感谢。解决方案 1 的评论:我曾尝试做类似的事情,但遇到了一个问题:当我嵌套控件时,橙色字形开始表现得很奇怪,我需要这样做。例如,如果我将它放在另一个 TLP 的单元格中,那么字形就会溢出到上面的单元格中......另外,在你的例子中,如果你将控件拖到表单的顶部,橙色矩形是否会在顶部表单的标题栏与我尝试自己的方法时一样吗?
  • 解决方案 2 的命令:哇,这实际上是一个了不起的想法——我没想到!!!这可能只是我需要的绝妙的解决所有问题的想法,因为它提供了一个像所有者绘制的控件一样的选取框,即使我无法访问它的设计器!我将尝试这个想法——哇!谢谢!!!
  • 橙色矩形最终出现在表单标题栏的顶部 → 这是预期的行为,与其他字形出现在顶部的方式相同。总的来说,我分享了解决方案 1 作为示例,以展示如何在设计时使用字形。
  • 您还可以查看我的链接帖子,描述解决方案 3。
  • 嗨 Reza,我在进行研究时查看了您的链接帖子。我会在那里发表评论...谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 2022-09-29
相关资源
最近更新 更多