问题
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!
回答1:
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:
- How to: Extend the Appearance and Behavior of Controls in Design Mode
- Adorner
- Glyph
- Behavior
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
来源:https://stackoverflow.com/questions/59965679/keeping-adorner-glyphs-on-top-for-selected-control-in-winforms-designer