【问题标题】:Encapsulating a Windows.Forms.Button封装一个 Windows.Forms.Button
【发布时间】:2011-02-11 16:28:06
【问题描述】:

我想定义一种特殊的按钮,它只允许两个可能的标签:“ON”和“OFF”。我决定从 Windows.Forms.Button 继承来实现这个,但现在我不知道我应该如何执行这个规则。我应该像这样覆盖 Text 属性吗?

public override string Text
{
    set
    {
        throw new InvalidOperationException("Invalid operation on StartStopButton!");
    }
}

我看到的问题是我违反了所有按钮都应该具备的合同。如果任何代码尝试类似

foreach (Button button in myForm) {
    button.Text = "123";
}

如果我在表单上有我的任何特殊按钮,他们会得到一个例外,这是不可预料的。首先,因为人们认为属性只是“公共”变量,而不是方法,其次,因为他们习惯于使用和设置任何他们想要的按钮,而不必担心异常。

我应该让 set 属性什么都不做吗?这也可能导致尴尬的结果:

myButton.Text = "abc";
MessageBox.Show(abc); //not "abc"!

OO 世界的一般想法是在这种情况下使用组合而不是继承。

public class MySpecialButton : <Some class from System.Windows.Forms that already knows how to draw itself on forms>

    private Button button = new Button(); //I'd just draw this button on this class
                                          //and I'd then only show the fields I consider
                                          //relevant to the outside world.
    ...
}

但要使 Button 在表单上“活动”,它必须从某个特殊类继承。我看过 Control,但它似乎已经定义了 Text 属性。我想理想的情况是从某种甚至没有定义 Text 属性的类继承,但它具有可用的位置、大小等属性。在层次结构的上层,在 Control 之后,我们有 Component,但这看起来像是一个非常原始的类。

关于如何实现这一点的任何线索?我知道这是一个很长的帖子:(

谢谢

【问题讨论】:

    标签: c# .net winforms oop


    【解决方案1】:

    您可以对设计器隐藏 Text 属性,然后添加一个新属性,该属性采用只有两个值的简单枚举:

    public enum MyButtonLabelValue
    {
        On,
        Off
    }
    
    public class MyButton : Button
    {
        [Browsable(false)]
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                // either do nothing or only accept "On" and "Off". I know you're concerned about violating the 
                // contract, but that would be preferrable to not having a public .Text property at all
            }
        }
    
    
        public MyButtonLabelValue LabelValue 
        {
            get
            {
                return Text == "On" ? MyButtonLabelValue.On : MyButtonLabelValue.Off;
            }
            set
            {
                base.Text = value == MyButtonLabelValue.On ? "On" : "Off";
            }
        }
    }
    

    【讨论】:

    • 似乎不是一个好主意。你仍然需要重写 Text 的 mutator 并让它什么都不做。
    • 是的。但是当我考虑了一段时间后,我觉得这不会出现问题,因为任何使用您的自定义按钮的人都会确切地知道他们在做什么,并且不希望文本是除此之外的任何东西开或关。
    【解决方案2】:

    您是否考虑过按照您描述的方式使用 Control 但不使用 Control 的 Text 属性来设置 Button 的 Text 属性?

    许多 Windows 控件都具有在任何地方都不使用的 Text 属性。

    【讨论】:

    • 没想到。如果没有更好的解决方案,我会这样做。
    【解决方案3】:

    编辑:我已经为问题发起者添加了实际代码,并不太理解我的观点。 “新建按钮代码”

    namespace WindowsFormsApplication1
    {
        public partial class Component1 : Component
        {
            public Component1()
            {
                InitializeComponent();
            }
    
            public Component1(IContainer container)
            {
                container.Add(this);
    
                InitializeComponent();
            }
            bool bIsOn = true;
            public bool On
            {
                set 
             {
             bIsOn=value;
    
             if (bIsOn)
                 button1.Text = "ON";
             else 
                 button1.Text = "OFF";
    
             button1.Invalidate(); //force redrawing
            }
    
            }
            public void AddToForm(System.Windows.Forms.Form form)
            {
                form.Controls.Add(this.button1);
            }
        }
    }
    
    The main Form code : 
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                Component1 onOffButton = new Component1();
    
                onOffButton.On = true;
    
                onOffButton.AddToForm(this);
    
    
    
    
            }
        }
    }
    

    你不能在这里使用继承,因为从 OOP 原则 POV 来看它没有意义。

    限制Text属性输入违反了原界面。 相反,通过使用组合定义您自己的接口和实现,并通过简单的布尔测试设置值,如下所示:

    class OnOffButton : I_OnOffButton
    {
         Button regularButton;
         bool bIsOn = true;
         public bool On
         {
          set 
             {
             bIsOn=value;
    
             bIsOn?(regularButton.Text = "ON"):(regularButton.Text = "OFF")
    
             regularButton.Invalidate(); //force redrawing
            }
         }
    ...
    
    }
    

    希望这会有所帮助!

    【讨论】:

    • 它没有。我认为您错过了线程的全部要点:要在表单上显示按钮,您必须从 Button 或 Component 继承,并且在这两种情况下,您都将不得不违反 Text 属性合同。
    • 你确定我是这里漏掉重点的人吗?您真的认为不可能将成员对象regularButton(即System.Windows.Forms.Button)添加到Form 中吗?坦率地说,您应该先重新阅读我的代码 sn-p,因为您的评论似乎没有反映完全理解。
    • 我一定是遗漏了一些东西,因为我看不到如何将一个没有实现控件的类的实例放在表单中,首先..
    • 这是我在评论中所说的吗?还是我在这方面提到了 Button 类型的成员对象?请在回答前仔细阅读。
    • 这是正确答案。这是封装的工作,而不是继承。
    猜你喜欢
    • 2012-05-06
    • 1970-01-01
    • 2018-07-26
    • 2010-11-29
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    相关资源
    最近更新 更多