views:

70

answers:

3

I want to define a special kind of button that only allows two possible labels: "ON" and "OFF". I decided to inherit from a Windows.Forms.Button to implement this but now I don't know I how should enforce this rule. Should I just override the Text property like this?

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

The problem I see with this is that I am breaking the contract that all buttons should have. If any code tries something like

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

they will get an Exception if I have any of my special buttons on the form, which is something that isn't expectable. First, because people think of properties just as "public" variables, not methods, second, because they are used to using and setting whatever they want to buttons without having to worry with Exceptions.

Should I instead just make the set property do nothing? That could also lead to awkward results:

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

The general idea from the OO world is to in this kind of cases use Composition instead of inheritance.

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.
    ...
}

But to make the Button "live" on a form it must inherit from some special class. I've looked on Control, but it seems to already have the Text property defined. I guess the ideal situation would be to inherit from some kind of class that wouldn't even have the Text property defined, but that'd have position, size, etc properties available. Upper in the hierarchy, after Control, we have Component, but that looks like a really raw class.

Any clue about how to achieve this? I know this was a long post :(

Thanks

+1  A: 

You could hide the Text property from the designer, and then add a new property which takes a simple enumeration with only two values:

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";
        }
    }
}
Charles
Doesn't seem that good of an idea. You still have to override Text's mutator and make it do nothing.
devoured elysium
True. But when I thought about it for a while, I didn't feel that would be likely to present a problem since anyone who uses your custom buttom will know exactly what they're doing and won't want the text to be anything other than On or Off.
Charles
+2  A: 

Have you considered using Control in the way you described but not using the Text property of the Control to set the Text property of the Button?

Lots of Windows Controls have Text properties that aren't used anywhere.

pdr
Haven't thought of that. If there is no better solution that's what I'll be doing.
devoured elysium
A: 

EDIT : I've added the actual code for the question originator didn't quite understand my point. The "New button code"

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);




        }
    }
}

alt text

You must not use inheritance here because it would make no sense from OOP principles POV.

Limiting Text property input violates the original interface. Instead, define your own interface and implementation by use of composition, and have it set values by simple boolean test, like this:

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
        }
     }
...

}

Hope this helps!

Jas
It doesn't. I think you are missing the whole point of the thread : to have a button show on a form you must either inherit from a Button or a Component, and in both cases you are going to have to violate the Text property contract.
devoured elysium
Are you sure I'm the one missing the point here ? Do you actually think it's impossible to add the member object regularButton (Which is System.Windows.Forms.Button) to a Form? Frankly, you should re-read my code snippet first, because your comment does not seem to reflect a full understanding.
Jas
I must be missing something, as I can't see how you can put an instance of a class that doesn't implement a Control in a Form, in the first place..
devoured elysium
Is that what I said in my comment? Or did I mention the member object of type Button in this regard? Read carefully before answering, please.
Jas
I must have a button that can be PLACED IN A FORM. Otherwise it wouldn't be a button. See the answers of other posters in the thread.
devoured elysium
@Jas I think what the OP may mean by "place in form" means using the VS designer, which your solution as is does not do. Conceptually I like it, however.
Davy8
@devoured elysium - OK you genius, you sound SO CONFIDENT, that you actually made me spend 10 minutes coding this for you (check the EDITED ANSWER) , so that you can take a look at the code and copy-paste it in your project.Oh yeah, and it can be 'PLACED IN A FORM', BUT you have to THINK, and be a bit skilfull, insteaf of being a cocky, arrogant little piece of ...
Jas
@Davy8 - He should have defined his question better then. I am not here to guess anyone's thoughts, with all due respect..
Jas
@Jas regardless of that even though it's conceptually clean, in practical terms it's not a very good solution. The OP mentions " I guess the ideal situation would be to inherit from some kind of class that wouldn't even have the Text property defined, but that'd have **position, size, etc properties available.**" (emphasis mine) which come for free with inheritance. With composition you'd either have to duplicate all those properties (messy), expose the real button (defeats the original intent), or hard-code it (not very reusable). Additionally even though you can add it to the(continued...)
Davy8
form, it breaks the expectation of anyone familiar with other winforms controls that to add the button they have to use a completely new method. Additionally as it stands there's no way to position the button in the form, add any kind of margin or padding, or basically do anything useful with it other than technically barely fitting the minimum stated requirements, ignoring the implied requirement that being a special case of a `Button` one should be able to treat it in all other respects like a button. Even the very basic Click event would need to be bubbled up to get basic functionality.
Davy8
@Davy8 - So why don't you share your solution to the problem here?
Jas
Because the other two answers provided already cover it. As @pdr described in his answer there is already precedence in the framework of `Control`s not disregarding the `Text` property even though it's present and @Charles shows a way to hide it from the designer view. Like I said, I like the concept of your answer but in practical terms it just doesn't do it. You either lose too much built-in functionality or you end up duplicating everything for small gains in semantics.
Davy8
Also I feel you're a little over-defensive of your answer and it feels like your're more focused on showing your answer is correct than you are on trying to help.
Davy8
I designed my solution based on preserving the contract, that is why I am defending it. As Charles admits himself in the code comment, his solution is effectively violating the inheritance contract.In the words of OP himself (in the question text): "The problem I see with this is that I am breaking the contract that all buttons should have.". Dixi.
Jas