views:

936

answers:

4

I have been trying to build a user control with some custom properties set in the designer. However the control involves some interop code and settings which shouldn't be adjusted at runtime. Is there a way to stop the values being changed after they have been initially set by the designer code?

+1  A: 

You could throw an exception inside the property setter?

public int SomeProperty {

   set {

      if(designerComplete) {
          throw new IllegalOperationException();
      }

   }

}

Set designerComplete as a class variable - set it to true after the InitializeComponent method is called in the constructor.

Kazar
This might work if you also add a check to see whether or not you're in design mode.
Kevin Pullin
Is there a #define set if the code is running inside the form designer? If so, that would probably be the way to go.
Kazar
@Kevin: petebob796 doesn't actually mean designer code, he means the runtime invocation of InitializeComponent, which will set the property to the value set in the form designer. And so checking for design mode won't do what he wants.
Michael Petrotta
Kevin is correct I meant the runtime invokation of the designer will set the property (if not the default) past then I want it to be stopped from changing. I thought there might be a built in way to do this but if not I can set a flag once the designer code has run, it's just a bit more messy.
PeteT
+5  A: 

Are you able to modify the property definition? One approach is, add a sentinel to the property setter, and allow only one set operation (which would usually be done by InitializeComponent()):

private int _myProperty;
private bool _isMyPropertySet = false;
public int MyProperty
{
    set
    {
     if (!_isMyPropertySet)
     {
      _isMyPropertySet = true;
      _myProperty = value;
     }
     else
     {
      throw new NotSupportedException();
     }
    }
}
Michael Petrotta
Actually, this is cleaner than my solution - +1
Kazar
This is the method I am thinking of using. I just thought I may be missing something built into the language and would like to use best practice.
PeteT
There's nothing else that I can think of, without knowing more about the control you're wrapping. Even knowing that, I probably wouldn't be of much help, but someone else might...
Michael Petrotta
What happens if the control's property is changed twice at runtime?
Michael Petrotta
Basically i'm wrapping a web cam interface into a control for taking images and cropping them. I can scale the input so take a 1.3MP webcam and scale the displayed output to say 320x240 while still taking the shot at 1.3mp. The scale can only be set at design time due to the com interop so adjusting the scale property adjusts the size of the control making the webcam fall outside the controls area. Hope that makes sense.
PeteT
+1  A: 

The WinForms architecture provides a built-in way to test whether code is currently being executed in design mode - the Component.DesignMode property.

So you probably want an implementation something like this:

private int _foo;

public int Foo
{
    get { return _foo; }
    set
    {
        if (this.DesignMode)
            throw new InvalidOperationException();

        _foo = value;
    }
}
Noldorin
This is the correct answer, however the DesignMode property is available directly on the user control, don't need to go to the Site for it.
benPearce
DesignMode doesn't do what you think it does. It will return true if the form is being loaded from within the Visual Studio designer. It will return false if the form is loaded at runtime from within the "designer-created" InitializeComponent(). It's the latter that the OP is interested in.
Michael Petrotta
@Michael: Are you sure that's the case? I've used this approach before for the same purpose and it's worked absolutely fine.
Noldorin
@benPearce: You're absolutely right. Not sure how I missed that. :)
Noldorin
@Noldorin: Yep, I'm sure. Give it a whirl yourself (I just did, just in case). Add a label to your form, and in InitializeComponent, after the label's constructed, this.label.Text = this.DesignMode.ToString();
Michael Petrotta
(WinForms only, I have no idea what happens in WPF. I'd assume the same.)
Michael Petrotta
@Michael: Yeah, it seems you're right. In this case, I would tend to recommend an Initialize method that sets a flag, rather than a property. It gives you the advantage of not having to mess with the property setter (or more than one potentially), so that even automatic properties should be fine.
Noldorin
@Noldorin: yes, you have a good point. A simple method call that the designer stays away from is a good idea.
Michael Petrotta
This isn't actually quite what I was meaning, I'm aware of the design mode flag. I actually want to allow the setting to work in design mode to be changed. But at runtime the property should not be set again, remember the designer code will set it once on initialization.
PeteT
@petebob796: Did you see my latest comment? I would definitely suggest this approach.
Noldorin
+1  A: 

Michael provided a great answer, and it will solve your problem at runtime. However, at design time, if you need to be able to change that value more than once (it is design time, and the probability is likely high), then you will want to combine the DesignMode check with Michaels example:

private int _myProperty;
private bool _isMyPropertySet = false;
public int MyProperty
{
    set
    {
        if (this.DesignMode || !_isMyPropertySet)
        {
                _isMyPropertySet = true;
                _myProperty = value;
        }
        else
        {
                throw new NotSupportedException();
        }
    }
}

Now you will be able to edit this value to your hearts content during design, without running into that NotSupportedException() and getting a botched designer on the second set.

jrista
Yes, after all that discussion about DesignMode, that's an important addition to that setter.
Michael Petrotta