在C#Winforms中有一种方法可以在所有控件周围放置虚线边框,并在运行时选择特定控件时显示夹点吗?

我在一个类似于Visual Studio的IDE上工作,为我们的本地客户开发自定义Winform代码。 在我们的代码中,我们覆盖了用户控件以使我们的任务更容易,但我们的大多数控件都是从基本的C#Winform控件派生的。

我目前需要帮助实现所有控件周围的虚线边框,以及Visual Studio提供的抓点类型。

未选择的控件

在此处输入图像描述

选定的控件

在此处输入图像描述

此function非常需要,因为它可以帮助对齐而无需补偿视觉指导。

我们目前在所有控件周围实现了一个黑色边框

this.BackColor = Color.Black; this.Height = ComboBox.Height + 4; 

这会在生成的控件周围放置一个黑色边框,在上面的代码片段中是一个ComboBox。

一位成员指出我们使用边缘和填充,如Microsoft文档中所示: https : //msdn.microsoft.com/library/3z3f9e8b(v=vs.110)

但这主要是理论,并没有多大帮助。 到目前为止,最接近解决此问题的方法是在线CodeProject链接 :

 public class MyGroupBox : GroupBox { protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset, Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset, Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset, Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset); } } 

到目前为止,我很惊讶找不到与我的搜索结果非常接近,也许我使用了错误的术语,因为我最近进入了这个领域的编程。

我相信,如果这个问题得到解决,未来的在线搜索将会受益。 期待指针形成那些有这个问题经验的人。 非常感谢这方面的任何帮助。

我在一个类似于Visual Studio的IDE上工作。

开发自定义表单设计器并不是一项简单的任务,需要大量的知识和大量的时间,我相信您可以使用的最佳解决方案是托管Windows窗体设计器。

它不只是绘制选择边框:

  • 每个控件都有自己的具有特定function的设计器,例如像MenuStrip这样的控件有自己的设计器,可以在设计器上添加/删除项目。
  • 控件可能具有一些特定的尺寸和定位规则。 例如,它们中的一些是像TextBox一样自动resize的,或者停靠的控件不能通过鼠标重新定位等等。
  • 您可能需要在表单上看到组件,您可能需要对其进行编辑。
  • 一些属性是设计时属性。
  • 使用扩展程序提供程序添加了一些属性,您需要执行其他任务以提供在自定义设计器中更改它们的方法。
  • 还有很多其他考虑因素。

解决方案1 ​​ – 托管Windows窗体设计器

要了解有关设计时架构的更多信息,请查看设计时架构 。 要在应用程序中托管Windows窗体设计器,您需要实现一些接口,如IDesignerHostIContainerIComponentChangeServiceIExtenderProviderITypeDescriptorFilterServiceIExtenderListServiceIExtenderProviderService

有一些很好的例子,你可以看看:

  • 由Tim Dawson 主持Windows窗体设计师
  • 通过Sayed Y. Hashimi 用.NET构建自定义窗体设计器来定制应用程序

解决方案2 – 在透明面板中绘制选区边框

虽然我强烈建议使用第一个解决方案,但仅仅为了学习目的,如果你想在控件周围绘制选择边框,你可以将你想要编辑的表单作为控件添加到主机表单,然后在表单上方放置一个透明面板。 处理透明面板的Click事件并在鼠标位置找到控件并在透明面板上绘制一个选择边框,如下所示:

在此处输入图像描述

在这个例子中,我刚刚创建了一个透明面板并绘制了选择边框。 这只是一个示例,执行大小调整和定位超出了示例的范围。 它只是向您展示如何在控件周围绘制选区边框。 您还可以使用该想法创建SelctionBorder控件并在控件中封装大小和定位逻辑,而不是绘制边框,将SelectionBorder控件的实例添加到透明面板,并在其大小和定位事件中,更改相应的控件坐标。

请注意它只是一个例子,在真实的设计师环境中,你应该考虑很多重要的事情。

透明面板

 using System.Windows.Forms; public class TransparentPanel : Panel { const int WS_EX_TRANSPARENT = 0x20; protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT; return cp; } } protected override void OnPaintBackground(PaintEventArgs e) { } } 

