Description: When binding to a null value, the RadSpinEditor throws an exception. This missing functionality is also noticed with the standard Windows Numeric Up Down control.
How to reproduce:
- add a RadSpinEditor and two RadButtons;
- use the following code:
public partial class Form1 : Form
{
private BindingSource _bindActiveObject = new BindingSource { DataSource = typeof(MyBindingObject) };
public Form1()
{
InitializeComponent();
this.radSpinEditor1.DataBindings.Add(new Binding("Value", _bindActiveObject, "MyValue"));
}
private void radButton1_Click(object sender, EventArgs e)
{
_bindActiveObject.DataSource = new MyBindingObject { MyValue = Convert.ToDouble(100) };
}
private void radButton2_Click(object sender, EventArgs e)
{
_bindActiveObject.DataSource = new MyBindingObject { MyValue = null };
}
}
public class MyBindingObject
{
private decimal? _myValue;
public decimal? MyValue
{
get
{
return _myValue;
}
set
{
_myValue = value;
}
}
}
Workaround:
Use the following custom RadSpinEditor:
private BindingSource _bindActiveObject = new BindingSource { DataSource = typeof(MyBindingObject) };
public Form1()
{
InitializeComponent();
this.radSpinEditor1.NullableValueChanged += Form1_NullableValueChanged;
this.radSpinEditor1.DecimalPlaces = 2;
this.radSpinEditor1.DataBindings.Add(new Binding("NullableValue", _bindActiveObject,
"MyValue", true, DataSourceUpdateMode.OnPropertyChanged));
}
private void Form1_NullableValueChanged(object sender, EventArgs e)
{
Console.WriteLine("NullableValue = " + this.radSpinEditor1.NullableValue + "");
}
private void radButton1_Click(object sender, EventArgs e)
{
MyBindingObject obj = new MyBindingObject { MyValue = 65.45m };
_bindActiveObject.DataSource = obj;
}
private void radButton2_Click(object sender, EventArgs e)
{
_bindActiveObject.DataSource = new MyBindingObject { MyValue = null };
}
public class MyBindingObject
{
private decimal? _myValue;
public decimal? MyValue
{
get
{
return _myValue;
}
set
{
_myValue = value;
}
}
}
public class MySpinEditor : RadSpinEditor
{
public event EventHandler NullableValueChanged;
public decimal? NullableValue
{
get
{
return (this.SpinElement as MySpinEditorElement).NullableValue;
}
set
{
(this.SpinElement as MySpinEditorElement).NullableValue = value;
}
}
public MySpinEditor()
{
this.AutoSize = true;
this.TabStop = false;
base.SetStyle(ControlStyles.Selectable, true);
}
protected override void CreateChildItems(RadElement parent)
{
Type baseType = typeof(RadSpinEditor);
MySpinEditorElement element = new MySpinEditorElement();
element.RightToLeft = this.RightToLeft == System.Windows.Forms.RightToLeft.Yes;
this.RootElement.Children.Add(element);
element.ValueChanging += spinElement_ValueChanging;
element.ValueChanged += spinElement_ValueChanged;
element.TextChanging += spinElement_TextChanging;
element.NullableValueChanged += element_NullableValueChanged;
element.KeyDown += OnSpinElementKeyDown;
element.KeyPress += OnSpinElementKeyPress;
element.KeyUp += OnSpinElementKeyUp;
baseType.GetField("spinElement", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, element);
}
void element_NullableValueChanged(object sender, EventArgs e)
{
if (this.NullableValueChanged != null)
{
this.NullableValueChanged(this, EventArgs.Empty);
}
}
private Dictionary<string, MethodInfo> cache = new Dictionary<string, MethodInfo>();
private void InvokeBaseMethod(string name, params object[] parameters)
{
if (!cache.ContainsKey(name))
{
cache[name] = typeof(RadSpinEditor).GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic);
}
cache[name].Invoke(this, parameters);
}
private void OnSpinElementKeyUp(object sender, KeyEventArgs e)
{
this.InvokeBaseMethod("OnSpinElementKeyUp", sender, e);
}
private void OnSpinElementKeyPress(object sender, KeyPressEventArgs e)
{
this.InvokeBaseMethod("OnSpinElementKeyPress", sender, e);
}
private void OnSpinElementKeyDown(object sender, KeyEventArgs e)
{
this.InvokeBaseMethod("OnSpinElementKeyDown", sender, e);
}
private void spinElement_TextChanging(object sender, TextChangingEventArgs e)
{
this.InvokeBaseMethod("spinElement_TextChanging", sender, e);
}
private void spinElement_ValueChanged(object sender, EventArgs e)
{
this.InvokeBaseMethod("spinElement_ValueChanged", sender, e);
this.NullableValue = this.Value;
}
private void spinElement_ValueChanging(object sender, ValueChangingEventArgs e)
{
this.InvokeBaseMethod("spinElement_ValueChanging", sender, e);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab)
{
(this.SpinElement as MySpinEditorElement).CommitText();
}
else if (keyData== Keys.Delete)
{
(this.SpinElement as MySpinEditorElement).NullableValue = null;
}
return base.ProcessCmdKey(ref msg, keyData);
}
protected override Size DefaultSize
{
get
{
return GetDpiScaledSize(new Size(100, 20));
}
}
}
public class MySpinEditorElement : RadSpinElement
{
private bool validating;
private decimal? nullableValue;
private RadButtonElement nullButton;
public decimal? NullableValue
{
get
{
return this.nullableValue;
}
set
{
this.nullableValue = value;
if (value.HasValue)
{
this.internalValue = value.Value;
}
else
{
this.internalValue = 0m;
}
this.Validate();
this.OnNullableValueChanged();
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
this.CommitText();
e.Handled = true;
return;
}
base.OnKeyDown(e);
}
void nullButton_Click(object sender, EventArgs e)
{
this.NullableValue = null;
}
public virtual void CommitText()
{
this.NullableValue = this.GetValueFromText();
}
protected override decimal GetValueFromText()
{
if (this.TextBoxItem.Text == "")
{
return 0m;
}
return base.GetValueFromText();
}
public override bool Validate()
{
if (!this.NullableValue.HasValue)
{
this.TextBoxItem.Text = "";
return true;
}
this.TextBoxItem.Text = this.GetTextFromNumber(this.NullableValue.HasValue ? this.internalValue : 0m, this.Hexadecimal,
this.ThousandsSeparator, this.DecimalPlaces);
return true;
}
private string GetTextFromNumber(decimal num, bool hex, bool thousands, int decimalPlaces)
{
if (hex)
{
return string.Format("{0:X}", (long)num);
}
return num.ToString((thousands ? "N" : "F") + decimalPlaces.ToString(CultureInfo.CurrentCulture), CultureInfo.CurrentCulture);
}
public override void PerformStep(decimal step)
{
decimal value = this.GetValueFromText();
try
{
decimal incValue = value + step;
value = incValue;
}
catch (OverflowException)
{
}
this.NullableValue = this.Constrain(value);
this.Validate();
}
protected override Type ThemeEffectiveType
{
get
{
return typeof(RadSpinElement);
}
}
public event EventHandler NullableValueChanged;
protected virtual void OnNullableValueChanged()
{
if (this.NullableValueChanged != null)
{
this.NullableValueChanged(this, EventArgs.Empty);
}
}
}