(Using VS 2010 Beta 2 - .Net 4.0 B2 Rel)
I have a class, MyTable, derived from BindingList where S is a struct. S is made up of several other structs, for example:
public class MyTable<S>:BindingList<S> where S: struct
{
...
}
public struct MyStruct
{
public MyReal r1;
public MyReal r2;
public MyReal R1 {get{...} set{...}}
public MyReal R2 {get{...} set{...}}
...
}
public struct MyReal
{
private Double d;
private void InitFromString(string) {this.d = ...;}
public MyReal(Double d) { this.d = d;}
public MyReal(string val) { this.d = default(Double); InitFromString(val);}
public override string ToString() { return this.real.ToString();}
public static explicit operator MyReal(string s) { return new MyReal(s);}
public static implicit operator String(MyReal r) { return r.ToString();}
...
}
OK, I use the MyTable as a binding source for a DataGridView. I can load the data grid easily using InitFromString on individual fields in MyStruct.
The problem comes when I try to edit a value in a cell of the DataGridView. Going to the first row, first column, I change the value of the existing number. It gives an exception blizzard, the first line of which says:
System.FormatException: Invalid cast from 'System.String' to 'MyReal'
I've looked at the casting discussions and reference books but don't see any obvious problems.
Any ideas?
I tried your method of handling CellParsing, and it worked. Although I did it a little differently, handling any types:
private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
e.Value = Activator.CreateInstance(e.DesiredType, new object[] { e.Value });
e.ParsingApplied = true;
}
The DataGridView uses a TypeConverter.
You must define a TypeConverter object for your structure and decorate it with the TypeConverterAttribute, so that the DataGridView uses it as soon as it encounters a property that is typed with your structure.
The example I give here of such a TypeConverter is a bit rudimentary, but the Degree structure I use is similar to yours, it will give you a good idea of what to do.
[TypeConverter(typeof(DegreeConverter))]
public struct Degree {
double Value;
public Degree(double v) { Value = v; }
public static implicit operator double(Degree v) => v.Value;
public static implicit operator Degree(double v) => new Degree(v);
public override string ToString() => Value.ToString();
}
class DegreeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
return destinationType == typeof(string) ? true : base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
return new Degree(Convert.ToDouble(value));
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
return value.ToString();
}
}
I (almost) fixed this problem by handling the CellParsing event, e.g.
private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
... // (checking goes here)
e.Value = new MyReal(e.Value.ToString());
e.ParsingApplied = true;
}
e.Value is being set correctly but the DataGridView still shows the old value. How does the new value get placed in the BindingList?
Do I need an explicit method call to force the new value into the BindingList and, if so, where?
My gridcells are filled with objects of type GridValueGroup and they have an ObjValue property displayed by the ToString override. When modifying the string in the grid cell, the CellParsing event is handled by:
filling the cell-value and
modifying the e.Value (which is of type string initially while the DesiredType is GridValueGroup) to become the desired type.
This way I avoid creating a new object because the cell already has that object.
Also it keeps the entered value in the data source. Not certain yet if it blocks other events (CellValueChanged has to be handled sometimes after the parsing of the value).
private void grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) {
string val = e.Value.ToString();
DataGridViewCell cell = this.dgvGroup1[e.ColumnIndex, e.RowIndex];
if (e.DesiredType == typeof(GridValueGroup))
{
((GridValueGroup)cell.Value).ObjValue = val;
e.Value = ((GridValueGroup)cell.Value);
}
e.ParsingApplied = true;
}
Related
I define a DynamicObject.
I have created a list of DynamicObjects with the same structure and link them to a WPF GridView.
I allow editing of some of the properties via the grid.
As the DynamicObjects present the property data as objects, how can I enforce Type restrictions?
if the user types alphabet into a cell that I would like as an int how can I get the DynamicObject to refuse the input?
You could use a TryParse wherever you're taking the cell input:
int result;
if(int.TryParse(cellText, out result))
{
// Is an integer
}
else
{
}
bool and other value types also have a TryParse if you're taking those values as well.
See also:
Comparing Types in this question
The DynamicDictionary example in the docs for a more verbose implementation on adding and editing properties.
In the constructor of my DynamicObject, I pass in with the properties definition, a dictionary of the types.
I then override the TrySetMember method to convert the value from the string supplied by the grid into its required type.
The issue I now have is sending a error message back to the grid if the conversion fails.
Here is My DynamicObject definition:
public sealed class FwDynamicObject : DynamicObject
{
private readonly Dictionary<string, object> _properties;
private readonly Dictionary<string, Type> _propertyTypes;
public FwDynamicObject(Dictionary<string, object> properties, Dictionary<string, Type> propertyTypes = null)
{
_properties = properties;
_propertyTypes = propertyTypes;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _properties.Keys;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (_properties.ContainsKey(binder.Name))
{
result = _properties[binder.Name];
return true;
}
else
{
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_properties.ContainsKey(binder.Name))
{
var t = GetMemberType(binder.Name);
if (t != null)
{
try
{
value = Convert.ChangeType(value, t);
}
catch(Exception e)
{
return false;
}
}
_properties[binder.Name] = value;
return true;
}
else
{
return false;
}
}
private Type GetMemberType(string name)
{
if (_propertyTypes.ContainsKey(name))
{
return _propertyTypes[name];
}
return null;
}
}
Am trying to work with a Winform Property Grid, and I am not able to format displayed value (mind too strongly tied to wpf now)
So what I want is, there is a drop down in property grid that has its own UITypeEditor, this editor shows values such as
1 - On
2 - Off
3 - Unknown
so the property on that listens to propertyGrid changes is int and for some odd reasons I cant change it to string, so like in wpf can I have a converter sort of thing that converts 1 into 1- On and 1-On to 1 ?
how can I decorate my property or property grid to be this intelligent ?
My property looks like
[LocalizedCategory("Limits", typeof(Api.Properties.Resources))]
[LocalizedDisplayName("Maximum", typeof(Api.Properties.Resources))]
[LocalizedDescription("Maximum", typeof(Api.Properties.Resources))]
[Editor(typeof(TextConversionTypeEditor), typeof(UITypeEditor))]
public int CriticalMaximum
{
get; set;
}
Can I make my property grid display more information than just an int ?
If you can use an Enum as type of your property, then it shows available values in drop-down, otherwise you can create a TypeConverter to provide values for drop-down. To do so you can use either of these options:
Use TypeConverter of an Enum for your int Property
If values are limited and known at design-time, In this case although the property is int, you can use the converter of an Enum for your property, without overriding anything:
public class MyObject
{
[TypeConverter(typeof(MyTypeConverter))]
public int MyProperty { get; set; }
}
public class MyTypeConverter : EnumConverter
{
public MyTypeConverter() : base(typeof(MyValues)) { }
}
public enum MyValues
{
On = 1,
Off,
Unknown
}
Create your own TypeConverter which supports standard values
If you can not have an enum and your standard values are generated at run-time, you can create such TypeConverter:
public class MyTypeConverter : TypeConverter
{
Dictionary<int, string> values;
public MyTypeConverter()
{
values = new Dictionary<int, string> { { 1, "1 - On" }, { 2, "2 - Off" }, { 3, "3 - Unknown" } };
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string)) return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value != null && values.ContainsValue(value.ToString()))
return values.Where(x => x.Value == value.ToString()).FirstOrDefault().Key;
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value != null && value.GetType() == typeof(int))
return values[(int)value];
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(values.Keys);
}
}
I am developing an asp.net component that requires many parameters. It will be called from classic ASP. I can of course pass in 10-20 parameters, but I'd love to be a bit tidier.
I'm fairly confident I could pass in an array, but ideally I'd like to be able to pass in an object.
Is this possible?
I decided to do a little test.
The Classic ASP:
Dim objDictionary
Set objDictionary = CreateObject("Scripting.Dictionary")
objDictionary.Add "startDate", startDate
objDictionary.Add "endDate", endDate
MyComponent.checkObj(objDictionary)
In my ASP.net component I have:
public string checkObj(object config)
{
return "StartDate is " + config.startDate;
}
Edit:
I have progressed the issue so I'm changing this:
I created an abstract class and now it's checking against that and building perfectly. At run time I am now getting and error - Microsoft VBScript runtime error: Invalid procedure call or argument: 'checkObj' .
Is it possible to pass a collection into a com assembly?
Perhaps the problem is that the com component is receiving an object of type Scripting.Dictionary, and not the abstract class I created, but such a thing doesn't exist in .net?
you could try using a .net object in your asp page like System.Collections.ArrayList or System.Collections.Hashtable instead of that dictionary...
<%# LANGUAGE="VBSCRIPT" %>
<%
dim netObj
set netObj = server.createobject("System.Collections.Hashtable")
' or:
'set netObj = server.createobject("System.Collections.ArrayList")
%>
that should make things easier in your .net component
I wanted to do something similar and so created a wrapper class for .NET DataRow. You could use a HasTable/Dictionairy/Other Custom Implementation as your backing store if you want.
I expose my "properties" using an Indexer Property on my wrapper object, so working with a property in asp-classic would look like this:
Dim lngCustomerId
lngCustomerID = CLng(objectWrapper("CustomerId"))
I expose my wrapper using a COM Registered .NET assembly. My wrapper inherits from DynamicObject and exposes the following by a COM visible interface:
[ComVisible(true)]
[Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDynamicModel
{
dynamic this[string propertyName] { get; set; }
bool TryGetMember(GetMemberBinder binder, out object result);
bool TrySetMember(SetMemberBinder binder, object value);
}
I would think that TryGetMember and TrySetMember will not be necessary for your needs.
My wrapper class implementation looks like this:
[ComVisible(true)]
[Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
[ProgId("COMLIB.DynamicModel")]
[ClassInterface(ClassInterfaceType.None)]
public sealed class DynamicModel : DynamicObject, IDynamicModel
{
#region indexer
public dynamic this[string propertyName]
{
get
{
dynamic propertyValue;
if (TryGetMember(propertyName, out propertyValue) != true)
{
propertyValue = null;
}
return propertyValue;
}
set
{
if (TrySetMember(propertyName, value) != true)
{
throw new ArgumentException("Cannot set property value");
}
}
}
#endregion indexer
#region Fields
private DataRow dataRow;
#endregion Fields
#region Properties
public dynamic GetAsDynamic { get { return this; } }
#endregion Properties
#region CTOR Methods
public DynamicModel()
: base()
{
DataTable dataTable = new DataTable();
this.dataRow = dataTable.NewRow();
}
public DynamicModel(DataRow dataRow)
: base()
{
this.dataRow = dataRow;
}
#endregion CTOR Methods
#region Dynamic Object Member Overrides
public override bool TryGetMember(GetMemberBinder binder, out object columnValue)
{
bool result = false;
columnValue = null;
result = TryGetMember(binder.Name, out columnValue);
return result;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
bool result = false;
result = TrySetMember(binder.Name, value);
return result;
}
#endregion Dynamic Object Member Overrides
#region Operations
public bool TryGetMember(string columnName, out dynamic columnValue)
{
bool result = false;
columnValue = null;
if (dataRow != null && dataRow.Table.Columns.Contains(columnName))
{
columnValue = dataRow[columnName];
result = true;
}
return result;
}
public bool TrySetMember(string columnName, dynamic columnValue)
{
bool result = false;
if (dataRow != null && dataRow.Table.Columns.Contains(columnName) == true)
{
dataRow[columnName] = columnValue;
result = true;
}
else
{
Type type = columnValue.GetType();
DataColumn dataColumn = new DataColumn(columnName, type);
result = TrySetDataColumn(dataColumn, type, columnValue);
}
return result;
}
private bool TrySetDataColumn(DataColumn dataColumn, Type type, object value)
{
bool result = false;
dataRow.Table.Columns.Add(dataColumn);
result = TrySetMember(dataColumn.ColumnName, value);
return result;
}
#endregion Operations
}
I hope this helps.
So I have the following snippet of code:
private Nullable<decimal> _excessWages;
public decimal ExcessWages
{
get
{
return _excessWages ?? CalculateExcessWages();
}
set
{
if (value != CalculateExcessWages())
_excessWages = value;
else
_excessWages = null;
}
}
So basically the behavior I'm trying to implement is if a field is left blank or is assigned a value equal the calculated one use the calculated value, otherwise store the assigned value.
I have a lot of fields that need to support overriding like this. Is this the best way to accomplish this? If not what would you suggest?
I worked on this a bit based mostly on Vlad's suggestion. Turns out you can use a single generic class to abstract this. Here is the end result:
public class Overridable<T>
{
private Func<T> _calculate;
private readonly Func<T, T, bool> _compare;
protected T _t;
public Overridable(Func<T> calculate, Func<T, T, bool> compare)
{
_calculate = calculate;
_compare = compare;
}
public T Value
{
get { return _compare(_t, default(T)) ? _calculate() : _t; }
set { _t = _compare(value, _calculate()) ? default(T) : value; }
}
}
You need to pass in a compare delegate because the type isn't known until you set it in a subclass. So a simple == isn't going to cut it. I went the easy route and used a Func delegate but this could be replaced with a normal delegate if it had to be adapted for .NET 2.0 for some reason.
You'll notice I'm using default(T) instead of null. This works because the default value for a Nullable<T> is null (or more precisely, undefined but it works out to be the same).
This doesn't prevent you from trying to declare an Overridable<T> for a non-nullable type. What you'd wind up with won't through run time errors but it isn't as useful. Trying to set a Overridable<decimal>.Value to null will get you a compiler error. While setting it to default(decimal) will cause it to revert to calculating the value.
I went this route because the properties from this the class I'm using this in needs to populate a serializable object thats eventually transmitted as xml. The schema for the xml includes numeric fields defined as a mixture of integers, decimals and strings.
You then use the Overriddable class like so:
private Overridable<decimal?> _excessWages =
new Overridable<decimal?>(CalculateExcessWages, (x,y) => x == y);
public virtual decimal? ExcessWages
{
get
{
return _excessWages.Value;
}
set
{
_excessWages.Value = value;
}
}
The only problem I ran into with this was that CalculateExcessWages is a non-static method so it can't be used in a field initializer. Since all the properties in my class are non-static I had to initialize all the backing fields in the constructor.
You can make a class wrapper for this.
class OverridableValue<T>
{
public OverridableValue<T>(Func<T> calculator)
{
_calculator = calculator;
}
private Nullable<T> _t;
private Func<T> _calculator;
public T Get()
{
return return _t ?? _calculator();
}
public void Set(T value)
{
_t = (value != _calculator()) ? value : null;
}
}
It's not so syntactically sweet, but at least saves some keystrokes.
Now you can use it like this:
class Foo
{
OverridableValue<decimal> _excessWages =
new OverridableValue<decimal>(CalculateExcessWages);
public decimal ExcessWages
{
get { return _excessWages.Get(); }
set { _excessWages.Set(value); }
}
...
}
The advantage is that the whole logic is hidden at the class.
You could do this by defining a handy set/get helper method
private static T GetUtil<T>(ref Nullable<T> value, Func<T> calc) {
return value ?? calc();
}
private static void SetUtil<T>(ref Nullable<T> value, T newValue, Func<T> calc) {
if ( newValue != calc() ) {
value = newValue
} else {
value = null;
}
}
private Nullable<decimal> _excessWages;
public decimal ExcessWages
{
get { return GetUtil(ref _excessWages, CalculateExcessWages); }
set { SetUtil(ref _excessWages, value CalculateExcessWages); }
}
That looks reasonable to my eyes. The only change I might make is to cache CalculateExcessWages(), if it is expensive to do, and ok to cache:
private Nullable<decimal> _excessWages;
private Nullable<decimal> _excessWagesCalculated;
public virtual decimal ExcessWages
{
get
{
if (_excessWagesCalculated == null)
_excessWagesCalculated = CalculateExcessWages();
return _excessWages ?? _excessWagesCalculated;
}
set
{
if (_excessWagesCalculated == null)
_excessWagesCalculated = CalculateExcessWages();
if (value != _excessWagesCalculated)
_excessWages = value;
else
_excessWages = null;
}
}
But, this is more code than yours, and I think you are looking to simplify.
As I understood , The property grid is given an object which it can manipulate by extracting its Properties using reflections.
My problem is that I have a set of Parameters that is determined during run-time , thus I can't staticly compose a class with properties to represent this set.
I have two idea in mind to solve this problem but both are complex and will probably consume lot of time , infact i will say they are not practical under my time constraints. One is to use Reflection Emit in order to define a class dynamically and the other is to dynamiclly build a C# source file and then compile it using CodeDom.
Can Property grid behave in a different manner( other then extracting the Properties of an object using reflections ) that can suite my problem?
If no do you know any other control that can do the job for me?
I want to say that the reason I went to the property grid from the begining was its ability to provide realy nice Data Retrieval UI for common types.For color you autometically get a palette , For dataTime you automatically have a nice calender. I would like to get those things automatically , If possible.
Yes, PropertyGrid can display things other than just the compile-time properties, by using any of TypeConverter, ICustomTypeDescriptor or TypeDescriptionProvider to provide runtime pseudo-properties. Can you give an example of what your parameters look like? I should be able to provide an example...
here's a basic example (everything is string, etc) based on an earlier reply (related but different):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
class PropertyBagPropertyDescriptor : PropertyDescriptor {
public PropertyBagPropertyDescriptor(string name) : base(name, null) { }
public override object GetValue(object component) {
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value) {
((PropertyBag)component)[Name] = (string)value;
}
public override void ResetValue(object component) {
((PropertyBag)component)[Name] = null;
}
public override bool CanResetValue(object component) {
return true;
}
public override bool ShouldSerializeValue(object component)
{ // *** this controls whether it appears bold or not; you could compare
// *** to a default value, or the last saved value...
return ((PropertyBag)component)[Name] != null;
}
public override Type PropertyType {
get { return typeof(string); }
}
public override bool IsReadOnly {
get { return false; }
}
public override Type ComponentType {
get { return typeof(PropertyBag); }
}
}
[TypeConverter(typeof(PropertyBagConverter))]
class PropertyBag {
public string[] GetKeys() {
string[] keys = new string[values.Keys.Count];
values.Keys.CopyTo(keys, 0);
Array.Sort(keys);
return keys;
}
private readonly Dictionary<string, string> values
= new Dictionary<string, string>();
public string this[string key] {
get {
string value;
values.TryGetValue(key, out value);
return value;
}
set {
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}
// has the job of (among other things) providing properties to the PropertyGrid
class PropertyBagConverter : TypeConverter {
public override bool GetPropertiesSupported(ITypeDescriptorContext context) {
return true; // are we providing custom properties from here?
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, System.Attribute[] attributes) {
// get the pseudo-properties
PropertyBag bag = (PropertyBag)value;
string[] keys = bag.GetKeys();
PropertyDescriptor[] props = Array.ConvertAll(
keys, key => new PropertyBagPropertyDescriptor(key));
return new PropertyDescriptorCollection(props, true);
}
}
static class Program {
[STAThread]
static void Main() { // demo form app
PropertyBag bag = new PropertyBag();
bag["abc"] = "def";
bag["ghi"] = "jkl";
bag["mno"] = "pqr";
Application.EnableVisualStyles();
Application.Run(
new Form {
Controls = { new PropertyGrid {
Dock = DockStyle.Fill,
SelectedObject = bag
}}
});
}
}