Deriving from Control but hiding properties
Yes, I am aware this has been asked and answered before, but nothing fully addresses several issues.
References
- Hiding inherited members
- Hiding unwanted properties in custom controls
Essentially, I'd like to derive from Control
but without exposing certain properties. In my case, I am designing a Container
, which will contain many Element
s, arranged in a programmatic way. I'd like (and may require) that Element
derives from Control
, in order to get all of the drawing and events, but also be able to add Element
objects to the Container
's Children
collection (for obvious reasons).
The only problem is, if Element
derives from Control
, then its Location
property is exposed, and the user could easily destroy the programmatic placement. Naturally, my instinct is to keep all the goodies that come along with Control
and hide/disable the Location
.
I'm aware of the following:
- This violates the SOLID Liskov substitution principle (design by contract)
- This has been asked before
- I could encapsulate
Control
in myElement
and forward all of the calls to those ofControl
- This would be a ridiculous amount of work, and for no real gain.
- The encapsulating
Element
would still not be type-compatible withControl
and could not be used in many cases where a derived form ofControl
would.
I feel there must be some more elegant solution to this (seemingly) recurring problem. Thank you in advance!
Edit
The only way I see to accomplish this is:
public class Element : Control
{
public new Point Location { get; private set; }
}
But then, I have the problem of, how does my container class set the Element
s location? In C++ one would use a friend class. I suppose if I kept this control in its own assembly, I could mark the setter a开发者_如何转开发s internal
.
Edit
For those looking to do something this, my solution was: to not do it. Instead, I made a custom object and implemented all of the clicking / dragging, etc myself. It was just too complicated to try and use a Control, while excluding functionality like Location.
You cannot completely prevent the Control.Location property from being updated by a determined programmer. Using the new keyword will change the accessiblity of the property but it only applies to Element and Element derived class references. If the programmer casts the instance reference to the base class of Control then they can access the original Location property and so can update the value. If you do not care about this workaround then simply go ahead and use the override that you have shown in the question.
To truely prevent anyone setting the location except your container then you would have to do a little more work in the following way. Any change to a controls Location or Size is actually implemented by making a call to the virtual SetBoundsCore method. So you could override this virtual method in your Element class and ignore all attempted changes except when an instance variable such as AllowChange is set. Then in your container you set that AllowChange just before updating the size/location and then reset it back afterwards. Now the only way to change the size is by your container or if the programmer uses the little known update property.
It doesn't sound like you're trying to create a replacement method (since you worry about the replaceability of "Location") so either you create a pair of methods for "Location" that merely stub out the fact that they can't update the underlying one (so you're creating an override for the properties you don't want affected) or you ignore the Liskov SOLID principles because that's not what you're doing.
The best way to go about this is going to be the use of an internal control object. This is completely untested and I'm not yet certain about the accessibility of all interfaces, but this is the way I would start out:
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.AutoDispatch)]
public class Element : Component, IDropTarget, ISynchronizeInvoke, IWin32Window,
IBindableComponent, IComponent, IDisposable
{
private Control _control;
}
Here you have the ability to completely hide or expose any properties you choose. Any of your interfaces members can simply refer to the internal control properties.
HTH
精彩评论