28

Tengo un control de usuario personalizado con un cuadro de texto y me gustaría exponer la línea de referencia (del texto en el cuadro de texto) fuera del control personalizado. Sé que crea un diseñador (heredado de ControlDesigner) y anula SnapLines para tener acceso a los snapines, pero me pregunto cómo obtener la línea de base de texto de un control que he expuesto por mi control de usuario personalizado.Etiquetas básicas en controles personalizados de Winforms

Respuesta

24

acabo de tener una necesidad similar, y lo resolvió así:

public override IList SnapLines 
{ 
    get 
    { 
     IList snapLines = base.SnapLines; 

     MyControl control = Control as MyControl; 
     if (control == null) { return snapLines; } 

     IDesigner designer = TypeDescriptor.CreateDesigner(
      control.textBoxValue, typeof(IDesigner)); 
     if (designer == null) { return snapLines; } 
     designer.Initialize(control.textBoxValue); 

     using (designer) 
     { 
      ControlDesigner boxDesigner = designer as ControlDesigner; 
      if (boxDesigner == null) { return snapLines; } 

      foreach (SnapLine line in boxDesigner.SnapLines) 
      { 
       if (line.SnapLineType == SnapLineType.Baseline) 
       { 
        snapLines.Add(new SnapLine(SnapLineType.Baseline, 
         line.Offset + control.textBoxValue.Top, 
         line.Filter, line.Priority)); 
        break; 
       } 
      } 
     } 

     return snapLines; 
    } 
} 

De esta manera se trata en realidad la creación de un sub-diseñador temporal para el subcontrol con el fin de localizar el origen del "verdadero" Snapline línea de base es.

Esto pareció razonablemente efectivo en las pruebas, pero si perf es una preocupación (y si el cuadro de texto interno no se mueve), la mayoría de este código se puede extraer al método Initialize.

Esto también asume que el cuadro de texto es un elemento directo del UserControl. Si hay otros controles que afectan el diseño en el camino, entonces el cálculo del desplazamiento se vuelve un poco más complicado.

+1

Impresionante. Eso hizo lo que necesitábamos hacer. ¡Gracias! – Mike

+3

Tenemos varios controles para los que necesitamos que esto funcione, por lo que terminamos factorizando esto en un diseñador común para los controles que implementamos una interfaz "ISnapable" que implementamos explícitamente y expusimos los controles. De esta manera, la lógica aún está encapsulada en el diseñador pero generalizada. – Mike

2

Estás en el camino correcto. Usted tendrá que reemplazar la propiedad SnapLines en su DesignR y hacer algo como esto:

Public Overrides ReadOnly Property SnapLines() As System.Collections.IList 
    Get 
     Dim snapLinesList As ArrayList = TryCast(MyBase.SnapLines, ArrayList) 

     Dim offset As Integer 
     Dim ctrl As MyControl = TryCast(Me.Control, MyControl) 
     If ctrl IsNot Nothing AndAlso ctrl.TextBox1 IsNot Nothing Then 
      offset = ctrl.TextBox1.Bottom - 5 
     End If 

     snapLinesList.Add(New SnapLine(SnapLineType.Baseline, offset, SnapLinePriority.Medium)) 

     Return snapLinesList 

    End Get 
End Property 

En este ejemplo, el control de usuario contiene un cuadro de texto. El código agrega una nueva línea de inserción que representa la línea de base para el cuadro de texto. Lo importante es calcular el desplazamiento correctamente.

37

Como una actualización de la respuesta de Miral ... aquí hay algunos de los "pasos que faltan", para alguien nuevo que está buscando cómo hacer esto. :) El código de C# de arriba está casi listo para usar, con la excepción de cambiar algunos de los valores para hacer referencia al UserControl que se modificará.

posibles referencias necesarias:
System.Design (@robyaw)

usings necesarios:

using System.Windows.Forms.Design; 
using System.Windows.Forms.Design.Behavior; 
using System.ComponentModel; 
using System.ComponentModel.Design; 
using System.Collections; 

En el control de usuario que necesita el atributo siguiente:

[Designer(typeof(MyCustomDesigner))] 

Luego hay una clase de "diseñador" que tendrá la anulación de SnapLines:

private class MyCustomerDesigner : ControlDesigner { 
    public override IList SnapLines { 
    get { 
    /* Code from above */ 
    IList snapLines = base.SnapLines; 

    // *** This will need to be modified to match your user control 
    MyControl control = Control as MyControl; 
    if (control == null) { return snapLines; } 

    // *** This will need to be modified to match the item in your user control 
    // This is the control in your UC that you want SnapLines for the entire UC 
    IDesigner designer = TypeDescriptor.CreateDesigner(
     control.textBoxValue, typeof(IDesigner)); 
    if (designer == null) { return snapLines; } 

    // *** This will need to be modified to match the item in your user control 
    designer.Initialize(control.textBoxValue); 

    using (designer) 
    { 
     ControlDesigner boxDesigner = designer as ControlDesigner; 
     if (boxDesigner == null) { return snapLines; } 

     foreach (SnapLine line in boxDesigner.SnapLines) 
     { 
      if (line.SnapLineType == SnapLineType.Baseline) 
      { 
       // *** This will need to be modified to match the item in your user control 
       snapLines.Add(new SnapLine(SnapLineType.Baseline, 
        line.Offset + control.textBoxValue.Top, 
        line.Filter, line.Priority)); 
       break; 
      } 
     } 
    } 

    return snapLines; 
} 

    } 
    } 
} 
+6

