Hiding base class dependency property in derived control - c#

I am writing a set of custom TextBox controls for different data types. I do not only want the text to be validated (which I do) but also store the input in a property of adequate type.
So for example I have an UnsignedIntegerBox which inherits from TextBox, should store the input in an "uint UnsignedInteger" property, whith default set in xaml. It validates the input in the OnPreviewTextInput event. The OnTextChanged is used to update the UnsignedInteger from Text.
Question: Is there any way to hide the TextBox.Text property so that it is not exposed (and cannot be used) in XAML ?

I would suggest to create new CustomTextbox class that could inherit from UserControl / Control class, create a DP property on it as you want and bind it to a TextBox in Control template / Content of your new control. So that you still use TextBox for input/visuals but from code point it is hidden behind your new CustomTextBox class

When you extend a superclass, then the subclass is the superclass. You can never remove any members of the superclass. You can change the behavior by overriding virtual members or hiding accessible members. Maybe you should revisit the inheritance rules of OO languages like C# to understand the concept.
This is what you can do, where 3, 4, 5 are the only clean and useful solutions:
When you hide the Text property to declare it private, then you would get a XAML error, due to the type inference of the XAML engine. This way the Text property is available via Intellisense, but you can't set it. But in C# the next accessible member is chosen. The member lookup behavior will automatically exclude the new hiding private Text property and will then find the public superclass member.
class MyTextBox : TextBox
{
// Only has an effect in XAML
new private string Text { get; set; }
}
Even if this would work, you could always set the static DependencyProperty using the DependencyObject.SetValue method. Hiding is also only hiding and not removing. You can always cast to the superclass to get access to the original Text property.
You can override the DependencyProperty meta data to disallow data binding
public class MyTextBox : TextBox
{
static MyTextBox()
{
TextBox.TextProperty.OverrideMetadata(
typeof(MyTextBox),
new FrameworkPropertyMetadata(default, FrameworkPropertyMetadataOptions.NotDataBindable));
}
}
This will throw an exception if you try to bind to the Text property.
But you can still set the value via assignment.
Use composition over inheritance. You can the design the class API to your requirements and delegate the functionality to the inaccessible composition type.
// Alternatively extend Control
class MyTextBox : TextBoxBase
{
private TextBox TextBox { get; }
public int Number
{
get => return this.TextBox.Text;
set
{
if ( IsValid(value))
{
this.TextBox.Text = value;
}
}
}
Extend the class in the type hierarchy that does not declare the unwanted members. In your case this would be TextBoxBase.
// Will not have a Text property
class MyTextBox : TextBoxBase
{
}
Throw a NotSupportedException exception to make using the inherited members impossible. The developer is immediately notified that e.g., the Text property is not availble. May not be the best solution for public libraries.
public class MyTextBox : TextBox
{
static MyTextBox()
{
TextBox.TextProperty.OverrideMetadata(
typeof(MyTextBoxl),
new FrameworkPropertyMetadata(null, OnCoerceText));
}
private static object OnCoerceText(DependencyObject d, object baseValue)
=> throw new NotSupportedException();
}

Related

ObservableCollection is Null when I try to add TextBlocks through XAML to a custom control

In my WPF Project, i am trying to create an ObservalbeCollection dependency property inside a custom control. I am not sure if i am doing it the right way but i am using the below code :
public static readonly DependencyProperty MenuOptionsDependency = DependencyProperty.Register("MenuOptions", typeof(ObservableCollection<TextBlock>), typeof(DropDownButton));
public ObservableCollection<TextBlock> MenuOptions
{
get
{
return (ObservableCollection<TextBlock>)GetValue(MenuOptionsDependency);
}
set
{
SetValue(MenuOptionsDependency, value);
}
}
The problem arises when i try to add TextBlocks through XAML to this control as follows :
<local:CustomControl1 x:Name="cmm">
<local:CustomControl1.MenuOptions>
<TextBlock/>
</local:CustomControl1.MenuOptions>
</local:CustomControl1>
This throws a design time error saying :
Collection 'CustomControl1'.'MenuOptions' is null
After going through the accepted answer on this SO post, i understand ObservableCollection, being a reference type, will be null as it will be default value. I read through the answer but i am still unclear/unsure on how to implement the solution in my situation.
I need to be able to add objects to the collection through XAML(and also through C#). I would really appreciate if someone points out where i am missing/what i am doing wrong.
You must never set the default value of a collection type dependency property to something else than null. When you assign a non-null default value by (static) property metadata, all instances of your control will use the same collection object.
Instead, set a default value in the control's constructor by SetCurrentValue:
public DropDownButton()
{
SetCurrentValue(MenuOptionsDependency, new ObservableCollection<TextBlock>());
}
Besides that, there is a strict naming convention, according to which the dependency property identifier field must be named as the property with a Property suffix. So your MenuOptionsDependency should actually be named MenuOptionsProperty.
It's also unclear whether the property type really needs to be ObservableCollection. You don't seem to register a CollectionChanged event handler anywhere, which indicates that your control is not supposed to react on such changes.
Consider a property declaration like this:
public DropDownButton()
{
SetCurrentValue(MenuOptionsProperty, new List<TextBlock>());
}
public static readonly DependencyProperty MenuOptionsProperty =
DependencyProperty.Register(
nameof(MenuOptions), typeof(IEnumerable<TextBlock>), typeof(DropDownButton));
public IEnumerable<TextBlock> MenuOptions
{
get { return (IEnumerable<TextBlock>)GetValue(MenuOptionsProperty); }
set { SetValue(MenuOptionsProperty, value); }
}

Hide some properties in PropertyGrid at run-time

I am doing a project that allows user to customized the properties of a Control. I have a form that has a control like Label, TextBox, Button and PropertyGrid control. When the user clicks on the Label i am showing the properties of the Label in the ProeprtyGrid which is all working fine using below code:
propertyGrid1.SelectedObject = SelectedControl;
But I just want to show some properties like BackColor, Font, ForeColor, Text. Is it possible to hide the properties since I don't want user to change it or show to them? If yes, how?
I believe you are looking for custom type descriptors.
While the other answer is sharing correct information about Browsable attribute and BrowsableAttributes of PropertyGrid, but I'd say it's not a proper practical solution for the problem.
It's not practical to set Browsable attribute, or any other custom attributes for existing control classes like Label, Button, and so on. Because in this way, the op needs to override all properties of those classes and decorate them with suitable attribute. And even worst, not all propertied are overridable.
What's the practical solution?
As I mentioned earlier, I believe you are looking for custom type descriptors. You can provide metadata about an object assigning a new TypeDescriptor or implementing ICustomTypeDescriptor or deriving from CustomTypeDescriptor.
Example
Here for example, I create a CustomObjectWrapper class deriving from CustomTypeDescriptor which accepts an object in constructor. This way I can simply filter the properties of the wrapped object by overriding GetProperties.
Then instead of assigning button1 to PropertyGrid, I wrap it in CustomObjectWrapper and assing the CustomObjectWrapper to property grid. This way it just shows the filtered properties and the properties are actually come from button1.
Here is the implantation:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class CustomObjectWrapper : CustomTypeDescriptor
{
public object WrappedObject { get; private set; }
public List<string> BrowsableProperties { get; private set; }
public CustomObjectWrapper(object o)
:base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
{
WrappedObject = o;
BrowsableProperties = new List<string>() { "Text", "BackColor" };
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Where(p=>BrowsableProperties.Contains(p.Name))
.Select(p => TypeDescriptor.CreateProperty(
WrappedObject.GetType(),
p,
p.Attributes.Cast<Attribute>().ToArray()))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
And as usage:
propertyGrid1.SelectedObject = new CustomObjectWrapper(button1);
You can simply add new property names to BrowsableProperties of the CustomObjectWrapper. It's a public property.
UPDATE
Please note this is only useful for Hiding properties (when you can). Reza Aghaei answer is actually the correct answer.
I'll leave this here as it's suitable for the other case when you just simply want to hide a property when you have access to it.
Original
Easiest way is probably to use
[Browsable(false)]
BrowsableAttribute Class
Specifies whether a property or event should be displayed in a
Properties window.
[Browsable(false)]
public int SecretSquirrels
{
get; set;
}
Also as pointed out by Marc Gravell, there is also
PropertyGrid.BrowsableAttributes Property
Gets or sets the browsable attributes associated with the object that
the property grid is attached to.

How can I hide inherited properties of an ASP.NET custom control?

Here is my first experience creating a custom control. My real example is much larger, but this is boiled down for clarity. Ultimately, I need to hide as many properties of the custom control as possible so that when I share my new control with the rest of my team, they need only worry about the few properties that are required.
I have a control called TimeNow which inherits System.Web.UI.WebControls.Literal and basically just prints the current time on the web page:
public class TimeNow : Literal
// Set to private so Text is hidden from the editor.
private string Text
{
get;
set;
}
protected override void Render(HtmlTextWriter writer)
{
// Get and write the time of now.
}
This works, but it seems clunky. I no longer see Text available in intellisense when I drop the control on a web page, but I do receive a warning that my Text is hiding the inherited Text. Is there a better way to hide the Text property?
There should be more to that warning message, suggesting you use the new keyword if you really intend to hide the inherited member, do what it says:
public class TimeNow : Literal
{
new private string Text
{
get;
set;
}
}
Try this:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
private string Text
{
}
I think you're doing something wrong if you derive from a Literal that behaves like an ITextControl (Literal has implemented this Interface) and then you try to remove the essential Text property? This is like deriving from cat but don't want to let them do "meow" and force them flying like a duck. You are searching for the Animal class instead, I think.
I don't know much about ASP.NET (only .Net for the Desktop). Maybe it is possible to use composition instead of inheritance (if you not really need the Literal -> "Cat") and you can inherit from System.Web.UI.Control (-> "Animal") instead.
public class TimeNow : System.Web.UI.Control
{
// no need to do something here with the Text property, is not defined
}
or with composition
public class TimeNow : System.Web.UI.Control
{
private readonly Literal literal;
public TimeNow()
{
this.literal = new Literal();
// ... and set the Text etc., no one else can access
}
// or something like this
public TimeNow(ILiteralFactory literalFactory)
{
// the factory is just an example... don't know your context but this way your newly created literal can't be accessed
this.literal = literalFactory.CreateNewLiteral();
// do what you want with the text control, e.g. store internally
// Clone() the Literal etc.
// the
}
}
Update: Had a very quick look into the MSDN and maybe a Content Control is the thing you are looking for, instead of the Literal. (sorry if wrong, writing Desktop Apps)

How to make a User control property of type Collection<MyClass> editable in Form Designer?

Today at work, I stumbled upon a problem that was driving me nuts.
Basically my goal is this:
I have a UserControl1, with a field of the type Collection<Class1> and a corresponding property Collection<Class1> Prop. Like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = null;
// later changed to:
//private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[DefaultValue(null)]
[Description("asdf")]
public Collection<Class1> prop
{
get { return field; }
set { field = value; }
}
}
// later added:
//[Serializable]
public class Class1
{
private bool booltest; public bool Booltest { get...set...}
private int inttest; public int Inttest { get...set...}
}
If you already know what I screwed up: no need to read the rest. I am going to describe what exactly I did.
Now I put the UserControl onto a random Form and change the Prop property. A generic "Collection Editor" appears, like the one used for the columns and groups in a listview control. I can enter data as expected. However, when I click OK, the data is gone.
It took me over hour to figure out that I actually have to instantiate my field: private Collection<MyClass> field = new Collection<MyClass>();. Very good, only that the designer entered superspazzing mode. Cascading nightmare error message that can be reduced to: "You must put [Serializable] before your Class1." After doing that I could actually put my UserControl1 on the Form again.
But that only works once. When opening the designer of the Form where I use the UserControl1 after editing something, it gives me an error:
Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'.
Well. The Error List says:
Warning: ResX file Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'. Line 134, position 5. cannot be parsed.
The designer tries to fetch the Property's data from the resx file. Removing the resx file "solves" that exactly once.
The Form can now be displayed again, with my UserControl1. The Collection property is editable, and it is being saved. It actually works. Once. Whenever I change something and then try to open the Form's designer again, the above error occurs again. I can delete the resx file, but that will of course also delete my data.
Relevant resources that helped me so far (among a ton of not so helpful search results):
http://www.codeproject.com/Answers/190675/Usercontrol-with-custom-class-property#answer1
http://www.codeproject.com/KB/cs/propertyeditor.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=94
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx
(I also tried implementing ISerializable and overriding GetObjectData with
{ info.AddValue("testbool", testbool); info.AddValue("testint", testint); }
didn't help either (I also tried the property names instead of the field names))
Sorry for writing this like a bad horror novel btw.
What you want is a design time support with CodeDom serialization. You do not need SerializableAttribute or ISerializable, those are for binary serialization.
Since you want to serialize the collection, you must tell the designer to serialize it as such. That is done with the DesignerSerializationVisibiliby attribute - value of Content tells the designer to serialize property contents rather than property itself. Contents of the property should of course be CodeDom serializable, which simple classes with simple properties are by default.
So if you change your UserControl1 class like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[Description("asdf")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<Class1> prop
{
get { return field; }
}
}
... it should do the trick. Oh and collection properties are usually not writeable, although that is not mandatory. But serializer expects the collection property to be initialized, that is why you had to add initialization for the field.
Another note, if you do not want that your property is marked with bold in the property editor, you can specify a more complex "default value" through a special method ShouldSerializePropertyName, which can even be private. Like so:
private bool ShouldSerializeprop()
{
return (field.Count > 0);
}
Now your property will only be bold when it is not empty. But I digress, this was not a question :)
The perfect exemple is this:
public partial class SCon : UserControl
{
public SCon()
{
InitializeComponent();
if (Persoanas == null)
{
Persoanas = new List<Persoana>();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Persoan> Persoanas { get; set; }
}
[Serializable]
public class Persoan
{
public int Id { get; set; }
public String Name { get; set; }
}
Just change Collection<> to List<>

When do I need to use automatic poperties and when properties with propertychanged event?

I am using wpf and its C sharp!
I have this in my Animal.cs clas
private string _animalName;
public string AnimalName
{
get { return _animalName; }
set
{
if(_animalName!= value)
{
_animalName= value;
this.NotifyPropertyChanged("AnimalName");
}
}
}
I could also write:
public string AnimalName {get;set;}
There is no difference in binding and validation. Everythings works as before when I exchange the code.
Is this due to the fact that I only create new animals but I do not allow to update the animals name in my application ?
So I need to call the propertyChanged("AnimalName"); only when I want to change its property value?
I am a c# beginner ;)
If your object has an updateable property (setter) that will be bound to a control then you need to ensure to let the bound control know of any changes to that property via INotifyPropertyChanged. However, if you have a readonly property and/or a property that's not going to be used in a data-binding scenario then you don't care about implementing or calling NotifyPropertyChanged method from within that property's setter in which case you can use automatic properties.

Categories

Resources