10

我正在与一次又一次出现的情况作斗争,但我不确定我做事的方式是否错误,或者我是否可以以不同的方式做事。

一个例子:

我有一个 Windows 窗体,它有一个带有一些私有方法的 DataGridView 来执行数据网格的验证和解释鼠标右键单击 datagridview 等。这个窗体本质上是一个“抽象”类,从不直接实例化。

然后我从这个基类继承并以各种方式(模板模式)对其进行自定义,例如定义 datagridview 的列和特定于这些列的特定格式化方法等。

当我使用这些类时,基类公共方法形成我的接口,我可以实例化我想要的特定类型的 datagridview 并通过公共接口操作它。迷人的。

问题:

主要问题是,您实际上不能将 Windows 窗体类声明为抽象类,而不会导致 Visual Studio 设计器因为它无法实例化这些抽象类而产生不稳定。

一些解决方案:

目前,我正在“实现”我想要覆盖的基类中的那些方法:

    throw new NotSupportedException();

所以至少如果我忘记覆盖形成我的界面的这些方法之一。不过,这对我来说似乎很臭,我真的不喜欢它。

我玩弄的另一个解决方案是完全取消继承并定义一个接口(例如 IMyDataGrid)并在每个 datagridview 类中实现它(某种策略模式)。但是这里的问题是您失去了代码重用的好处,继承给您的意思是您必须创建许多不同的表单,在它们上放置一个 datagridview - 有效地将相同的代码复制并粘贴到每个表单中。坏的。

有没有更好的方法来实现这一目标?

4

3 回答 3

4

根据您的要求,有很多方法可以做到这一点。

  • 将表单内容放入一个用户控件中,该控件实现了一个接口来做自定义逻辑
  • 派生类DataGridView实现一个接口来执行自定义逻辑
  • 如前所述,使用带有virtual方法的具体类而不是使用abstract
  • ...

您必须选择最适合您需求的选项。在不知道你的问题被问到的领域和细节的情况下,我认为我们不能给你一个 100% 确定的答案。

于 2010-03-04T16:50:10.530 回答
1

查看此方法以了解如何创建所需的两个属性。

您需要以下属性和类型描述符类(取自 UrbanPotato 的代码)

// Source : taken from http://www.urbanpotato.net/default.aspx/document/2001 Seem to be down
// Allow the designer to load abstract forms
namespace YourNamespace
{

    // Place this attribute on any abstract class where you want to declare
    // a concrete version of that class at design time.
    [AttributeUsage(AttributeTargets.Class)]
    public class ConcreteClassAttribute : Attribute
    {
        Type _concreteType;
        public ConcreteClassAttribute(Type concreteType)
        {
            _concreteType = concreteType;
        }

        public Type ConcreteType { get { return _concreteType; } }
    }

    // Here is our type description provider.  This is the same provider
    // as ConcreteClassProvider except that it uses the ConcreteClassAttribute
    // to find the concrete class.
    public class GeneralConcreteClassProvider : TypeDescriptionProvider
    {
        Type _abstractType;
        Type _concreteType;

        public GeneralConcreteClassProvider() : base(TypeDescriptor.GetProvider(typeof(Form))) { }

        // This method locates the abstract and concrete
        // types we should be returning.
        private void EnsureTypes(Type objectType)
        {
            if (_abstractType == null)
            {
                Type searchType = objectType;
                while (_abstractType == null && searchType != null && searchType != typeof(Object))
                {

                    foreach (ConcreteClassAttribute cca in searchType.GetCustomAttributes(typeof(ConcreteClassAttribute), false))
                    {
                        _abstractType = searchType;
                        _concreteType = cca.ConcreteType;
                        break;
                    }
                    searchType = searchType.BaseType;
                }

                if (_abstractType == null)
                {
                    // If this happens, it means that someone added
                    // this provider to a class but did not add
                    // a ConcreteTypeAttribute
                    throw new InvalidOperationException(string.Format("No ConcreteClassAttribute was found on {0} or any of its subtypes.", objectType));
                }
            }
        }

        // Tell anyone who reflects on us that the concrete form is the
        // form to reflect against, not the abstract form. This way, the
        // designer does not see an abstract class.
        public override Type GetReflectionType(Type objectType, object instance)
        {
            EnsureTypes(objectType);
            if (objectType == _abstractType)
            {
                return _concreteType;
            }
            return base.GetReflectionType(objectType, instance);
        }


        // If the designer tries to create an instance of AbstractForm, we override 
        // it here to create a concerete form instead.
        public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
        {
            EnsureTypes(objectType);
            if (objectType == _abstractType)
            {
                objectType = _concreteType;
            }

            return base.CreateInstance(provider, objectType, argTypes, args);
        }
    }
}

将它们分配给您的抽象形式,如下所示:

[TypeDescriptionProvider(typeof(GeneralConcreteClassProvider))]
[ConcreteClass(typeof(MyAbstractConcreteForm))]
public abstract partial class MyAbstractForm : Form
{
}

创建一个继承自抽象形式的新类。此类将由 Visual Studio 实例化

public class MyAbstractConcreteForm: MyAbstractForm 
{
    public MyAbstractConcreteForm() : base() { }
}

这应该有效。

于 2009-08-20T15:14:49.627 回答
0

我也有同样的问题。
这个页面可能对您有所帮助,即使它只是一种解决方法:从抽象类继承表单(并使其在设计器中工作)

我还没有找到更好的解决方案,但似乎没有。因此,确实 Windows 窗体设计器正在迫使您调整您的类设计。

于 2010-03-04T14:51:47.170 回答