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

时间:2017-03-22 15:00:40

标签: c# winforms oop

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

我希望通过向名为SetDraggable()的Windows窗体控件添加扩展方法,将我学到的知识付诸实践,而后者又使用MouseDown和{{1} }事件移动控件。

一切正常,除了它只适用于特定控件的想法 - 在我的情况下,MouseMove

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; } } } } 访问这些事件。

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

据我所知,这个想法的主要缺陷是我 ASSUMING 所有派生类都将拥有我需要的事件;但是,由于代表形式的功能存在类似的情况,我希望有人可以权衡涉及对象或参数的案例。

4 个答案:

答案 0 :(得分:6)

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

理想情况下,您还希望避免使用这些静态字段,因为它可能具有多个并发可拖动字段; 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 = e.X + control.Left - mouseDownLocation.X;
        control.Top = e.Y + control.Top - mouseDownLocation.Y;
      }
    }
}

答案 1 :(得分:3)

这里不需要泛型。您只需使用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 = e.X + control.Left - MouseDownLocation.X;
        controlTop = e.Y + control.Top - MouseDownLocation.Y;
    }
}

答案 2 :(得分:1)

有两种可能性:

  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 :(得分:1)

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

扩展程序提供程序组件

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

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

示例

在这个例子中,我创建了一个DraggableExtender扩展程序组件。当您在表单上删除此组件的实例时,所有控件都将有一个名为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<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;
        }
    }
}

了解有关扩展程序提供程序的详情