28

Ich verfüge über ein benutzerdefiniertes Benutzersteuerelement mit einem Textfeld und möchte die Basislinie (des Texts im Textfeld) außerhalb des benutzerdefinierten Steuerelements freilegen. Ich weiß, dass Sie einen Designer (von ControlDesigner geerbt) erstellen und SnapLines überschreiben, um auf die Fanglinien zuzugreifen, aber ich frage mich, wie ich die Textbasislinie eines Steuerelements ermitteln kann, das ich von meinem benutzerdefinierten Benutzersteuerelement verfügbar gemacht habe.Baseline-Fanglinien in benutzerdefinierten Winforms-Steuerelementen

Antwort

24

Ich hatte gerade ein ähnliches Bedürfnis, und ich löste es wie folgt aus:

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

diese Weise ist es tatsächlich ist ein temporären Unter Designern für das Substeuerelement um, wo die „echte“ Baseline-Ausrichtungslinie, um herauszufinden, die Schaffung ist.

Dies schien beim Testen einigermaßen performant zu sein, aber wenn Perforation ein Problem darstellt (und sich das interne Textfeld nicht bewegt), kann der Großteil dieses Codes in die Initialize-Methode extrahiert werden.

Dies setzt auch voraus, dass das Textfeld ein direktes Kind des UserControl ist. Wenn andere layoutbeeinflussende Steuerungen im Weg sind, wird die Offsetberechnung etwas komplizierter.

+1

Ehrfürchtig. Das hat getan, was wir brauchten. Vielen Dank! – Mike

+3

Wir haben mehrere Steuerelemente, für die wir diese Funktion benötigen. Daher haben wir dies in einen gemeinsamen Designer für Steuerelemente eingerechnet, der eine "ISnapable" -Schnittstelle implementiert, die wir explizit implementieren und die Steuerelemente verfügbar machen. Auf diese Weise wird die Logik im Designer noch verkapselt, aber verallgemeinert. – Mike

2

Sie sind auf dem richtigen Weg. Sie müssen die Ausrichtungslinien Eigenschaft in Ihrem DesignR außer Kraft zu setzen und etwas zu tun, wie folgt aus:

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 

In diesem Beispiel wird die Usercontrol enthält eine Textbox. Der Code fügt eine neue Fanglinie hinzu, die die Grundlinie für das Textfeld darstellt. Wichtig ist, den Offset korrekt zu berechnen.

37

Als ein Update auf die Miral-Antwort .. hier sind ein paar der "fehlenden Schritte", für jemanden neu, der sucht, wie dies zu tun ist. :) Der oben stehende C# -Code ist fast "drop-in" bereit, mit der Ausnahme, dass einige der Werte geändert werden müssen, um auf das zu ändernde UserControl zu verweisen.

Mögliche Referenzen benötigt:
System.Design (@robyaw)

Usings benötigt:

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

Auf Ihrem Usercontrol Sie folgende Attribute benötigen:

[Designer(typeof(MyCustomDesigner))] 

Dann brauchen Sie eine "Designer" -Klasse, die die SnapLines-Überschreibung haben wird:

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

Eine große Abstimmung (ich wünschte, ich könnte mehr geben), weil Sie zu erwähnen, müssen Sie die ControlDesigner-Klasse INSIDE Ihre UserControl UND dekorieren Sie Ihre Klasse UserControl mit dem Designer-Attribut, das die ControlDesigner-Klasse diktiert. Alles macht Sinn, wenn Sie das sehen, aber nicht vorher. Prost. –

+0

Stuart - Ich hatte das gleiche Problem, weshalb ich diese Frage hinzugefügt habe. Ich bin froh, dass die zusätzlichen Informationen Ihnen helfen konnten! –

+0

Ich versuche dies, aber VS2012 erkennt ControlDesigner nicht, ich benutze winforms, und .net 4.5 ive nie dies vorher getan, so vermutet, dass ich etwas falsch machen, aber nicht herausfinden, was? – f1wade

6

VB.Net Version:
Hinweis: Sie haben die txtDescription auf die Textbox oder einem anderen internen Kontroll Namen zu ändern, die Sie verwenden. und ctlUserControl zu Ihrem usercontrol Namen

<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

Dank an alle, die für die Hilfe. Das war schwer zu schlucken. Der Gedanke, eine private Unterklasse in jedem UserControl zu haben, war nicht sehr schmackhaft.

Ich kam mit dieser Basisklasse, um zu helfen ..

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

Als nächstes leiten Sie Ihr Benutzersteuerelement von dieser Basis:

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

    ... 

} 

Nochmals vielen Dank für dieses Posting.

+0

Dies ist eine schöne und generische Lösung für das Problem. Vielen Dank. – abrahala

Verwandte Themen