I have a class
class PartitionTemplate
{
public PartitionTemplate()
{
keepLabels = new List<string>();
partitions = new List<partition>();
}
[JsonProperty("keepLabels")]
public List<String> keepLabels { get; set; }
[JsonProperty("slot")]
public int slot { get; set; }
....
}
my goal is to edit it with propertyGrid , using the following code:
PartitionTemplate partiTemplate;
//fi is FileInfo with the class as json using
//Newtonsoft.Json.JsonConvert.DeserializeObject<PartitionTemplate>(File.ReadAllText(partitionfile.FullName));
PartitionTemplate.ReadOrCreatePartitonConfigurationFile(out partiTemplate, fi);
propertyGrid1.SelectedObject = partiTemplate;
my problem is :
when I try to add element to keepLabels i get the following error :
Exception thrown: 'System.MissingMethodException' in mscorlib.dll
Additional information: Constructor on type 'System.String' not found.
how can it be fixed?
This happens because when you click the 'Add' button in the Collection Editor (a standard editor for the property grid) it creates a new item using a supposed public parameterless constructor, which doesn't exist on System.String (you can't do var s = new String();).
What you can do though, if you want to keep the keepLabels property as is, is to create a custom editor, like this:
// decorate the property with this custom attribute
[Editor(typeof(StringListEditor), typeof(UITypeEditor))]
public List<String> keepLabels { get; set; }
....
// this is the code of a custom editor class
// note CollectionEditor needs a reference to System.Design.dll
public class StringListEditor : CollectionEditor
{
public StringListEditor(Type type)
: base(type)
{
}
// you can override the create instance and return whatever you like
protected override object CreateInstance(Type itemType)
{
if (itemType == typeof(string))
return string.Empty; // or anything else
return base.CreateInstance(itemType);
}
}
Related
I have a custom generic form
public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>
where CONTACTS_BASE is an EntityFramework entity.
on the parent class I would like to have a property so that when I click on it at designer time from the property grid it opens a form and on that form I would like to have a combobox populated with the fields on the CONTACTS_BASE entity.
I have found this post
Marc Gravell's answer helped me to open a new form when clicked on the property at design time and I have also populated the ComboBox with fields of CONTACTS_BASE. but to do this on the form load event I called to function I made to that returns the list of fields and set it to ComboBox's DataSource.
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<CONTACTS_BASE>();
however what I would like to accomplish is making this generic
so what I would like to do is something like this to populate the ComboBox.
public partial class BdEditorForm <TParentEntity>:Form where TParentEntity:class
{
private void BdEditorForm_Load(object sender, EventArgs e)
{
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<TParentEntity>();
}
}
is something like this possible? because When I try to do this
I need to make make TypeEditor generic too and then when giving attributes to the property I create
[Editor(typeof(BdFormTypeEditor<TParentEntity>), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
and it says:
any help is appreciated thanks and sorry for my bad english
Short Answer
To know how to solve the problem, you need to know EditValue method has a context parameter which is of type ITypeDescriptorContext and has an Instance property which is the owner object of the property that you are editing. Having the owner (the Form) we know the type of the form and therefore we know the generic parameter type and therefore we can create our generic editor form.
Step By Step Example
Above fact is the key point for the answer, but to solve the problem, you need to apply some other tricks as well. For example you should get a generic type and create an instance of it using reflection.
Here I put a project containing the whole source code of the example:
r-aghaei/GenericModalUITypeEditorSample
Here are steps of the example which creates a custom model UI Type Editor to show a list of properties of T when you are editing a specific property of a form which is derived from MyBaseForm<T>.
Generic Base Form
It's the base form for other forms which contains SomeProperty, the property which you want to edit using a custom editor.
Add a MyGenericType property to the class which returns typeof(T), the generic type of the form:
public partial class MyBaseForm<T> : Form
{
public MyBaseForm()
{
InitializeComponent();
}
[Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string SomeProperty { get; set; }
[Browsable(false)]
public Type MyGenericType { get { return typeof(T); } }
}
Derived Form
It's a sample derived form which is derived from the MyBaseForm<T>. We will edit SomeProperty of an instance of this class.
public partial class MyDerivedForm : MyBaseForm<MySampleModel>
{
public MyDerivedForm()
{
InitializeComponent();
}
}
Sample Model
It's a sample model which we are going to show its properties in the custom editor window.
public class MySampleModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
Editor Form
It's the form which UITypeEditor will show. In the form, we fill comoBox1 with field names of the generic argument.
public partial class MyEditorForm<T> : Form
{
public MyEditorForm()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
var list = ListBindingHelper.GetListItemProperties(typeof(T))
.Cast<PropertyDescriptor>()
.Select(x => new { Text = x.Name, Value = x }).ToList();
this.comboBox1.DataSource = list;
this.comboBox1.DisplayMember = "Text";
this.comboBox1.ValueMember = "Value";
}
public string SelectedProperty
{
get
{
return comboBox1.GetItemText(comboBox1.SelectedItem);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
}
}
UI Type Editor
When calling EditValue method of the UITypeEditor, the context parameter is of type System.Windows.Forms.PropertyGridInternal.PropertyDescriptorGridEntry which has a Component property which its value is the instance of the form which you are editing, so we know the type of the form and therefore we know the generic parameter type and therefore we can create our generic editor form and use it.
public class MyUITypeEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
var svc = provider.GetService(typeof(IWindowsFormsEditorService))
as IWindowsFormsEditorService;
var myGenericTypeProperty = context.Instance.GetType()
.GetProperty("MyGenericType");
var genericArgument = (Type)myGenericTypeProperty.GetValue(context.Instance);
var editorFormType = typeof(MyEditorForm<>);
var genericArguments = new[] { genericArgument };
var editorFormInstance = editorFormType.MakeGenericType(genericArguments);
if (svc != null)
{
using (var f = (Form)Activator.CreateInstance(editorFormInstance))
if (svc.ShowDialog(f) == DialogResult.OK)
return ((dynamic)f).SelectedProperty;
}
else
{
using (var f = (Form)Activator.CreateInstance(editorFormInstance))
if (f.ShowDialog() == DialogResult.OK)
return ((dynamic)f).SelectedProperty;
}
return base.EditValue(context, provider, value);
}
}
I have a created an object that will hold errors encountered as well as a boolean field to indicate if any errors were present at all. The class looks like this.
public class ValidationResult
{
public bool IsValid{ get; set; }
public List<string> Errors { get; set; }
}
I go to use an instance of this class in a validation method as such
public class ValidationService
{
// This instance will hold the errors if there are any
ValidationResult myValidationResult = new ValidationResult();
public void ValidationMethod()
{
// Validation takes place here
...
// Some errors occurred to lets add then to the instance of the ValidationResult object
myValidationResult.IsValid = false;
myValidationResult.Errors.Add("An error occurred here are the details");
}
}
The problem is that the Errors collection in the instance myValidationResult is null? Why is this? I created an instance of the class and the boolean property IsValid is available, yet the Errors collection is null.
You must initialize your Errors property:
public class ValidationResult
{
public ValidationResult()
{
Errors = new List<string>();
}
public bool IsValid{ get { return (Errors.Count() == 0); } }
public List<string> Errors { get; set; }
}
Only value types are initialized per default. Your List<string> is a reference type and it does have it default value - null.
Look here for a little bit more information:
https://msdn.microsoft.com/en-us/library/aa691171(v=vs.71).aspx
I am working an ASP.net MVC4 website and have model & view model layer. Because of certain reasons I have different names for few properties in Model and ViewModel
Model
public partial class Project
{
public string Desc {get; set;}
}
View Model
public class ProjectViewModel
{
public string Description { get; set; }
}
Now at model layer, I need to use ViewModel name of a property if it is different. I was thinking of creating a custom attribute so that I can have something like this in models:
public partial class Project
{
[ViewModelPropertyName("Description")]
public string Desc {get;set;}
}
and use it at model layer as
string.Format("ViewModel Property Name is {0}", this.Desc.ViewModelPropertyName())
I want this to generic so that if there is no ViewModelPropertyName attribute on a property then it should return the same property name i.e. if Desc property has no attribute then it should return "Desc" only.
Here is what I tried
public class ViewModelPropertyNameAttribute : System.Attribute
{
#region Fields
string viewModelPropertyName;
#endregion
#region Properties
public string GetViewModelPropertyName()
{
return viewModelPropertyName;
}
#endregion
#region Constructor
public ViewModelPropertyNameAttribute(string propertyName)
{
this.viewModelPropertyName = propertyName;
}
#endregion
}
Need help for how to access custom attribute
Current state
public static class ModelExtensionMethods
{
public static string ViewModelPropertyName(this Object obj)
{
// ERROR: Cannot convert from 'object' to 'System.Reflect.Assembly'
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(obj);
foreach (System.Attribute attr in attrs)
{
if (attr is ViewModelPropertyNameAttribute)
{
return ((ViewModelPropertyNameAttribute)attr).GetViewModelPropertyName();
}
}
return string.Empty;
}
}
But this has compile time error:
Unfortunately you can not get the attributes you used to decorate the properties by reflecting on the type of the property itself. I have therefore modified your ViewModelPropertyName(this object) Extension method slightly to take in the name of your desired property.
This method will now take in the name of the property whose attribute you wish to get. If the attribute exists it will return the value passed to its constructor, if it on the other hand, does not exist it will simply return the name of the property you passed in.
public static class ModelExtensionMethods
{
public static string ViewModelPropertyName(this object obj, string name)
{
var attributes = obj.GetType()
.GetCustomAttributes(true)
.OfType<MetadataTypeAttribute>()
.First()
.MetadataClassType
.GetProperty(name)
.GetCustomAttributes(true);
if (attributes.OfType<ViewModelPropertyNameAttribute>().Any())
{
return attributes.OfType<ViewModelPropertyNameAttribute>()
.First()
.GetViewModelPropertyName();
}
else
{
return name;
}
}
}
You can also define the following classes to test this new approach.
[MetadataType(typeof(TestClassMeta))]
class TestClass { }
class TestClassMeta
{
[ViewModelPropertyName("TheName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
Also, as you can see from the following lines of code, your ViewModelPropertyName(this object, string) Extension method will now be called on the instance of your TestClass, instead of calling it on the property itself.
class Program
{
static void Main()
{
Console.WriteLine(new TestClass().ViewModelPropertyName("FirstName"));
Console.WriteLine(new TestClass().ViewModelPropertyName("LastName"));
Console.Read();
}
}
In Winform application I have a class with 2 properties and I want the user to be able to choose the type of those properties.
This is what I made so far:
Class with the properties:
static public class DataGridColumnData
{
public static object SearchColumn { get; set; }
public static object ResultColumn { get; set; }
}
And the user can choose the type of the properties using a Combobox with DropDownList Style which has values like
System.String
System.Double
System.Int32
System.Boolean
System.DateTime
Is there a way to make those properties to be types the ones that user chooses?
You can make your class generic:
static public class DataGridColumnData<T>
{
public static T SearchColumn { get; set; }
public static T ResultColumn { get; set; }
}
Then, in your code, you can create a class of the desired type:
object myDataGridColumnData;
if (userSelection == "String") {
myDataGridColumnData = new DataGridColumnData<string>();
} else if (userSelection == "Double") {
myDataGridColumnData = new DataGridColumnData<double>();
} ...
Note that, technically, DataGridColumnData<string> is a completely different type than DataGridColumnData<int>, so object is the only common supertype. Thus, to be able to access the values of myDataGridColumnData in code, you might need to use a dynamic variable or (prefered) use some common interface or base class that returns the values typed as objects.
There are ways to make the properties strongly typed in runtime using generics, but I am not sure how useful it is. Here is a solution either way:
Create an interface that is not strongly typed to facilitate interaction with the object:
public interface IDataGridColumnData
{
object SearchColumnAsObject { get; set; }
object ResultColumnAsObject { get; set; }
}
Create generic class that allows for the creation of strongly typed versions at runtime (and in code as well, of course), and that implements the interface:
public class DataGridColumnData<TSearch, TResult> : IDataGridColumnData
{
public TSearch SearchColumn { get; set; }
public static TResult ResultColumn { get; set; }
public object SearchColumnAsObject
{
get { return SearchColumn; }
set { SearchColumn = (TSearch)value; }
}
public object ResultColumnAsObject
{
get { return ResultColumn; }
set { ResultColumn = (TResult)value; }
}
}
Create a factory method that will manufacture strongly typed versions of the class, returning it as the object-typed interface:
private static IDataGridColumnData GetDataGridColumnData(
Type searchType, Type resultType)
{
var typedColumnDataType = typeof(DataGridColumnData<,>)
.MakeGenericType(new[] { searchType, resultType });
return (IDataGridColumnData)Activator.CreateInstance(typedColumnDataType);
}
...and put it to use:
IDataGridColumnData instance = GetDataGridColumnData(
Type.GetType("System.Int32"),
Type.GetType("System.String"));
// use the properties
instance.SearchColumnAsObject = 42; // works well
instance.SearchColumnAsObject = "42"; // throws exception
No, ther is not. A class is statically compiled. No wy to change the property for a static class at runtime.
You can create a subclass nd override it, via bytecode emission, though.
You can use the is keyword
if (x.SearchColumn is Double)
{
}
See also MSDN: Is (C# Reference)
Is it possible to create a class in .NET 4 with:
an indexer,
a property named "Item"?
For example, this C# class will not compile for me:
public class MyClass
{
public object Item { get; set; }
public object this[string index] { get { return null; } set { } }
}
The compiler gives an error CS0102:
The type 'MyClass' already contains a definition for 'Item'
although I am only explicitly defining Item once.
Based on this site, it is possible to use an attribute to rename the Indexer
public class MyClass
{
public object Item { get; set; }
[System.Runtime.CompilerServices.IndexerName("MyItem")]
public object this[string index] { get { return null; } set { } }
}
C# internally creates a property called Item for languages that don't support the indexer. You can control this name using the IndexerNameAttribute, like this:
[IndexerName("MyIndexer")]
public object this[string index]
{
get { return blah; }
}
If I remember correctly, such an indexer can be accessed from VB.Net through an "Item()" method. That would be where that "defined twice" comes from.