【问题标题】:What is a good way to deal with multiple object types in the same form?以相同的形式处理多个对象类型的好方法是什么?
【发布时间】:2012-02-14 20:57:38
【问题描述】:

我有一个抽象基类和两个派生类。基类包含 6 个属性,所有这些属性都可以在表单上维护。

两个派生类都有 1 个额外的属性。这两个属性也可以在同一个表单上维护。

在我的表单中,我现在有这样的代码:

  btnSomething.visible = (myObject is DerivedA);
  pnlPanel.visible = !(myObject is DerivedA);

  if(myObject is DerivedA)
    myBindingSource.DataSource = myObject as DerivedA

  mySecondBindingSource = myObject;

我对这种方法不太满意,它闻起来很臭。所以我的问题是,什么是使这更加面向对象的简洁/好方法?因为未来有可能DerivedC进来……

我认为这种方法违反了 OCP 原则(可能还有其他原则)

【问题讨论】:

    标签: c# winforms oop


    【解决方案1】:

    一个疯狂的想法是让 UI 可扩展。 您可以使表单实现基本表单。

    然后在派生的表单类中,您只需为其模型类插入缺少的控件和行为。 在派生模型类或库中,您可以对正确的形式进行某种类型的绑定。

    一个好的方法是遵循一些 MVP 原则。

    希望它能以某种方式帮助你..

    【讨论】:

    • 我实际上做了一个这样的实现,但在我的解决方案中,我有不同的库实现了一组接口和基类,并且每个库都可以选择从主配置表单派生。然后在主应用程序中,我将显示默认表单或库实现的自定义表单。
    【解决方案2】:

    我将为每个需要根据底层类型运行的控件声明一个抽象布尔方法/属性。

    例如

    // to handle pnlPanel.visible = !(myObject is DerivedA);    
    abstract bool SupportsPanel{get;}
    

    至于您的绑定源,我还会提供一些虚拟的BindingSourceSecondBindingSource 属性。

    也许像(纯粹是一个例子)

    public abstract class BaseClass
    {
        // All your exising class declaration here
    
        public virtual object BindingSource
        {
            get
            {
                // By default, a BaseClass is not valid as a binding source
                return null;
            }
        }
    
        public virtual object SecondBindingSource
        {
            get
            {
                // By default, a BaseClass is a valid Second Binding source
                return this;
            }
        }
    }
    
    public class DerivedA : BaseClass
    {
        // All your exising class implementation here
    
        public override object BindingSource
        {
            get
            {
                // For DerivedA, the data sourse is itself.
                // other classes might have their own implementations.
                return this;
            }
        }
    
        // No need to override SecondBindingSource as the BaseClass one works as expected.
    
    }
    

    因此,您的代码可能不再关心对象类型,而是如下所示:

    myBindingSource.DataSource = myObject.BindingSource;
    mySecondBindingSource = myObject.SecondBindingSource;
    

    希望这会有所帮助。

    【讨论】:

    • 您能否提供一个(虚拟)示例来说明如何处理绑定源?
    • @Martijn 我已经为你更新了我的帖子。这与我一开始的想法略有不同,但想法是一样的:只将您的对象用作BaseClass,并将特定的内容仅封装在派生类中。
    【解决方案3】:

    你可以在这里使用多态和继承:

    定义接口

    interface ICommonFeatures
    {
        bool ContainsFoo {get;}
        //yak-yak
    }
    

    然后你的派生类实现它

    class DerivedA: ICommonFeatures
    {
        bool ContainsFoo {get {return true;}}
        //so-and-so
    }
    class DerivedB: ICommonFeatures
    {
        bool ContainsFoo {get {return false;}}
        //this-and-that
    }
    

    而当你使用它时,你只处理界面

    ICommonFeatures foo = new DerivedB();
    
    btnSomething.visible = foo.ContainsFoo;
    pnlPanel.visible = foo.Prop2;
    myBindingSource.DataSource = foo.CurrentDataSource
    

    【讨论】:

    • 我在想这个方向,但在你的情况下,我可以将foo.Prop1 读成foo.ShowButtonSomething 吗?所以基本上,每个依赖于 Derived 类的 GUI 控件,你都创建一个布尔属性?
    • 是的,但是在界面中您应该放置逻辑负载,而不是 UI。所以ShowButtonSomething => ContainsFoo。然后在您的逻辑中更深入地检查该类是否包含 foo 那么您想显示一个按钮。这种方法会将逻辑封装在类中,并且表示将与模型分离(这始终是一个好主意)。另一件事是您的 GUI 控件应依赖于抽象 - 接口或抽象类,如果您愿意的话。
    • 我想我明白了,所以在ICommonFeatures 中我可以拥有像bool ContainsCompanyAddress 这样的属性,并在我的表单中设置txtCompanyAddress.visible = foo.ContainsCompanyAddress?
    • 您将如何处理数据绑定?我对DerivedADerivedB 有不同的数据绑定
    猜你喜欢
    • 2014-09-08
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    • 2010-12-02
    • 2011-12-14
    相关资源
    最近更新 更多