主持人表格

 using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; public partial class HostForm : Form { private Panel containerPanel; private TransparentPanel transparentPanel; private PropertyGrid propertyGrid; public HostForm() { this.transparentPanel = new TransparentPanel(); this.containerPanel = new Panel(); this.propertyGrid = new PropertyGrid(); this.SuspendLayout(); this.propertyGrid.Width = 200; this.propertyGrid.Dock = DockStyle.Right; this.transparentPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.transparentPanel.Name = "transparentPanel"; this.containerPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.containerPanel.Name = "containerPanel"; this.ClientSize = new System.Drawing.Size(450, 210); this.Controls.Add(this.transparentPanel); this.Controls.Add(this.propertyGrid); this.Controls.Add(this.containerPanel); this.Name = "HostForm"; this.Text = "Host"; this.Load += this.HostForm_Load; this.transparentPanel.MouseClick += this.transparentPanel_MouseClick; this.transparentPanel.Paint += this.transparentPanel_Paint; this.ResumeLayout(false); } private void HostForm_Load(object sender, EventArgs e) { this.ActiveControl = transparentPanel; /**************************************/ /*Load the form which you want to edit*/ /**************************************/ var f = new Form(); f.Location = new Point(8, 8); f.TopLevel = false; this.containerPanel.Controls.Add(f); SelectedObject = f; f.Show(); } Control selectedObject; Control SelectedObject { get { return selectedObject; } set { selectedObject = value; propertyGrid.SelectedObject = value; this.Refresh(); } } void transparentPanel_MouseClick(object sender, MouseEventArgs e) { if (this.Controls.Count == 0) return; SelectedObject = GetAllControls(this.containerPanel) .Where(x => x.Visible) .Where(x => x.Parent.RectangleToScreen(x.Bounds) .Contains(this.transparentPanel.PointToScreen(e.Location))) .FirstOrDefault(); this.Refresh(); } void transparentPanel_Paint(object sender, PaintEventArgs e) { if (SelectedObject != null) DrawBorder(e.Graphics, this.transparentPanel.RectangleToClient( SelectedObject.Parent.RectangleToScreen(SelectedObject.Bounds))); } private IEnumerable GetAllControls(Control control) { var controls = control.Controls.Cast(); return controls.SelectMany(ctrl => GetAllControls(ctrl)).Concat(controls); } void DrawBorder(Graphics g, Rectangle r) { var d = 4; r.Inflate(d, d); ControlPaint.DrawBorder(g, r, Color.Black, ButtonBorderStyle.Dotted); var rectangles = new List(); var r1 = new Rectangle(r.Left - d, r.Top - d, 2 * d, 2 * d); rectangles.Add(r1); r1.Offset(r.Width / 2, 0); rectangles.Add(r1); r1.Offset(r.Width / 2, 0); rectangles.Add(r1); r1.Offset(0, r.Height / 2); rectangles.Add(r1); r1.Offset(0, r.Height / 2); rectangles.Add(r1); r1.Offset(-r.Width / 2, 0); rectangles.Add(r1); r1.Offset(-r.Width / 2, 0); rectangles.Add(r1); r1.Offset(0, -r.Height / 2); rectangles.Add(r1); g.FillRectangles(Brushes.White, rectangles.ToArray()); g.DrawRectangles(Pens.Black, rectangles.ToArray()); } protected override bool ProcessTabKey(bool forward) { return false; } protected override void OnResize(EventArgs e) { base.OnResize(e); this.Refresh(); } } 

这里需要注意的一点是,在Winforms设计师做出简单决定后对UI设计师进行建模,实际实现它是一项可以让你长时间工作的工作。 发现你无法在控制界之外进行绘画,这确实是你遇到的第一个障碍,更多的是这样。

您可能考虑的第一个快捷方式是为控件绘制占位符,这样您就不依赖于Control类。 工作正常,只要它不必看起来像真正的控件(即放弃WYSIWYG),你不必调整它们的大小。

但你肯定会放弃这一点。 然后你必须做Winforms设计师做的事情,你必须在设计表面的顶部覆盖一个透明窗口。 您可以在该叠加层上绘制任何想要的内容,它提供自动鼠标和键盘隔离,因此控件本身完全无视设计时间的交互。 在这篇文章和这篇文章中找到这种叠加的例子。

最后但同样重要的是,值得一提的是,您也可以在自己的项目中利用现有的Winforms设计器。 您必须实现IDesignerHost。 还有更多,不幸的是抽象级别相当高,MSDN文档相当简短。 最好的办法是从展示全function设计师的样本开始。 这篇知识库文章有链接。 代码非常好且文档齐全,您可以获得一个几乎完整的设计器,其中包含工具箱和属性窗口,可以将设计从/向XML序列化,并可以生成C#和VB.NET代码。 看看过去的花哨UI,它不启用视觉样式和颜色选择是我会做的那种:)使它漂亮不是代码示例的重点。

我创建了Windows窗体应用程序希望这将帮助您

BackEnd C#代码

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Paint += new PaintEventHandler(this_Paint); } private void this_Paint(object sender, PaintEventArgs e) { Pen pen = new Pen(Color.Green, 2.0F); pen.DashStyle = DashStyle.Dash; foreach (Control c in groupBox1.Controls) { e.Graphics.DrawRectangle(pen, (groupBox1.Location.X + c.Location.X)-1, (groupBox1.Location.Y + c.Location.Y)-1, c.Width + 2, c.Height + 2); } pen.Dispose(); } private void Form1_Load(object sender, EventArgs e) { } } } 

