PropertyGrid... for users Id like to leave only several of them. But now I see all, and users would be confused when see something like Dock or Cursor and such...
Hope it's clear for now...
Use this attribute:
[Browsable(false)]
public bool AProperty {...}
For the inherited properties:
[Browsable(false)]
public override bool AProperty {...}
Another idea (since you are trying to hide all base class members):
public class MyCtrl : TextBox
{
private ExtraProperties _extraProps = new ExtraProperties();
public ExtraProperties ExtraProperties
{
get { return _extraProps; }
set { _extraProps = value; }
}
}
public class ExtraProperties
{
private string _PropertyA = string.Empty;
[Category("Text Properties"), Description("Value for Property A")]
public string PropertyA {get; set;}
[Category("Text Properties"), Description("Value for Property B")]
public string PropertyB { get; set; }
}
and then for your property grid:
MyCtrl tx = new MyCtrl();
pg1.SelectedObject = tx.ExtraProperties;
The down side is it changes your access level of those properties from
tx.PropertyA = "foo";
to
tx.ExtraProperties.PropertyA = "foo";
To hide MyCtrl properties, use [Browsable(False)] attribute on the property.
[Browsable(false)]
public bool AProperty { get; set;}
To hide inherited proeprties, you need to override the base and apply the browsable attribute.
[Browsable(false)]
public override string InheritedProperty { get; set;}
Note: You may need to add the virtual or new keyword depending on the circumstances.
A better approach would be to use a ControlDesigner. The designer has an override called PreFilterProperties that can be used to add extra attributes to the collection that has been extracted by the PropertyGrid.
Designer(typeof(MyControlDesigner))]
public class MyControl : TextBox
{
// ...
}
public class MyControlDesigner : ...
{
// ...
protected override void PreFilterProperties(
IDictionary properties)
{
base.PreFilterProperties (properties);
// add the names of proeprties you wish to hide
string[] propertiesToHide =
{"MyProperty", "ErrorMessage"};
foreach(string propname in propertiesToHide)
{
prop =
(PropertyDescriptor)properties[propname];
if(prop!=null)
{
AttributeCollection runtimeAttributes =
prop.Attributes;
// make a copy of the original attributes
// but make room for one extra attribute
Attribute[] attrs =
new Attribute[runtimeAttributes.Count + 1];
runtimeAttributes.CopyTo(attrs, 0);
attrs[runtimeAttributes.Count] =
new BrowsableAttribute(false);
prop =
TypeDescriptor.CreateProperty(this.GetType(),
propname, prop.PropertyType,attrs);
properties[propname] = prop;
}
}
}
}
You can add the names of proeprties you wish to hide to propertiesToHide which allows for a cleaner separation.
Credit where due: http://www.codeproject.com/KB/webforms/HidingProperties.aspx#
Related
I have 2 classes StaggingAttorney and Attorney. I will use the StaggingAttorney to collect information about an attorney and once I have all the information I will use it to create an Attorney profile using the best results. The 2 classes look like this;
private class StaggingAttorney : CourtCase.Attorney
{
public bool scraping = false;
public bool scraped = false;
public string caseNumber;
public CourtCase.Attorney toAttorney()
{
CourtCase.Attorney attorney = new CourtCase.Attorney();
return attorney;
}
}
...and...
public class Attorney
{
public string names;
public string matchString;
...
public List<Identity> IdentityMatches = new List<Identity>();
public List<Identity> getIdentityMatches()
{
return IdentityMatches;
}
public class Identity
{
public string names;
public string barNumber;
public string email;
public string phoneNumber { get; internal set; }
public object faxNumber { get; internal set; }
}
}
I have created a method called CourtCase.Attorney toAttorney() which you can see above. In this method I want to return a new CourtCase.Attorney with all CourtCase.Attorney inherited properties in the the StaggingAttorney
As #derloopkat suggested, you can simply cast your "StaggingAttorney" instance to his parent class. ("Attorney" in this case)
But if you really need a new instance of an "Attorney" with the same values than the parent "StaggingAttorney" just access to the parent fields of your "StaggingAttorney" object.
private class StaggingAttorney : CourtCase.Attorney
{
public bool scraping = false;
public bool scraped = false;
public string caseNumber;
public CourtCase.Attorney toAttorney()
{
CourtCase.Attorney attorney = new CourtCase.Attorney()
{
names = this.names,
matchString = this.matchString,
[... Initialize the other properties ...]
};
return attorney;
}
}
When you create an instance of a child class you are also creating the parent. So there are no many scenarios where you need to make another new instance from a child.ToParent() method. Having a conversion method like this makes more sense when one class is not inheriting from the other.
var attorney = new StaggingAttorney() { scraped = false };
attorney.names = "John"; //During the scraping process
attorney.scraped = true;
CourtCase.Attorney court = (CourtCase.Attorney)attorney; //casting
Console.WriteLine(court.names); //returns "John"
No need to copy data, because the child inherited names from its parent.
is there a way to make an enum value not browsable to combo box
or just, not to come back from Enum.GetValues() ??
public enum DomainTypes
{
[Browsable(true)]
Client = 1,
[Browsable(false)]
SecretClient = 2,
}
This is a generic method (based on another SO answer which I can't find) which you can call on any enum.
By the way, the Browsable attribute is already defined in System.ComponentModel.
For example:
ComboBox.DataSource = EnumList.Of<DomainTypes>();
...
public class EnumList
{
public static List<T> Of<T>()
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.Where(x =>
{
BrowsableAttribute attribute = typeof(T)
.GetField(Enum.GetName(typeof(T), x))
.GetCustomAttributes(typeof(BrowsableAttribute),false)
.FirstOrDefault() as BrowsableAttribute;
return attribute == null || attribute.Browsable == true;
}
)
.ToList();
}
}
There is nothing already in place to do this for you with the Enum.GetValues() method. If you want to use attributes, you can create your own custom attribute and use it via reflection:
public class BrowsableAttribute : Attribute
{
public bool IsBrowsable { get; protected set; }
public BrowsableAttribute(bool isBrowsable)
{
this.IsBrowsable = isBrowsable;
}
}
public enum DomainTypes
{
[Browsable(true)]
Client = 1,
[Browsable(false)]
SecretClient = 2,
}
And then you can use reflection to check for custom attributes and generate a list of Enums based on the Browsable attribute.
It really can't be done in C# - a public enumeration exposes all members. Instead, consider using a wrapper class to hide/expose the items selectively. Maybe something like this:
public sealed class EnumWrapper
{
private int _value;
private string _name;
private EnumWrapper(int value, string name)
{
_value = value;
_name = name;
}
public override string ToString()
{
return _name;
}
// Allow visibility to only the items you want to
public static EnumWrapper Client = new EnumWrapper(0, "Client");
public static EnumWrapper AnotherClient= new EnumWrapper(1, "AnotherClient");
// The internal keyword makes it only visible internally
internal static readonly EnumWrapper SecretClient= new EnumWrapper(-1, "SecretClient");
}
Hope this helps. Good luck!
How can I now in a list obtained with Type.GetProperties() if the properties are user-defined?
For example
class test
{
public string propertyOne{get;set;}
public string propertyTwo{get;set;}
}
With typeof(test).GetProperties() I get two PropertyInfo, how can I now they are user defined?
Information about the context, here is the test that should pass
[Test]
public void GetFullNameScalarPropertiesTest()
{
// Act
var properties = ReflectionHelper.GetFullNameScalarProperties(typeof(Parent));
// Assert
Assert.True(properties.Contains("PropertyOne"));
Assert.True(properties.Contains("Child.PropertyTwo"));
Assert.True(properties.Contains("Child.GrandChild.PropertyThree"));
Assert.That(properties.Count, Is.EqualTo(3));
}
class Parent
{
public Parent()
{
Child = new Child();
}
public string PropertyOne { get; set; }
public Child Child { get; set; }
}
class Child
{
public Child()
{
GrandChild = new GrandChild();
}
public string PropertyTwo { get; set; }
public GrandChild GrandChild { get; set; }
}
class GrandChild
{
public string PropertyThree { get; set; }
}
So, in a recursive method I'm getting properties and creating a list with the names
ATM the code that pass this test is
public static IList<string> GetFullNameScalarProperties(Type type)
{
var lista = new List<string>();
var path = string.Empty;
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
GetFullNameScalarProperties(propertyInfo, path, lista);
return lista;
}
private static void GetFullNameScalarProperties(PropertyInfo propertyInfo, string path, ICollection<string> lista)
{
if (!string.IsNullOrEmpty(path))
path += ".";
path += propertyInfo.Name;
if (propertyInfo.PropertyType.FullName != null)
if (propertyInfo.PropertyType.FullName.StartsWith("System"))
{
lista.Add(path);
return;
}
var properties = propertyInfo.PropertyType.GetProperties();
foreach (var pi in properties)
GetFullNameScalarProperties(pi, path, lista);
}
It's unclear what you mean by "user-defined" - are you trying to spot the difference between an automatically implemented property and one which has been written by hand? If so, an automatically implemented property will have the [CompilerGenerated] attribute on the getter and the setter.
using System;
using System.Runtime.CompilerServices;
class Program
{
public int AutomaticallyImplemented { get; set; }
public int HandWritten {
get { return 0; }
set {}
}
static void Main()
{
foreach (var property in typeof(Program).GetProperties())
{
bool auto = property.GetGetMethod().IsDefined
(typeof(CompilerGeneratedAttribute), false);
Console.WriteLine("{0}: {1}", property.Name, auto);
}
}
}
Obviously you'd normally want to check whether there is a getter first :)
If you want to get those non-inherited members, try Type.GetProperties, and pass BindingFlags.DeclaredOnly as an argument like:
var properties = typeof(test).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
Maybe you want to know which one aren't .NET Framework ones.
You can invoke Type.GetProperties and iterate found properties with LINQ in order to know where these were defined in your class, and which ones, in framework level.
As others said, you need PropertyInfo.DeclaringType to know where some property was defined.
If any of your project's object are inheriting from some base class, maybe you can do this:
someObject.GetType().GetProperties().Where(propInfo => propInfo.DeclaringType.IsSubclassOf(typeof(ProjectBaseType))
There is nothing like a 'user defined' property. To learn in what type in an inheritance hierarchy a property was declared, have a look at PropertyInfo.DeclaringType.
If you want to findout whether or not a property is inherited compare the PropertyInfo.DeclaringType to the Type you are testing
Ok, this is a tough one.
Introduction: My idea is to attach an instanciated QueryBuilder class which I wrote, to a PropertyGrid. The QueryBuilder class now contains a couple of fields, which are hardcoded like in the example below. Thus allowing a user to specify, which fields should be used in a query in what way (sorted, grouped, and so on). After the user having specified all the settings to these properties (by code or via the PropertyGrid GUI), the QueryBuilder is able to produce a query. Everything is working fine like that. Pseudo code:
class QueryBuilder {
public QBField name {get; set;}
public QBField prename {get; set;}
public QBField zip {get; set;}
// ...
public void QueryBuilder() {
name = new QBField();
prename = new QBField();
// ...
}
public getQuery() {
// logic to build the query
}
}
class QBField {
public bool shown {get; set;}
public bool sortby {get; set;}
public bool groupby {get; set;}
}
Challenge: Now instead of hardcoding each field as public properties in the QueryBuilder class, I was wondering how I could use i.e. a List<string> containing all my fields to "populate" my instanciated QueryBuilder with these properties.
So this leads to three questions:
Could this be accomplished by somehow overriding GetProperties() of the Type of the QueryBuilder class, and if yes, how is it best done?
How can I then iterate through all of these at runtime generated QBField properties and instanciate them? Idea: PropertyDescriptors and Activators?
How can I iterate through all of these properties to read the values of each QBField object? The problem I ran in was, that when reading the Properties of QBField with reflection and trying getValue(obj, null), of course the first parameter needed is an object, which I do not know since I have lots of these QBField objects. Perhaps putting all my QBFields into a List<QBField> and iterating through it? Would that work in this example?
I'm just a bit lost but I feel that I'm very close to the solution. Therefore any help or just pointers in the right direction are most greatly appreciated!
PropertyGrid can be influenced via TypeConverter, ICustomTypeDescriptor and/or TypeDescriptionProvider. Of these, TypeConverter is the simplest, by overriding GetProperties (and mark it as supported).
In any event, you will also need to write a PropertyDescriptor implementation that knows how to take a field and an object, and get/set the value, i.e.
public override void SetValue(object component, object value) {
((YourType)component)[fieldNameSetInConstructor] = value;
}
Here's a basic property bag that exposes everything as string; obviously as you extend this (different property types, change-notification, etc) it gets more complex very quickly. Note also that this TypeConverter approach only works for PropertyGrid; for DataGridView etc you'll need either ICustomTypeDescriptor or TypeDescriptionProvider. For collections you'll need ITypedList. And there are about 20 other interfaces around the edges for specific scenarios. But you get the point ;p The key thing is that our PropertyDescriptor acts as the translation between your actual model (the dictionary in my case), and the model you expose to TypeDescriptor (the fake properties per key).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var bag = new BasicPropertyBag { Properties = {
new MetaProp("Name", typeof(string)),
new MetaProp("Description", typeof(string)),
new MetaProp("DateOfBirth", typeof(DateTime)
, new CategoryAttribute("Personal"), new DisplayNameAttribute("Date Of Birth"))
} };
bag["Name"] = "foo";
bag["DateOfBirth"] = DateTime.Today;
Application.Run(new Form { Controls = { new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = bag } } });
}
}
public class MetaProp
{
public MetaProp(string name, Type type, params Attribute[] attributes)
{
this.Name = name;
this.Type = type;
if (attributes != null)
{
Attributes = new Attribute[attributes.Length];
attributes.CopyTo(Attributes, 0);
}
}
public string Name { get; private set; }
public Type Type { get; private set; }
public Attribute[] Attributes { get; private set; }
}
[TypeConverter(typeof(BasicPropertyBagConverter))]
class BasicPropertyBag
{
private readonly List<MetaProp> properties = new List<MetaProp>();
public List<MetaProp> Properties { get { return properties; } }
private readonly Dictionary<string, object> values = new Dictionary<string, object>();
public object this[string key]
{
get { object value; return values.TryGetValue(key, out value) ? value : null; }
set { if (value == null) values.Remove(key); else values[key] = value; }
}
class BasicPropertyBagConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptor[] metaProps = (from prop in ((BasicPropertyBag)value).Properties
select new PropertyBagDescriptor(prop.Name, prop.Type, prop.Attributes)).ToArray();
return new PropertyDescriptorCollection(metaProps);
}
}
class PropertyBagDescriptor : PropertyDescriptor
{
private readonly Type type;
public PropertyBagDescriptor(string name, Type type, Attribute[] attributes)
: base(name, attributes) {
this.type = type;
}
public override Type PropertyType { get { return type; } }
public override object GetValue(object component) { return ((BasicPropertyBag)component)[Name]; }
public override void SetValue(object component, object value) { ((BasicPropertyBag)component)[Name] = (string)value; }
public override bool ShouldSerializeValue(object component) { return GetValue(component) != null; }
public override bool CanResetValue(object component) { return true; }
public override void ResetValue(object component) { SetValue(component, null); }
public override bool IsReadOnly { get { return false; } }
public override Type ComponentType { get { return typeof(BasicPropertyBag); } }
}
}
I've got some aspect like this:
public class MyAttribute : OnMethodInvocationAspect
{
public int Offset { get; internal set; }
public MyAttribute(int offset)
{
this.Offset = offset;
}
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
//do some stuff
}
}
Now I'm having my class, and I add my attribute to it:
class MyClass
{
[MyAttribute(0x10)]
public int MyProp { get; set; }
}
Works all fine. Yet now I want to use reflection to get my offset; when I do
typeof(MyClass).GetProperty("MyProp").GetCustomAttributes(true);
It returns nothing. How can I access my original Offset value (the property on my attribute)?
Ah, I fixed it this way:
First add an attribute to your attribute definition like:
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData=true)]
public class MyAttribute : OnMethodInvocationAspect
And then I can call the get_ method of my property to get the data I want:
foreach (PropertyInfo pi in typeof(T).GetProperties())
{
var entityAttribute = (MyAttribute)(typeof(T).GetMethod("get_" + pi.Name).GetCustomAttributes(typeof(MyAttribute), true).FirstOrDefault());
}