C#:将派生类作为一个通用参数传递

我最近开始学习更多关于事件/代理以及类扩展的知识。

我想通过向Windows Form控件添加一个名为SetDraggable()扩展方法,将我学到的东西付诸实践,然后使用MouseDownMouseMove事件来移动控件。

一切正常,除了它只适用于特定控件的想法 – 在我的例子中,一个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 = eX + StubButton.Left - MouseDownLocation.X; StubButton.Top = eY + StubButton.Top - MouseDownLocation.Y; } } } } 

可以看出,我需要特定的控件才能调用Mouse事件 – 我无法从System.Windows.Forms访问这些事件。

所以我的问题仍然存在 – 是否有一个概念允许程序员一般将所有派生类作为参数传递 。 我基本上试图避免复制粘贴每个控件的下面的代码,并希望将它推广到从System.Windows.Forms派生的所有类。

据我所知,这个想法的主要缺陷是我假设所有派生类都有我需要的事件; 但是,由于代表forms的function存在类似的事情,我希望有人可以权衡涉及对象或参数的情况。

父类不是System.Windows.Forms ,它只是命名空间。 实际的父类是Control ,你当然可以使用它:)使用generics方法也是可能的,但不是真的必要。

理想情况下,您还希望避免使用这些静态字段,因为它可能有多个并发可拖动字段; SetControlDraggable方法中的闭包将更好地工作:

 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 = eX + control.Left - mouseDownLocation.X; control.Top = eY + control.Top - mouseDownLocation.Y; } } } 

这里不需要generics。 您可以简单地使用Control基类,您的扩展将适用于从Control派生的所有类。

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

并且您不需要静态成员来保持控件的引用,因为它作为事件处理程序的sender参数传递:

 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 = eX + control.Left - MouseDownLocation.X; controlTop = eY + control.Top - MouseDownLocation.Y; } } 

有两种可能性:

  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(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 = eX + StubButton.Left - MouseDownLocation.X; StubButton.Top = eY + StubButton.Top - MouseDownLocation.Y; } } } 

关于一般问题,其他post可以帮助您解决问题。 你不需要使方法成为generics,并且使用Control作为方法的类型就足够了,如果由于任何原因你希望它是通用的,那么就足以添加一个where子句来声明类型应该从派生formsControl类。

扩展程序提供程序组件

对于Windows Forms,为控件添加此类扩展的一个很好的解决方案是创建一个扩展器组件,为您提供设计时支持。 您可以创建扩展程序组件并将EnableDrag属性添加到其他组件,然后可以将其设置为true或false以使其可拖动。

扩展程序提供程序提供的属性实际上驻留在扩展程序提供程序对象本身中,因此它不是它修改的组件的真实属性。 但在设计时,属性将显示在属性窗口中以进行扩展控制。 此外,在运行时,您无法通过调用extender组件的getter和setter方法来访问该属性。

在这个例子中,我创建了一个DraggableExtender扩展器组件。 当您在表单上删除此组件的实例时,所有控件将EnableDrag on draggableExtender1具有名为EnableDrag on draggableExtender1的附加属性,您可以在设计时将其设置为truefalse 。 然后,如果将控件设置为true则控件将在运行时拖动。

 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 EnableDragValues = new Dictionary(); 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; } } } 

了解有关Extender Provider的更多信息

  • 扩展程序提供程序概述
  • IExtenderProvider
  • 如何:实现HelpLabel Extender Provider