在 Winforms 设计器中将 Adorner Glyphs 保持在选定控件的顶部 [英] Keeping Adorner Glyphs On Top for Selected Control in Winforms Designer

查看:43
本文介绍了在 Winforms 设计器中将 Adorner Glyphs 保持在选定控件的顶部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

我尝试创建和维护各种布尔标志以在某些情况下抑制 OnPaint 消息.例如,我设置了一个标志来指示刚刚调整了控件的大小(要了解我是如何做到的,请参阅我最近的问题:

公共类 MyTLP : TableLayoutPanel{公共 MyTLP(){填充 = 新填充(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 - 使用装饰器和字形

<块引用>

此解决方案仅用于设计时

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

如果我创建的是自定义设计器,所有代码都属于控件设计器,但是既然您不想为 TableLayoutPanel 创建新的控件设计器,那么我

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

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

代码如下:

使用系统;使用 System.ComponentModel.Design;使用 System.Drawing;使用 System.Windows.Forms;使用 System.Windows.Forms.Design;使用 System.Windows.Forms.Design.Behavior;公共类 MyTLP:TableLayoutPanel{私人 IDesignerHost 设计师Host;私有行为服务行为服务;protected override void OnHandleCreated(EventArgs e){base.OnHandleCreated(e);if (DesignMode && Site != null){DesignerHost = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;行为服务=designerHost?.GetService(typeof(BehaviorService))作为行为服务;如果(行为服务!= null){var 装饰器 = 新装饰器();行为服务.Adorners.Insert(0, 装饰器);adorner.Glyphs.Add(new MyTLPGlypg(behaviorService, this));}}}}MyTLPGlypg 类:字形{控制控制;BehaviorServicebehaviorSvc;public MyTLPGlypg(BehaviorService behaviorSvc, Control control) :基地(新我的行为()){this.behaviorSvc = behaviorSvc;this.control = 控制;}公共覆盖矩形边界{得到{var edge = behaviorSvc.ControlToAdornerWindow(control);无功h = 30;返回新矩形(edge.X,edge.Y - h,control.Size.Width,h);}}公共覆盖 Cursor GetHitTest(Point p){//取消注释是否要附加特定行为//if (Bounds.Contains(p)) return Cursors.Hand;返回空;}公共覆盖无效油漆(PaintEventArgs pe){pe.Graphics.FillRectangle(Brushes.Orange, Bounds);}}MyBehavior 类:行为{public override bool OnMouseUp(Glyph g, MouseButtons button){//做某事并返回true,意味着事件处理返回真;}}

注意:

要了解有关锚点、字形和行为的更多信息,请查看以下链接:

  • TLDR: I'm seeking the correct method for timing the rendering of a juxtaposed graphic for a particular control on a design surface so that the graphic always is painted ahead of the adornment glyphs when that control is selected.

    This question concerns control designers for Winforms: When the user places a control on the design surface, I want to display a graphic above the client area of the control. I have succeeded to some extent doing that for a TableLayoutPanel (TLP) control by overriding its OnPaint event handler then using the e.Graphics object available to paint a peach-colored rectangle. Below is an image showing the results: a painted graphic that spans the width of the control and is 35 pixels high--remember, this is a designer instance of a control placed on a design surface (created with a BasicLoader):

    However, within the designer, if I resize the control, the graphic always ends up below the resize glyph (the glyph that has the North/South and West/East arrows on it):

    I've tried creating and maintaining various Boolean flags to suppress the OnPaint message under certain circumstances. For instance, I set a flag to indicate that the control was just resized (to see how I did that, see my recent question: BeginResize/EndResize Event for Control on WinForms Design Surface) in order to suppress the painting of the graphic, but that didn't work because an OnPaint event is inevitably raised after I've cleared a flag. I don't want saddle this question with details of all the flags and places I tried to use/set them but suffice it to say that I painstakingly spent hours experimenting--to no avail. I've concluded that there must be a better way.

    How can I ensure that the glyphs remain on top when I paint my graphics?

    Thank you!

    解决方案

    I can think of a few solutions, including the followings:

    • Using Padding of the TableLayoutPanel
    • Using Adorner and Glyph
    • Creating a custom panel, having header and editable content

    I think the first solution will suit you well, however the other solutions also some points.

    I can also think of a solution based on NativeWindow like what has been implemented in ErrorProvider, but It makes the post toooooo lengthy while the existing options are good enough. So I leave it to you if you like to pursue the idea.

    Solution 1 - Using Padding of the TableLayoutPanel

    This solution is for both design-time and run-time

    TableLayoutPanel has a Padding property and its layout engine respects to the padding well. You can use the padding area to render whatever you want:

    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));
        }
    }
    

    Solution 2 - Using Adorner and Glyph

    This solution is just for design-time

    For design-time rendering, I'll handle it using Adorner and Glyph.

    If I was creating my custom designer, all the code belong to the control designer, but since you don't want to create a new control designer for TableLayoutPanel, then the same way that I injected a new custom action in the action lists, here I'll get BehaviorService and I'll inject an adorner to the adorners of the control at design time and this will be the result:

    The behavior is quite similar to the other glyphs, it will be resized automatically when the control resizes and you don't need to do anything specific to handle the resize at design time.

    Please note: It's a design-time solution and the painting is just being done at designer. In case you need a run-time solution, you need a totally different solution.

    Here is the code:

    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;
        }
    }
    

    Note:

    To learn more about anchors, glyphs and behavior, take a look at the following links:

    Solution 3 - Creating a custom panel, having header and editable content

    You can create a custom panel having header and editable content. Then at design time disallow user from dropping content on the header part:

    To do so, you need to create a new designer which enables the inner panel on design-time by calling EnableDesignMode method. Then for the inner panel, you need to create a designer which disables moving, resizing and removes some properties from designer.

    I've posted a detailed answer here: UserControl with header and content - Allow dropping controls in content panel and Prevent dropping controls in header at design time

    这篇关于在 Winforms 设计器中将 Adorner Glyphs 保持在选定控件的顶部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