Saltar al contenido

En C # Winforms, ¿hay alguna manera de poner un borde punteado alrededor de todos los controles y mostrar puntos de agarre al seleccionar controles específicos en tiempo de ejecución?

Solución:

Trabajo en equipo trabajando en un IDE similar a Visual Studio …

Desarrollar un diseñador de formularios personalizados no es una tarea trivial y requiere mucho conocimiento y mucho tiempo y creo que la mejor solución que puede utilizar es hospedar el diseñador de formularios de Windows.

No se trata solo de dibujar bordes de selección:

  • Cada control tiene su propio diseñador con características específicas, por ejemplo, algunos controles como MenuStrip tiene su propio diseñador que le permite agregar / eliminar elementos en el diseñador.
  • Los controles pueden tener algunas reglas de posicionamiento y tamaño específicas. Por ejemplo, algunos de ellos tienen un tamaño automático como TextBox o los controles acoplados no se pueden reposicionar con el mouse, etc.
  • Los componentes no están visibles en su formulario, por lo que es posible que deba editarlos.
  • Algunas propiedades son propiedades en tiempo de diseño.
  • Algunas propiedades se agregan mediante proveedores de extensor y debe realizar tareas adicionales para proporcionar una forma de cambiarlas en su diseñador personalizado.
  • Y muchas otras consideraciones.

Solución 1 – Hosting Windows Forms Designer

Para obtener más información sobre la arquitectura en tiempo de diseño, eche un vistazo a Arquitectura en tiempo de diseño. Para alojar el diseñador de formularios de Windows en su aplicación, debe implementar algunas interfaces como IDesignerHost, IContainer, IComponentChangeService, IExtenderProvider, ITypeDescriptorFilterService, IExtenderListService, IExtenderProviderService.

Para ver algunos buenos ejemplos, puede echar un vistazo a:

  • Hosting Windows Forms Designers por Tim Dawson
  • Adapte su aplicación creando un diseñador de formularios personalizados con .NET de Sayed Y. Hashimi

Puede encontrar útil esta publicación:

  • Hosting Windows Forms Designer: serializar y deserializar el diseñador en tiempo de ejecución

La publicación contiene un ejemplo práctico sobre cómo alojar el diseñador de formularios de Windows en tiempo de ejecución y generar código:

Solución 2: borde de selección de dibujo sobre un panel transparente

Si bien recomiendo encarecidamente usar la primera solución, pero solo con fines de aprendizaje, si desea dibujar un borde de selección alrededor de los controles, puede agregar los formularios que desea editar como un control al formulario del host y luego colocar un panel transparente sobre el formulario . Resolver Click evento de Panel transparente y busque el control debajo de la posición del mouse y dibuje un borde de selección a su alrededor en el panel transparente como este:

ingrese la descripción de la imagen aquí

En el ejemplo, acabo de crear un panel transparente y dibujar un borde de selección. Es solo un ejemplo y realizar el dimensionamiento y el posicionamiento está fuera del alcance del ejemplo. Es solo para mostrarle cómo puede dibujar un borde de selección alrededor de los controles. También puede utilizar la idea para crear un SelctionBorder controlar y encapsular la lógica de tamaño y posicionamiento en el control y en lugar de dibujar los bordes, agregue una instancia de SelectionBorder control a panel transparente y en sus eventos de dimensionamiento y posicionamiento, cambiar las coordenadas de control correspondientes.

Preste atención, es solo un ejemplo y, en un entorno de diseñador real, debe considerar muchas cosas importantes.

Panel transparente

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

Formulario de anfitrión

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

Un poco de precaución sería apropiado aquí, modelar un diseñador de interfaz de usuario después del diseñador de Winforms es una decisión fácil, en realidad implementarlo es un trabajo que puede mantenerlo ocupado durante muchos meses. Descubrir que no puede pintar fuera de los límites de control es de hecho el primer obstáculo con el que se encontrará, muchos más como ese.

El primer atajo que podría considerar es dibujar marcadores de posición para los controles para que no dependa de la clase Control. Funciona bien siempre que no tenga que parecerse demasiado al control real (es decir, renunciar a WYSIWYG) y no tenga que cambiar su tamaño.

Pero seguramente lo descartarás. Luego debe hacer lo mismo que hace el diseñador de Winforms, debe cubrir una ventana transparente en la parte superior de la superficie de diseño. Puede dibujar lo que quiera en esa superposición y proporciona un aislamiento automático del mouse y el teclado, por lo que el control en sí mismo es completamente ajeno a la interacción del tiempo de diseño. Encuentre ejemplos de dicha superposición en esta publicación y en esta publicación.

Por último, pero no menos importante, vale la pena mencionar que también puede aprovechar el diseñador de Winforms existente en sus propios proyectos. Tienes que implementar IDesignerHost. Y mucho más, desafortunadamente el nivel de abstracción es bastante alto y los documentos de MSDN bastante breves. Lo mejor que puede hacer es trabajar a partir de una muestra que muestre un diseñador con todas las funciones. Este artículo de KB tiene el enlace. El código es excelente y está bien documentado, obtiene un diseñador casi completo con una caja de herramientas y una ventana de Propiedades que de / serializa el diseño desde / hacia XML y puede generar código C # y VB.NET. Mire más allá de la interfaz de usuario chillona, ​​no habilita los estilos visuales y las opciones de color son del tipo que haría 🙂 Hacerlo bonito no era el objetivo del código de muestra.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *