Special meaning of property "Name" within a property grid - c#

I use a PropertyGrid to allow the enduser to edit properties from a class ClassA
This class has a List<ClassB> property.
For the List<ClassB> property, the PropertyGrid shows (Collection) and a button with 3 dots which opens a new windows which looks like this (taken from another SO post).
I want to customize the Members: DisplayName on the left, so for ClassB I have overridden the ToString() method
public class ClassB
{
public string Name { get; set; }
public TimeSpan Value { get; set; }
public override ToString() { return String.Format("{0} ({1})", this.Name, this.Value); }
}
Now here comes the problem:
If Name is empty, it shows (00:00:00) as expected.
If I change the name to Test I expect it to show Test (00:00:00), but it just shows Test
If I rename the property Name to something else, it works as expected.
I suppose this is a special conventions that, if a class has a property Name and the value is not null or empty, the control shows this property rather than the name.
However, I have not found a doc that verifies that and I don't know how to change this behaviour. How do I achive this?
Note: Changing the property name is not an option.

Unfortunately, the logic is pretty hardcoded in the CollectionEditor.GetDisplayText Method. It's not documented, but you can disassemble it with a tool. This is the code:
protected virtual string GetDisplayText(object value)
{
string str;
if (value == null)
return string.Empty;
// use the Name property
PropertyDescriptor defaultProperty = TypeDescriptor.GetProperties(value)["Name"];
if ((defaultProperty != null) && (defaultProperty.PropertyType == typeof(string)))
{
str = (string) defaultProperty.GetValue(value);
if ((str != null) && (str.Length > 0))
{
return str;
}
}
// or use the DefaultPropertyAttribute
defaultProperty = TypeDescriptor.GetDefaultProperty(this.CollectionType);
if ((defaultProperty != null) && (defaultProperty.PropertyType == typeof(string)))
{
str = (string) defaultProperty.GetValue(value);
if ((str != null) && (str.Length > 0))
{
return str;
}
}
// or use the TypeConverter
str = TypeDescriptor.GetConverter(value).ConvertToString(value);
if ((str != null) && (str.Length != 0))
{
return str;
}
// or use the type name
return value.GetType().Name;
}
This code is pretty nasty because it does things basically the other way around. It should use the Name property as the last resort instead of focusing on it...
But all hope is not lost since the CollectionEditor class is not sealed. This is how you can fix it:
1) declare the EditorAttribute on the class holding the collection, something like this:
public class ClassA
{
[Editor(typeof(MyCollectionEditor), typeof(UITypeEditor))]
public List<ClassB> List { get; set; }
}
2) define you custom collection editor, like this;
public class MyCollectionEditor : CollectionEditor // needs a reference to System.Design
{
public MyCollectionEditor(Type type)
: base(type)
{
}
protected override string GetDisplayText(object value)
{
// force ToString() usage, but
// you also could implement some custom logic here
return string.Format("{0}", value);
}
}

Related

Write a validation routine for a C# class on change event

I'm trying to write an "onchange" event for a C# class that I have. The idea would be to capture anytime the class was instantiated or a property was changed and fire off some code to evaluate the "health" of the object, then set a property of the class to true or false based off of the method being invoked. My initial attempts were to simply call a private method in the setter of each property as such:
string _source = null;
public string Source
{
set
{
this._source = value;
OnClassChange();
}
get { return this._source; }
}
string _dest = null;
public string Dest
{
set
{
this._dest = value;
OnClassChange();
}
get { return this._dest; }
}
bool _isValid;
public bool IsValid
{
get { return _isValid; }
}
void OnClassChange()
{
_isValid = (_source == null) ? false : true ;
_isValid = (_dest == null) ? false : true;
}
but this seems sort of clunky and not elegant... I'd like to use some sort of listener, then in my OnClassChange() block simply loop through all the private properties of the class, determine the type of property and invoke some logic to determine if the values of the property is valid or not in a loop.
You don't really need a field for this at all - unless the validation is costly (so you want to avoid recomputing it each time it's requested) you can just have:
public string Source { get; set; }
public string Destination { get; set; }
public bool IsValid { get { return Source != null && Destination != null; } }
In C# 6 the IsValid code would be be even simpler:
public bool IsValid => Source != null && Destination != null;

ErrorProvider - Input string was not in a recognized format C#

I'm currently work with Entity Framework 5.0 and WinForms. What I'd like to do is set-up validation on my POCO classes so that when I databind them to form fields I'd like to display UI validation errors via an ErrorProvider. I have set up a base "ValidationEntity" class that implements the IDataErrorInfo interface with some simple validation methods for my child classes to call. For the most part, validating the field length, range, etc.. seem to be working fine displaying the errors to the user via the ErrorProvider.
However, I seem to have ran into an issue with the "ValidateRequiredField" method. If I have a class that has a non-nullable integer field and the user happens to remove this value on the form the ErrorProvider does show a message to the end-user, but the message is "Input string was not in a recognized format". Now I assume this is because the form, being bound to an integer field, is attempting to convert the empty text into an integer and a conversion error is occurring prior to the value being sent to the POCO class property. My question is, what would the best approach to solve this be?
I'm guessing that I may have to implement the Validating methods of the TextBox controls inside the form, catch the empty/null entry, and set the appropriate error message on the error provider. However, I was hoping on a way to let the class handle the empty/null value and set the error inside the POCO class so that it propagates to the UI. Originally I had thought of creating a custom TypeConverter (e.g. RequiredIntTypeConverter) but ran into issues since I'm inheriting from the ValidationEntity class and couldn't think of a good way of adding to the errors.
Below is a sample from the ValidationEntity class as well as a snippet from the Company class.
ValidationEntity.cs
public class ValidationEntity : IDataErrorInfo
{
private readonly Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
string IDataErrorInfo.Error
{
get { return string.Empty; }
}
string IDataErrorInfo.this[string propertyName]
{
get
{
return (!errors.ContainsKey(propertyName) ? null : String.Join(Environment.NewLine, errors[propertyName]));
}
}
public void AddError(string propertyName, string error, bool isWarning)
{
if (!errors.ContainsKey(propertyName))
{
errors[propertyName] = new List<string>();
}
if (!errors[propertyName].Contains(error))
{
if (isWarning)
{
errors[propertyName].Add(error);
}
else
{
errors[propertyName].Insert(0, error);
}
}
}
public void RemoveError(string propertyName, string error)
{
if (errors.ContainsKey(propertyName) &&
errors[propertyName].Contains(error))
{
errors[propertyName].Remove(error);
if (errors[propertyName].Count == 0)
{
errors.Remove(propertyName);
}
}
}
public void ValidateFieldLength(string propertyName, string value, int maxLength)
{
string errorMessage = string.Format("Text entered exceeds max length of {0} characters", maxLength);
if (value != null)
{
if (value.Length > maxLength)
{
if (!errors.ContainsKey(propertyName))
{
AddError(propertyName, errorMessage, false);
}
}
else
{
RemoveError(propertyName, errorMessage);
}
}
else
{
RemoveError(propertyName, errorMessage);
}
}
public void ValidateRequiredField(string propertyName, string value)
{
string errorMessage = string.Format("{0} is required.", propertyName);
if (string.IsNullOrWhiteSpace(value))
{
AddError(propertyName, errorMessage, false);
}
else
{
RemoveError(propertyName, errorMessage);
}
}
}
Company.cs
public class Company : ValidationEntity
{
private int companyID;
private string companyName;
public int CompanyID
{
get { return this.companyID; }
set
{
OnCompanyIdChanging(value.ToString());
this.companyID = value;
}
}
public string CompanyName
{
get { return this.companyName; }
set
{
OnCompanyNameChanging(value);
this.companyName = value;
}
}
private void OnCompanyIdChanging(string value)
{
ValidateRequiredField("CompanyID", value);
}
private void OnCompanyNameChanging(string value)
{
ValidateRequiredField("CompanyName", value);
ValidateFieldLength("CompanyName", value, 30);
}
}
Thank you for your assistance.
After doing some more research on this and testing out code samples I was able to find a solution to this particular item. A custom TypeConverter was needed for a non-nullable integer conversion. I was able to locate information here
and ended up with the following TypeConverter for testing a "Required Integer":
public class RequiredIntConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
[System.Diagnostics.DebuggerNonUserCode]
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
throw new ApplicationException("This field requires an integer value and cannot be blank.");
}
int result = 0;
if (!int.TryParse(value.ToString(), out result))
{
throw new ApplicationException("The value could not be parsed as a valid integer data type.");
}
else
{
return result;
}
}
}
When I initially tested the code from the link above my debugger kept breaking when I attempted to throw the ApplicationException. Thinking this was an error in my code I was confused on how to use the TypeConverter. However, I found the following post which describes how to suppress the debugger from breaking on a method.
I'm still a bit unsure on why it breaks on this ApplicationException and how the ErrorProvider knows to display the underlying exception. If anyone could point me to some additional resources on this it would be greatly appreciated.
Have a great day.

implementation of bidirectional association in c# :How its possible to pass one object as a parameter only for one time in c#

I hope to phrase my question correctly (if not please help me to entitle it better)
,to make it clear,please take a look at my code.
class Factory
{
public string Name { get; set; }
private Person _manager;
public Person Manager
{
get
{
return (_manager );
}
set
{
_manager = value;
if (_manager.WorkPlace!=this)
{
_manager.WorkPlace = this;
}
}
}
public Factory(string name, Person manager)
{
Name = name;
Manager = manager;
if (Manager.WorkPlace ==null)
{
Manager.WorkPlace = this;
}
}
public Factory(string name, string managerFullName, int managerAge)
{
Name = name;
Manager = new Person(managerFullName, managerAge);
if (Manager.WorkPlace != this)
{
Manager.WorkPlace = this;
}
}
public void ShowInfo()
{...}
}
my problem appears when using first constructor of factory class
class Program
{
static void Main(string[] args)
{
Person oPerson1=new Person("Jon",30);
factory oFactory1=new Factory("f1",oPerson1);
factory oFactory2=new Factory("f2",oPerson1);
factory oFactory3=new Factory("f3",oPerson1);
factory oFactory4=new Factory("f4",oPerson1);
...
}
}
as you can see in this constructor I can use one person object(as a manger) more than one time
,in fact so many times it could be used , and there is nothing to prevent me . that
means one person could manage many factories,I dont want it.I want a person could
mange only one factory,how is that possible?
to handle this issue some workarounds came to my mind.
1- deleting that constructor and using only another one .(but I am looking for a better solution,I would like to have that constructor.)
2- throwing an exception in Run time that i hate it
as i know the c# compiler has nothing to prevent passing an object more than one time.
should I change something in design of the class?
what is your recommendation? what is the best solution ?thank u so much for any advices.
EDIT:Our business Logic
each factory has a manager ,its meaningless to have a factory without a manger.
and a person could be a manager.
person(1..1)-------------(0..1)factory
Relationships are better modeled with static code relationships than with imperative checks. That would enable the compiler to help you enforce the relationship.
Remove the manager from the factory and add the factory to the manager:
public class Manager : Person
{
public Manager(Factory factory)
}
In this way a Manager can manage only one Factory...
The answer is in your code:
set
{
_manager = value;
if (_manager.WorkPlace!=this)
{
_manager.WorkPlace = this;
}
}
Replace this with
set
{
if (value == null) // Edit: Add manager release capability to change factories
{
if(_manager != null)
_manager.WorkPlace = null;
_manager = null;
}
else if (value.WorkPlace == null)
{
_manager = value;
_manager.WorkPlace = this;
}
else
throw new ArgumentException();
}
I use the following "micropattern" for setters:
public Person Manager
{
get
{
return (_manager );
}
set
{
if (_manager != null)
{
_manager.WorkPlace = null;
}
_manager = value;
if (_manager != null)
{
_manager.WorkPlace = this;
}
}
Now, whenever you associate a manager with a factory, it's old manager is automatically de-associated with the factory.
Now, this does not prevent you reassigning a manager to a factory. But it will make sure that pointers are always in sync with each other.
I learned this trick from Martin Fowlers Refactoring. An online reference to the technique can be found here.
Despite that you hate it, throwing an exception in the constructor will notify you early that you have an error. You also need to make sure that the Person isn't already a manager.
public Factory(string name, Person manager)
{ if (Manager.WorkPlace != null && Manager.WorkPlace.Manager==manager)
{
var errmsg = "Cannot pass an existing manager to Factory constructor.";
throw new ArgumentException("manager",errmsg);
}
Name = name;
Manager = manager;
Manager.WorkPlace = this;
}
A "Cell" has an "Item"; and an "Item" has a "Cell". If you update one of them; the other should be updated as well. So In Cell we have a property like:
public Item CurrentItem
{
get { return _currentItem; }
set
{
if (_currentItem == value) return;
var oldItem = _currentItem;
_currentItem = value;
if (oldItem != null && oldItem.CurrentCell == this)
{
oldItem.CurrentCell = null;
}
if (value != null)
{
value.CurrentCell = this;
}
}
}
In the opposite site (In Item) we have following property:
public Cell CurrentCell
{
get { return _currentCell; }
set
{
if (_currentCell == value) return;
var oldCell = _currentCell;
_currentCell = value;
if (oldCell != null && oldCell.CurrentItem == this)
{
oldCell.CurrentItem = null;
}
if (value != null)
{
value.CurrentItem = this;
}
}
}

What's the order of execution in property setters when using IDataErrorInfo?

Situation: Many times with WPF, we use INotifyPropertyChanged and IDataErrorInfo to enable binding and validation on our data objects. I've got a lot of properties that look like this:
public SomeObject SomeData
{
get { return _SomeData; }
set { _SomeData = value; OnPropertyChanged("SomeData"); }
}
Of course, I have an appropriate overridden IDataErrorInfo.this[] in my class to do validation.
Question: In a binding situation, when does the validation code get executed? When is the property set? When is the setter code executed? What if the validation fails?
For example:
User enters new data.
Binding writes data to property.
Property set method is executed.
Binding checks this[] for validation.
If the data is invalid, the binding sets the property back to the old value.
Property set method is executed again.
This is important if you are adding "hooks" into the set method, like:
public string PathToFile
{
get { return _PathToFile; }
set
{
if (_PathToFile != value && // prevent unnecessary actions
OnPathToFileChanging(value)) // allow subclasses to do something or stop the setter
{
_PathToFile = value;
OnPathToFileChanged(); // allow subclasses to do something afterwards
OnPropertyChanged("PathToFile");
}
}
}
If you want fine-grained control over the timing of validation, you can have it:
private Dictionary<string, string> Errors = new Dictionary<string, string>();
private object _MyProperty;
public object MyProperty
{
get { return _MyProperty; }
set
{
Errors["MyProperty"] = null;
if (value == _MyProperty)
{
return;
}
ValidateMyProperty(value); // may set Errors["MyProperty"]
if (Errors["MyProperty"] == null)
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
}
}
}
public string this[string propertyName]
{
return Errors[propertyName];
}
No matter when data error information is requested and who's requesting it, it always returns the property's validation status as of the last time something tried to set the property.
Note that if you work at it, you can encapsulate the logic thusly:
public object MyProperty
{
set { _MyProperty = Validate("MyProperty", value, _MyProperty); }
}
private Dictionary<string, Func<object, string>> ValidationFunctions;
private object Validate(string propertyName, object value, object field)
{
Errors[propertyName] = null;
if (value == field)
{
return;
}
if (!ValidationFunctions.ContainsKey(propertyName))
{
return value;
}
Errors[propertyName] = ValidationFunctions[propertyName](value);
return (Errors[propertyName] == null)
? value
: field;
}
}

