2017-03-22 4 views
4

Ich habe vor kurzem angefangen, mehr über Ereignisse/Delegaten in Verbindung mit Erweiterungen zu einer Klasse zu lernen.C#: abgeleitete Klassen als generischer Parameter übergeben

wollte ich setzen, was ich in die Praxis gelernt eine SetDraggable() genannt Erweiterung Methode, um Windows Form durch Hinzufügen von Steuerelementen, die wiederum ein MouseDown und MouseMove Ereignisse verwendet die Steuerung zu bewegen.

Alles funktioniert gut, bis auf die Idee, dass es nur für ein bestimmtes Steuerelement gilt - in meinem Fall eine Button.

namespace Form_Extensions 
{ 
    public static class Extensions 
    { 
     private static System.Windows.Forms.Button StubButton; 
     private static Point MouseDownLocation; 
     public static void SetDraggable(this System.Windows.Forms.Button b) 
     { 
      b.MouseDown += b_MouseDown; 
      b.MouseMove += b_MouseMove; 
      StubButton = b; 
     } 

     private static void b_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) 
     { 
      if (e.Button == System.Windows.Forms.MouseButtons.Left) 
      { 
       MouseDownLocation = e.Location; 
      } 
     } 

     static void b_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) 
     { 
      if (e.Button == System.Windows.Forms.MouseButtons.Left) 
      { 
       StubButton.Left = e.X + StubButton.Left - MouseDownLocation.X; 
       StubButton.Top = e.Y + StubButton.Top - MouseDownLocation.Y; 
      } 
     } 

    } 
} 

Wie man sehen kann, muss ich die spezifische Steuerung, um die Mausereignisse nennen - ich kann diese Ereignisse nicht zugreifen aus dem Eltern Klasse System.Windows.Forms.

Also meine Frage bleibt - gibt es ein Konzept, das Programmierer generisch alle abgeleiteten Klassen als Argument übergeben können. Ich versuche grundsätzlich zu vermeiden, den folgenden Code für jede einzelne Kontrolle einzufügen und möchte ihn für alle von System.Windows.Forms abgeleiteten Klassen verallgemeinern.

Soweit ich sagen kann, ist der Hauptfehler in dieser Idee die Tatsache, dass ich NAHME bin, dass alle abgeleiteten Klassen das Ereignis muss ich haben; Da jedoch eine ähnliche Funktion für Funktionen in Form von Delegaten existiert, hatte ich gehofft, jemand könnte für Fälle mit Objekten oder Parameter einwiegen.

+1

Die Elternklasse nicht 'System.Windows.Forms' ist, das ist einfach der Namespace. Die Elternklasse ist 'Control', und Sie können das sicher verwenden :) Eine generische Methode ist ebenfalls möglich, aber nicht wirklich notwendig. Im Idealfall sollten Sie diese statischen Felder auch vermeiden, da mehrere gleichzeitige ziehbare Elemente möglich sind. Eine Schließung in der 'SetControlDraggable' Methode wird viel besser funktionieren. Oder benutze einfach 'sender', das ist wirklich was es ist :) – Luaan

Antwort

6

Die Elternklasse ist nicht System.Windows.Forms, das ist nur die Namensraum. Die eigentliche Elternklasse ist Control, und Sie können sicherlich das verwenden :) Mit einer generischen Methode ist auch möglich, aber nicht wirklich notwendig.

Im Idealfall sollten Sie diese statischen Felder auch vermeiden, da mehrere gleichzeitige ziehbare Elemente vorhanden sein können. ein Verschluss in der SetControlDraggable Methode wird viel besser funktionieren:

public static void SetControlDraggable(this Control control) 
{ 
    Point mouseDownLocation = Point.Empty; 

    control.MouseDown += (s, e) => 
    { 
     if (e.Button == MouseButtons.Left) mouseDownLocation = e.Location; 
    } 
    control.MouseUp += (s, e) => 
    { 
     if (e.Button == MouseButtons.Left) 
     { 
     control.Left = e.X + control.Left - mouseDownLocation.X; 
     control.Top = e.Y + control.Top - mouseDownLocation.Y; 
     } 
    } 
} 
+0

Schöne Idee mit dem Verschluss. Ich dachte über einen Weg nach, den statischen Punkt zu vermeiden, konnte aber nicht auf diese elegante Lösung kommen und dachte dann, dass dies in der Praxis kein Problem sein wird. Aber diese Lösung ist natürlich sicherer. –

+0

Vielen Dank für die Klärung und Entschuldigung für die Verwechslung mit dem Namensraum. Ich schätze es auch, mir weitere Probleme zu ersparen, indem ich die statischen Felder beseitige! –

1

Es gibt zwei Möglichkeiten:

public static class Extensions 
{ 
    private static System.Windows.Forms.Control StubButton; 
    private static Point MouseDownLocation; 

    public static void SetControlDraggable(this System.Windows.Forms.Control b) 
    { 
     b.MouseDown += b_MouseDown; 
     b.MouseMove += b_MouseMove; 
     StubButton = b; 
    } 

    public static void SetDraggable<T>(this T b) 
     where T:System.Windows.Forms.Control 
    { 
     b.MouseDown += b_MouseDown; 
     b.MouseMove += b_MouseMove; 
     StubButton = b; 
    } 

