Hard to do "all in one", as the PropertyGrid uses the TypeConverter,
which allows for text conversions [inclusive or] list of available
options.
What you can do, however, is to split the two, and have the
TypeConverter be able to parse the entire string, else enter the value
and unit on separate lines in the grid.
See enclosed example (just add a CurrencyAmount property to your class
;-p).
Marc
using System;
using System.ComponentModel;
public enum UnitOfCurrency { GBP, USD, INR, EUR } // etc
public class CurrencyAmountConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
return sourceType == typeof(string) ||
base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value)
{
string text = value as string;
if (text == null) return base.ConvertFrom(context, culture,
value);
return CurrencyAmount.Parse(text);
}
public override bool
GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext
context, System.Collections.IDictionary propertyValues)
{
decimal amount = (decimal)propertyValues["Amount"];
UnitOfCurrency unit = (UnitOfCurrency)propertyValues["Unit"];
return new CurrencyAmount(unit, amount);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext
context)
{
return true;
}
public override PropertyDescriptorCollection
GetProperties(ITypeDescriptorContext context, object value, Attribute[]
attributes)
{
return TypeDescriptor.GetProperties(typeof(CurrencyAmount),
attributes).Sort(new string[] { "Amount", "Unit" });
}
}
[TypeConverter(typeof(CurrencyAmountConverter))]
[ImmutableObject(true)]
public struct CurrencyAmount : IEquatable<CurrencyAmount>, IComparable,
IComparable<CurrencyAmount>
{
public static CurrencyAmount operator *(CurrencyAmount lhs, decimal
rhs)
{
return new CurrencyAmount(lhs.Unit, lhs.Amount * rhs);
}
public int CompareTo(CurrencyAmount other)
{
int result = this.Unit.CompareTo(other.Unit);
if (result == 0) result = this.Amount.CompareTo(other.Amount);
return result;
}
int IComparable.CompareTo(object other)
{
return CompareTo((CurrencyAmount)other);
}
public override int GetHashCode()
{
return _amount.GetHashCode() * 7 + _unit.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals((CurrencyAmount)obj);
}
public bool Equals(CurrencyAmount amount)
{
return (amount == this);
}
public static bool operator <(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit == rhs.Unit && lhs.Amount < rhs.Amount;
}
public static bool operator >(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit == rhs.Unit && lhs.Amount > rhs.Amount;
}
public static bool operator <=(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit == rhs.Unit && lhs.Amount <= rhs.Amount;
}
public static bool operator >=(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit == rhs.Unit && lhs.Amount >= rhs.Amount;
}
public static bool operator ==(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit == rhs.Unit && lhs.Amount == rhs.Amount;
}
public static bool operator !=(CurrencyAmount lhs, CurrencyAmount
rhs)
{
return lhs.Unit != rhs.Unit || lhs.Amount != rhs.Amount;
}
private readonly decimal _amount;
private readonly UnitOfCurrency _unit;
public decimal Amount { get { return _amount; } }
public UnitOfCurrency Unit { get { return _unit; } }
public CurrencyAmount(UnitOfCurrency unit, decimal amount)
{
_amount = amount;
_unit = unit;
}
public override string ToString()
{
return _unit.ToString() + ": " + _amount;
}
public static CurrencyAmount Parse(string value)
{
if (value != null) value = value.Trim();
if (string.IsNullOrEmpty(value)) throw new
ArgumentNullException();
string[] parts = value.Split(':');
if (parts.Length != 2) throw new ArgumentException();
return new CurrencyAmount(
(UnitOfCurrency)Enum.Parse(typeof(UnitOfCurrency),
parts[0].Trim(), true),
decimal.Parse(parts[1].Trim()));
}
}