开发者

Deriving from Control but hiding properties

开发者 https://www.devze.com 2023-03-18 19:10 出处:网络
Yes, I am aware this has been asked and answered before, but nothing fully addresses several issues. References

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 Elements, 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 my Element and forward all of the calls to those of Control
    • This would be a ridiculous amount of work, and for no real gain.
    • The encapsulating Element would still not be type-compatible with Control and could not be used in many cases where a derived form of Control 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 Elements 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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号