在C#Winforms中,是否有一种方法可以在所有控件周围放置虚线边框,并在运行时选择特定控件后显示抓点? [英] In C# Winforms is there a way to put dotted border around all controls and show grip points upon selection of specific controls at runtime?

查看:138
本文介绍了在C#Winforms中,是否有一种方法可以在所有控件周围放置虚线边框,并在运行时选择特定控件后显示抓点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个团队中工作,该团队在类似于Visual Studio的IDE上为本地客户开发自定义Winform代码.在我们的代码中,我们重写了用户控件以简化我们的任务,但我们的大多数控件都来自基本的C#Winform控件.

I work in a team working on a IDE similar to Visual Studio to develop custom Winform code for our local clients. In our code we have User Controls overridden to make our tasks easier but most of our controls are derived from basic C# Winform Controls.

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

I currently need help in implementing dotted border around all our controls, with the type of grip points as provided by Visual Studio.

未选中的控件

所选控件

对此功能有很高的要求,因为它可以帮助对齐而无需对视觉准则进行补偿.

This feature is highly demanded as it can help in aligning without compensation on visual guidelines.

我们目前已使用

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

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

Which puts a black border around the generated Controls, which in the above code snippet is a ComboBox.

一个成员指出我们正按照Microsoft文档中的说明使用边距和填充: https://msdn.microsoft.com/library/3z3f9e8b(v = vs.110)

One member pointed us towards using Margins and Padding as shown in the Microsoft documentation: https://msdn.microsoft.com/library/3z3f9e8b(v=vs.110)

但这主要是理论上的,似乎并没有太大帮助.到目前为止,解决这个问题最接近的方法是在线

But this is mostly theory and does not seem to help much. the closest thing that has come to solve this problem so far has been an online CodeProject link:

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

令我惊讶的是,到目前为止,我还没有找到与我的搜索词非常匹配的词,也许是因为我使用了错误的术语,因为我最近开始从事该领域的编程工作.

I am surprized to not find a close match to my search so far, perhaps i am using the wrong terminology, as I recently got into programming in this domain.

我相信,如果这个问题得到解决,将来的在线搜索将会受益匪浅.期待有经验的人来指点一下.非常感谢您对此方向的任何帮助.

I believe that future online searches are going to be benifitted, if this problem gets solved. Looking forward for pointers form those with experience in this problem. Really appreciate any help in this direction.

推荐答案

我在一个团队中工作,该团队在类似于Visual Studio ...的IDE上工作.

I work in a team working on a IDE similar to Visual Studio ....

开发自定义表单设计器并非易事,需要大量知识和大量时间,我相信可以使用的最佳解决方案是托管Windows表单设计器.

Developing a custom form designer is not a trivial task and needs a lot of knowledge and a lot of time and I believe the best solution which you can use, is hosting windows forms designer.

不仅仅是绘制选择边框:

It's not just about drawing selection borders:

  • 每个控件都有自己的设计器,具有特定功能,例如某些控件,例如MenuStrip都有自己的设计器,使您可以在Designer上添加/删除项目.
  • 控件可能具有一些特定的大小和定位规则.例如,其中一些是自动调整大小的,例如TextBox,或者停靠的控件无法通过鼠标等重新定位.
  • 组件在您的表单上不可见,您可能需要对其进行编辑.
  • 某些属性是设计时属性.
  • 某些属性是使用扩展程序提供程序添加的,您需要执行其他任务以提供一种在自定义设计器中对其进行更改的方法.
  • 还有许多其他考虑因素.
  • Each control has it's own designer with specific features, for example some controls like MenuStrip has it's own designer which enables you to add/remove items on designer.
  • Controls may have some specific sizing and positioning rules. For example some of them are auto-sized like TextBox or docked controls can not be reposition by mouse and so on.
  • Components are not visible on your form which you may need to edit them.
  • Some properties are design-time properties.
  • Some properties are added using extender providers and you need to perform additional tasks to provide a way to change them in your custom designer.
  • And a lot of other considerations.

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

To learn more about design time architecture, take a look at Design-Time Architecture. To host windows forms designer in your application, you need to implement some interfaces like IDesignerHost, IContainer, IComponentChangeService, IExtenderProvider, ITypeDescriptorFilterService, IExtenderListService, IExtenderProviderService.

对于一些好的例子,您可以看一下:

For some good examples you can take a look at:

  • Hosting Windows Forms Designers by Tim Dawson
  • Tailor Your Application by Building a Custom Forms Designer with .NET by Sayed Y. Hashimi

您可能会发现这篇文章很有用:

You may find this post useful:

该帖子包含一个有效的示例,说明如何在运行时托管Windows窗体设计器并生成代码:

The post contains a working example on how to host windows forms designer at run-time and generate code:

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

While I strongly recommend using the first solution, but just for learning purpose if you want to draw selection border around controls, you can add the forms which you want to edit as a control to the host form, then put a transparent panel above the form. Handle Click event of transparent Panel and find the control under mouse position and draw a selection border around it on transparent panel like this:

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

In the example, I just created a transparent panel and drew selection border. It's just an example and performing sizing and positioning is out of scope of the example. It's just to show you how you can draw selection border around controls. You also can use the idea to create a SelctionBorder control and encapsulate sizing and positioning logic in the control and instead of drawing the borders, add an instance of SelectionBorder control to transparent panel and in its sizing and positioning events, change corresponding control coordinates.

请注意,这只是一个示例,在实际的设计人员环境中,您应该考虑很多重要的事情.

Please pay attention it's just an example and in a real designer environment you should consider a lot of important things.

透明面板

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<Control> GetAllControls(Control control)
    {
        var controls = control.Controls.Cast<Control>();
        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<Rectangle>();
        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();
    }
}

这篇关于在C#Winforms中,是否有一种方法可以在所有控件周围放置虚线边框,并在运行时选择特定控件后显示抓点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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