Una gran votación (ojalá pudiera dar más) porque se molestó en mencionar, necesita poner la clase ControlDesigner DENTRO de su UserControl Y decorar su clase UserControl con el atributo Designer que dicta la clase ControlDesigner. Todo tiene sentido una vez que lo veas, pero no antes. Saludos. –

+0

Stuart - Tuve el mismo problema, por lo que agregué a esta pregunta. ¡Me alegro de que la información adicional haya sido capaz de ayudarte! –

+0

Estoy intentando esto pero VS2012 no reconoce ControlDesigner, estoy usando winforms, y .net 4.5 nunca he hecho esto antes así que supongo que estoy haciendo algo mal pero no puedo entender qué? – f1wade

6

VB.Net Versión:
Nota: debe cambiar el txtDescription al cuadro de texto u otro nombre de control interno que utilice. y ctlUserControl a su nombre usercontrol

<Designer(GetType(ctlUserControl.MyCustomDesigner))> _ 
Partial Public Class ctlUserControl 
    '... 
    'Your Usercontrol class specific code 
    '... 
    Class MyCustomDesigner 
     Inherits ControlDesigner 
     Public Overloads Overrides ReadOnly Property SnapLines() As IList 
      Get 
       ' Code from above 

       Dim lines As IList = MyBase.SnapLines 

       ' *** This will need to be modified to match your user control 
       Dim control__1 As ctlUserControl = TryCast(Me.Control, ctlUserControl) 
       If control__1 Is Nothing Then Return lines 

       ' *** This will need to be modified to match the item in your user control 
       ' This is the control in your UC that you want SnapLines for the entire UC 
       Dim designer As IDesigner = TypeDescriptor.CreateDesigner(control__1.txtDescription, GetType(IDesigner)) 
       If designer Is Nothing Then 
        Return lines 
       End If 

       ' *** This will need to be modified to match the item in your user control 
       designer.Initialize(control__1.txtDescription) 

       Using designer 
        Dim boxDesigner As ControlDesigner = TryCast(designer, ControlDesigner) 
        If boxDesigner Is Nothing Then 
         Return lines 
        End If 

        For Each line As SnapLine In boxDesigner.SnapLines 
         If line.SnapLineType = SnapLineType.Baseline Then 
          ' *** This will need to be modified to match the item in your user control 
          lines.Add(New SnapLine(SnapLineType.Baseline, line.Offset + control__1.txtDescription.Top, line.Filter, line.Priority)) 
          Exit For 
         End If 
        Next 
       End Using 

       Return lines 
      End Get 
     End Property 
    End Class 

End Class 
6

Gracias a todos los de la ayuda. Esto fue difícil de tragar. La idea de tener una subclase privada en cada UserControl no era muy aceptable.

Vine con esta clase base para ayudar ..

[Designer(typeof(UserControlSnapLineDesigner))] 
public class UserControlBase : UserControl 
{ 
    protected virtual Control SnapLineControl { get { return null; } } 

    private class UserControlSnapLineDesigner : ControlDesigner 
    { 
     public override IList SnapLines 
     { 
      get 
      { 
       IList snapLines = base.SnapLines; 

       Control targetControl = (this.Control as UserControlBase).SnapLineControl; 

       if (targetControl == null) 
        return snapLines; 

       using (ControlDesigner controlDesigner = TypeDescriptor.CreateDesigner(targetControl, 
        typeof(IDesigner)) as ControlDesigner) 
       { 
        if (controlDesigner == null) 
         return snapLines; 

        controlDesigner.Initialize(targetControl); 

        foreach (SnapLine line in controlDesigner.SnapLines) 
        { 
         if (line.SnapLineType == SnapLineType.Baseline) 
         { 
          snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + targetControl.Top, 
           line.Filter, line.Priority)); 
          break; 
         } 
        } 
       } 
       return snapLines; 
      } 
     } 
    } 
} 

A continuación, se derivan de su control de usuario de esta base:

public partial class MyControl : UserControlBase 
{ 
    protected override Control SnapLineControl 
    { 
     get 
     { 
      return txtTextBox; 
     } 
    } 

    ... 

} 

Gracias de nuevo por publicar esto.

+0

Esta es una solución hermosa y genérica para el problema. Gracias. – abrahala

Cuestiones relacionadas