设计师C#代码

 namespace WindowsFormsApplication2 { partial class Form1 { ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  private void InitializeComponent() { this.groupBox1 = new System.Windows.Forms.GroupBox(); this.comboBox1 = new System.Windows.Forms.ComboBox(); this.comboBox2 = new System.Windows.Forms.ComboBox(); this.comboBox3 = new System.Windows.Forms.ComboBox(); this.comboBox4 = new System.Windows.Forms.ComboBox(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // // groupBox1 // this.groupBox1.BackColor = System.Drawing.Color.Transparent; this.groupBox1.Controls.Add(this.comboBox4); this.groupBox1.Controls.Add(this.comboBox3); this.groupBox1.Controls.Add(this.comboBox2); this.groupBox1.Controls.Add(this.comboBox1); this.groupBox1.Location = new System.Drawing.Point(33, 36); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(193, 184); this.groupBox1.TabIndex = 0; this.groupBox1.TabStop = false; this.groupBox1.Text = "groupBox1"; // // comboBox1 // this.comboBox1.FormattingEnabled = true; this.comboBox1.Location = new System.Drawing.Point(36, 40); this.comboBox1.Name = "comboBox1"; this.comboBox1.Size = new System.Drawing.Size(121, 21); this.comboBox1.TabIndex = 0; // // comboBox2 // this.comboBox2.FormattingEnabled = true; this.comboBox2.Location = new System.Drawing.Point(36, 67); this.comboBox2.Name = "comboBox2"; this.comboBox2.Size = new System.Drawing.Size(121, 21); this.comboBox2.TabIndex = 1; // // comboBox3 // this.comboBox3.FormattingEnabled = true; this.comboBox3.Location = new System.Drawing.Point(36, 94); this.comboBox3.Name = "comboBox3"; this.comboBox3.Size = new System.Drawing.Size(121, 21); this.comboBox3.TabIndex = 1; // // comboBox4 // this.comboBox4.FormattingEnabled = true; this.comboBox4.Location = new System.Drawing.Point(36, 121); this.comboBox4.Name = "comboBox4"; this.comboBox4.Size = new System.Drawing.Size(121, 21); this.comboBox4.TabIndex = 1; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(284, 261); this.Controls.Add(this.groupBox1); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); this.groupBox1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.ComboBox comboBox1; private System.Windows.Forms.ComboBox comboBox4; private System.Windows.Forms.ComboBox comboBox3; private System.Windows.Forms.ComboBox comboBox2; } } 

在此处输入图像描述

使GroupBox1 backgroundcolour’Transparent’,因为我在Form上绘制不在GroupBox上

您还可以通过添加if(c是ComboBox)在所选控件上创建边框

或者在foreach循环中是否(c.Name ==“comboBox1”)

! 根据您的需要改变颜色!!

 public partial class Form1 : Form { public Form1() { InitializeComponent(); hatchedPen = (Pen)SystemPens.ControlDarkDark.Clone(); hatchedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // Clear any existing grab handles using (Graphics g = Graphics.FromHwnd(this.Handle)) { foreach (Control ctrl in Controls) { var rect = GetGrabBounds(ctrl); g.FillRectangle(SystemBrushes.ButtonFace, rect); } } // Need to draw grab handles? if (ActiveControl != null && e.ClipRectangle.IntersectsWith(GetGrabBounds(ActiveControl))) { DrawGrabHandles(ActiveControl); } } private void DrawGrabHandles(Control ctrl) { using (Graphics g = Graphics.FromHwnd(this.Handle)) { Rectangle bounds = GetGrabRect(ctrl); g.DrawRectangle(hatchedPen, bounds); foreach (Point pt in new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Left + bounds.Width / 2, bounds.Top), new Point(bounds.Right, bounds.Top), new Point(bounds.Left, bounds.Top + bounds.Height / 2), new Point(bounds.Right, bounds.Top + bounds.Height / 2), new Point(bounds.Left, bounds.Bottom), new Point(bounds.Left + bounds.Width / 2, bounds.Bottom), new Point(bounds.Right, bounds.Bottom), }) { Rectangle r = new Rectangle(pt, new Size(5, 5)); rX = rX - 2; rY = rY - 2; g.FillRectangle(SystemBrushes.ButtonFace, r); g.DrawRectangle(SystemPens.ControlDarkDark, r); } } } private static Rectangle GetGrabRect(Control ctrl) { var result = ctrl.Bounds; result = Rectangle.Inflate(result, 4, 4); result.X--; result.Y--; return result; } private static Rectangle GetGrabBounds(Control ctrl) { var result = GetGrabRect(ctrl); result.Inflate(4, 4); return result; } private Pen hatchedPen; }