    private static void b_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) 
    { 
     if (e.Button == System.Windows.Forms.MouseButtons.Left) 
     { 
      MouseDownLocation = e.Location; 
     } 
    } 

    private static void b_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) 
    { 
     if (e.Button == System.Windows.Forms.MouseButtons.Left) 
     { 
      StubButton.Left = e.X + StubButton.Left - MouseDownLocation.X; 
      StubButton.Top = e.Y + StubButton.Top - MouseDownLocation.Y; 
     } 
    } 
} 
3

Es besteht keine Notwendigkeit für hier Generika. Sie können einfach die Basisklasse Control verwenden und Ihre Erweiterung funktioniert mit allen Klassen, die von Control abgeleitet sind.

public static void SetDraggable(this Control c) 
{ 
     c.MouseDown += c_MouseDown; 
     c.MouseMove += c_MouseMove; 
     control = c; 
} 

Und Sie kein statisches Element müssen die Bezugnahme der Kontrolle zu halten, da dies sender Argument als Ereignishandler übergeben wird:

private static void c_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) 
{ 
    Control control = (Control)sender; 
    if (e.Button == System.Windows.Forms.MouseButtons.Left) 
    { 
     control.Left = e.X + control.Left - MouseDownLocation.X; 
     controlTop = e.Y + control.Top - MouseDownLocation.Y; 
    } 
} 
1

über das allgemeine Problem, andere Beiträge helfen, das Problem zu lösen. Sie müssen die Methode nicht generisch machen, und die Verwendung von Control als Methodentyp ist ausreichend, auch wenn aus irgendeinem Grund, dass sie generisch sein soll, reicht es aus, eine where-Klausel hinzuzufügen, die besagt, dass der Typ von Control abgeleitet werden soll Klasse.

Extender Provider-Komponenten

für Windows Forms, eine gute Lösung für solche Erweiterungen zu den Kontrollen Zugabe wird eine Verlängerungskomponente zu schaffen, die für Sie Design-Zeit Unterstützung bringt. Sie können eine Extender-Komponente erstellen und eine EnableDrag-Eigenschaft zu anderen Komponenten hinzufügen, dann können Sie sie auf true oder false setzen, um sie ziehbar zu machen.

Die vom Extender-Provider bereitgestellte Eigenschaft befindet sich tatsächlich im Extender-Provider-Objekt selbst und ist daher keine echte Eigenschaft der Komponente, die sie modifiziert. Zur Entwurfszeit wird die Eigenschaft jedoch im Eigenschaftenfenster für erweiterte Steuerelemente angezeigt. Zur Laufzeit können Sie auch nicht auf die Eigenschaft zugreifen, indem Sie die Getter- und Setter-Methoden der Extender-Komponente aufrufen.

Beispiel

In diesem Beispiel habe ich eine DraggableExtender Verlängerungs-Komponente erstellt. Wenn Sie eine Instanz dieser Komponente in Ihrem Formular löschen, haben alle Steuerelemente eine zusätzliche Eigenschaft mit dem Namen EnableDrag on draggableExtender1, die Sie zur Entwurfszeit auf true oder false festlegen können. Dann kann das Steuerelement zur Laufzeit gezogen werden, wenn Sie es auf true setzen.

using System.Collections.Generic; 
using System.ComponentModel; 
using System.Drawing; 
using System.Windows.Forms; 
[ProvideProperty("EnableDrag", typeof(Control))] 
public class DraggableExtender : Component, IExtenderProvider 
{ 
    private Dictionary<Control, bool> EnableDragValues = 
     new Dictionary<Control, bool>(); 
    public bool CanExtend(object extendee) 
    { 
     //You can limit the type of extendee here 
     if (extendee is Control) 
      return true; 
     return false; 
    } 
    public bool GetEnableDrag(Control control) 
    { 
     if (EnableDragValues.ContainsKey(control)) 
      return EnableDragValues[control]; 
     return false; 
    } 
    public void SetEnableDrag(Control control, bool value) 
    { 
     EnableDragValues[control] = value; 
     { 
      if (value) 
      { 
       control.MouseDown += MouseDown; 
       control.MouseMove += MouseMove; 
       control.Cursor = Cursors.SizeAll; 
      } 
      else 
      { 
       control.MouseDown -= MouseDown; 
       control.MouseMove -= MouseMove; 
       control.Cursor = Cursors.Default; 
      } 
     } 
    } 

    Point p1; 
    private void MouseDown(object sender, MouseEventArgs e) 
    { 
     if (e.Button == MouseButtons.Left) 
      p1 = Cursor.Position; 
    } 
    private void MouseMove(object sender, MouseEventArgs e) 
    { 
     if (e.Button == MouseButtons.Left) 
     { 
      var control = (Control)sender; 
      var p = control.Location; 
      p.Offset(Cursor.Position.X - p1.X, Cursor.Position.Y - p1.Y); 
      control.Location = p; 
      p1 = Cursor.Position; 
     } 
    } 
} 

Erfahren Sie mehr über Extender Provider

+0

Der hier angesprochene generische Parameter: [Wie erstelle ich eine Template-Funktion für Steuerelemente eines Formulars?] (Http://stackoverflow.com/a/35946076/3110834), aber verpasse auch nicht die Extender-Provider-Option :) –

Verwandte Themen