Accessing object properties from string representations

I've got a custom object (example only code for ease of understanding) ...
public class MyClass
{
private string name;
private int increment;
private Guid id;
public string Name
{
get { return name; }
set { name = value; }
}
public int Increment
{
get { return increment; }
set { increment = value; }
}
public Guid Id
{
get { return id; }
set { id = value; }
}
}
... and a custom collection of this class ...
public class MyClassCollection : Collection<MyClass>
{
}
I was looking to write a Sort routine for the collection which will have the following public method ...
public void Sort(params string[] sortProperties)
{
if (sortProperties == null)
{
throw new ArgumentNullException("sortProperties", "Parameter must not be null");
}
if ((sortProperties.Length > 0) && (Items.Count > 1))
{
foreach (string s in sortProperties)
{
// call private sort method
Sort(s);
}
}
}
... and the private Sort method would take a parameter of the property name ...
private void Sort(string propertyName)
{
}
What I want to do is be able to pass in a set of property names into the method ...
MyClassCollection current = new MyClassCollection();
// setup a objects in the collection
current = GetCollectionData();
// sort by Name, then by Increment
current.Sort("Name", "Increment");
Using the property names passed into the method I want to be able to check to see if it has a property of that name, if so work out what type it is and then run through a sort of it.
The interim workaround which I have currently got is ...
private void Sort(string propertyName)
{
// convert to List
List<MyClass> myCurrentClass = Items as List<MyClass>;
// sort
if (myCurrentClass != null)
{
switch (propertyName)
{
case "Name":
myCurrentClass.Sort(delegate(MyClass myClassOne, MyClass myClassTwo)
{
return
Comparer<string>.Default.Compare(myClassOne.Name,
myClassTwo.Name);
}
);
break;
case "Increment":
myCurrentClass.Sort(delegate(MyClass myClassOne, MyClass myClassTwo)
{
return
Comparer<int>.Default.Compare(myClassOne.Increment,
myClassTwo.Increment);
});
break;
}
}
}
... but ideally I would like to switch on the underlying type of the Property (string, int etc.) and using a distinct number of delegate calls for the types for sorting. I've looked around but I've not found anything which points me in the right direction. I've had a look at reflection but I couldn't see anything which would be able to help me.
Is this even possible? and if so, how?!
Cheers!
Reflection would be the way to go - look at Type.GetProperty(string name). Creating the right comparer might be tricky after that - you might want to write a generic method, and then invoke that with reflection based on the property type. It all gets pretty icky, I'm afraid - but it's definitely feasible.
private void Sort( string propertyName )
{
List<MyClass> myCurClass = ...
myCurClass.Sort(delegate( MyClass left, MyClass right ){
PropertyInfo lp = typeof(MyClass).GetProperty (propertyName);
Comparer.Default.Compare (pi.GetValue(left), pi.GetValue(right));
});
}
I think this should get you started. :)
(Not tested, nor compiled, but you'll get the idea)
After hitting my head against the problem for a while and hoping on a train home last night I decided that I would try and bash out an answer. Using a combination of Jon's pointers and Frederik's use of the PropertyInfo class and keeping the original idea of switching on the underlying object type, this is what I came up with ...
private void Sort_version2(string propertyName)
{
// convert to list
List<MyClass> myCurrentClass = Items as List<MyClass>;
string typeOfProperty;
PropertyInfo pi;
// sort
if ((myCurrentClass != null) && (MyClass.HasDetailAndExtract(propertyName, out typeOfProperty, out pi)))
{
switch(typeOfProperty)
{
case "System.String":
myCurrentClass.Sort(delegate(MyClass one, MyClass two)
{
return
Comparer<string>.Default.Compare(pi.GetValue(one, null).ToString(),
pi.GetValue(two, null).ToString());
});
break;
case "System.Int32":
myCurrentClass.Sort(delegate (MyClass one, MyClass two)
{
return
Comparer<int>.Default.Compare(
Convert.ToInt32(pi.GetValue(one, null)),
Convert.ToInt32(pi.GetValue(two, null)));
});
break;
default:
throw new NotImplementedException("Type of property not implemented yet");
}
}
}
I've documented the thought process and more details on my blog let me know what you think!
Thanks to Jon and Frederik for the help :-)

Categories