I have a CustomTextBox which inherits from TextBox and overwrites the OnValidating method to allow empty strings. CustomTextBox is bound to Property Price in Domain.
public class CustomTextBox
{
protected override void OnValidating(...)
{
if(Text=="")
{
Text = null;
return;
}
base.OnValidating(e);
}
}
public class Domain
{
public System.Nullable<decimale> Price
{ ... }
}
All works well except that this prevents users froming setting Price to null. Text=null; did not propogate to the domain object. Is there a way to reset Price back to null when user clears out the TextBox?
If you are using Binding to propagate values to the domain object, then you should put this logic in the Parse event instead.
// Add binding
var b = new Binding("Text", myDataSource, "BoundProperty");
b.Parse += OnNullableTextBindingParsed;
myTextBox.DataBindings.Add(b);
// Sample parse handler
private void OnNullableTextBindingParsed(object sender, ConverterEventArgs e)
{
if (e.Value == String.Empty) e.Value = null;
}
Related
I am attempting to perform a DataBinding on a NumericUpDown WinForm control. Performing the binding works as designed, but I am having an issue with the value not being pushed to the binded property until the element goes out of focus. Is there something I am missing to get the property to update when the value changes in the control without requiring the focus to be lost?
If this is working as designed, is there a way to force the property update without losing focus?
Logic:
using System;
using System.Windows.Forms;
public partial class Form1 : Form
{
private NumericUpDown numericUpDown1 = new NumericUpDown();
private ExampleData _ed = new ExampleData();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Define the UI Control
numericUpDown1.DecimalPlaces = 7;
numericUpDown1.Location = new System.Drawing.Point(31, 33);
numericUpDown1.Name = "numericUpDown1";
numericUpDown1.Size = new System.Drawing.Size(120, 20);
numericUpDown1.TabIndex = 0;
// Add the UI Control
Controls.Add(numericUpDown1);
// Bind the property to the UI Control
numericUpDown1.DataBindings.Add("Value", _ed, nameof(_ed.SampleDecimal));
numericUpDown1.ValueChanged += NumericUpDown1_ValueChanged;
}
private void NumericUpDown1_ValueChanged(object sender, EventArgs e)
{
// This will fire as you change the control without losing focus.
System.Diagnostics.Debugger.Break();
}
}
public class ExampleData
{
public decimal SampleDecimal
{
get { return _sampleDecimal; }
set
{
// This set isn't called until after you lose focus of the control.
System.Diagnostics.Debugger.Break();
_sampleDecimal = value;
}
}
private decimal _sampleDecimal = 1.0m;
}
Change your binding to this:
numericUpDown1.DataBindings.Add(nameof(NumericUpDown.Value), _ed, nameof(ExampleData.SampleDecimal), false, DataSourceUpdateMode.OnPropertyChanged);
This will ensure that the binding fires when the value changes rather than when you move focus away from the control.
If you then want to be able to update the SampleDecimal from code and have it update on your numericupdown you'd need to implement the INotifyPropertyChanged interface on your SampleData class, like this:
public class ExampleData : INotifyPropertyChanged
{
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
public decimal SampleDecimal
{
get { return _sampleDecimal; }
set
{
_sampleDecimal = value;
OnPropertyChanged();
}
}
private decimal _sampleDecimal = 1.0m;
}
I have checkbox in my view which has bound to the property in the viewmodel. When I check/uncheck the checkbox, there is one condition in setter of the property which updates the same property if the condition is true. But when the property gets updated corresponding view does not change.
Here is the code:
View:
<CheckBox IsChecked="{Binding HoldingPen,Mode="Twoway" ,UpdateSourceTrigger=PropertyChanged}"/>
ViewModel:
public bool HoldingPen
{
get{m_holdingPen;}
set
{
m_hodingPen=value;
onPropertyChanged("HoldingPen");
OnHoldingPenCheckChanged();
}
public void OnHoldingPenCheckChanged()
{
if(HoldingPen && some other condition)
{
HoldingPen=false; //Here view should be updated simultaneously
}
}
I think it's a result of having two onPropertyChanged events fire, once with a value of true and one with a value of false
Typically for this kind of logic I prefer to use the PropertyChanged event instead of hiding the logic in property setters.
public class MyClass()
{
public MyClass()
{
// attach property changed in constructor
this.PropertyChanged += MyClass_PropertyChanged;
}
private void MyClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "HoldingPen")
OnHoldingPenCheckChanged();
}
public bool HoldingPen
{
get{ m_holdingPen; }
set
{
if (m_hodingPen == value)
return;
m_hodingPen=value;
onPropertyChanged("HoldingPen");
}
}
public void OnHoldingPenCheckChanged()
{
if(HoldingPen && some other condition)
{
HoldingPen=false; //Here view should be updated simultaneously
}
}
}
This has the additional benefit of having any custom code to modify a value in one location, rather than going through each setter when looking for something.
I am having trouble understanding why my databindings do not seem to work with my custom class. I made (hacked) my class extend the Control class to add the databindings functionality but it doesn't actually bind to my custom property.
My code for my custom class is:
public class RadioButtonSet : System.Windows.Forms.Control
{
private Dictionary<System.Windows.Forms.RadioButton, int> buttonList;
private int selectedValue;
public RadioButtonSet()
{
buttonList = new Dictionary<System.Windows.Forms.RadioButton, int>();
}
public void AddButton(System.Windows.Forms.RadioButton button, int buttonValue)
{
if (this.buttonList.ContainsKey(button))
throw new Exception("Button set already contains specified button");
else if (buttonValue <= 0)
throw new Exception("Cannot add specified key to button set");
else if (button == null)
throw new Exception("Parameter button cannot be null");
else
{
button.CheckedChanged += button_CheckedChanged;
this.buttonList.Add(button, buttonValue);
}
}
private void setSelectedButton()
{
this.buttonList.FirstOrDefault(x => x.Value == this.selectedValue).Key.Checked = true;
}
private void button_CheckedChanged(object sender, EventArgs e)
{
System.Windows.Forms.RadioButton btn = sender as System.Windows.Forms.RadioButton;
this.selectedValue = this.buttonList[btn];
}
public int SelectedButton
{
get
{
return selectedValue;
}
set
{
selectedValue = value;
setSelectedButton();
}
}
}
And I try to bind to this class using the following, where rbs_admin is an instance of my custom class:
rbs_admin.DataBindings.Add("SelectedButton", datatable, "admin");
I do not know what information may help so here goes.
I get the information to bind from a datatable which is populated by a data adapter. This custom class is not in it's own file, its part of another static class in my project.
I just dont understand as I created a custom textbox with the same custom property and it binds and works fine.
Any help is much appreciated.
Im talking about something like this:
someListControl.DataSource = datatable;
someListControl.DisplayMember = "someAnotherColumnName"
rbs_admin.DataBindings.Add("SelectedButton", datatable, "admin");
Then, selecting an item from list control will cause your control to update its binding according to the selected item.
I have a bindingSource in winforms as well as a controller class.
I want to be able to set the selected record from within the controller class using 2 way binding.
That is If the form is displaying and I set the SelectedPerson in the controller then the bindingSOurce should make that person the current record.
My controller code is
public class PeopleController : BaseController
{
private SortableBindingList<Person> _blvPersons;
public SortableBindingList<Person> BlvPersons
{
get
{
return this._blvPersons;
}
set
{
this._blvPersons = value;
this.SendChange("BlvPersons");
}
}
private Person _selectedPerson;
public Person SelectedPerson
{
get
{
return this._selectedPerson;
}
set
{
this._selectedPerson = value;
this.SendChange("SelectedPerson");
this.SendChange("BlvPersons");
this.Trace("## SelectedPerson = {0}", value);
}
}
public void InitBindingList
{
using (var repo = new PeopleRepository(new OrganisationContext()))
{
IList<Person> lst = repo.GetList(p => p.Id > 0 && p.Archived == false, x => x.Organisation);
this.BlvPersons = new SortableBindingList<Person>(lst);
} }
}
//ect
}
public class BaseController : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
public void SendChange(string propertyName)
{
System.Diagnostics.Debug.WriteLine("PropertyChanged {0} = {1}", propertyName, GetType().GetProperty(propertyName).GetValue(this, null));
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// etc
I have a bindingSource on my form and set bindingSource.DataSource = controller.BlvPersons
If I Update data values using the controller I will see these changes in the form.
However I cant work out how to set the current record in the controller and see the change in the form.
You can use BindingSource.Find method and set the Position property to the results of the Find method.
The Find method can only be used when the underlying list is an
IBindingList with searching implemented. This method simply refers the
request to the underlying list's IBindingList.Find method.
To implement search on a generic BindingList requires various steps. First, you have to indicate that searching is supported by overriding the SupportsSearchingCore property. Next, you have to implement the IBindingList.Find method, which performs the search.
You can use examples from here or here.
Because I don't want a winforms reference in my controller class, I don't want to share the bindingSource between the form and the controller.
Instead I came up with the idea of having a RecordPosition property in the controller and binding it to a textbox
In my form I have
BindHelper.BindText(this.textRecordPosition,this.controller,"RecordPosition");
private void textRecordPosition_TextChanged(object sender, EventArgs e)
{
this.bindingSource.Position = Convert.ToInt32(textRecordPosition.Text) -1;
}
private void bindingSource_PositionChanged(object sender, EventArgs e)
{
this.controller.RecordPosition = this.bindingSource.Position + 1;
}
In my controller I have
public int RecordPosition
{
get
{
return this._position;
}
set
{
this._position = value;
this.SendChange("RecordPosition");
}
}
In my BindHelper class I have
public static void BindText(TextBox box, object dataSource, string dataMember)
{
var bind = new Binding("Text", dataSource, dataMember, true, DataSourceUpdateMode.OnPropertyChanged);
box.DataBindings.Add(bind);
}
In Windows Forms and C#, I am inheriting from the TextBox class. I override the Text property from TextBox. Everything goes well until I try to use the TextChanged event. The OnTextChanged event does not work properly here, as the Text.set property is not invoked.
Initial field content 123, txpText.Text = 123
Field content changed to a , txpText.Text still 123
Field content changed to aa , txpText.Text still 123
Field content changed to aaa , txpText.Text still 123
Here is my custom TextBox code
public class ShowPartialTextBox : System.Windows.Forms.TextBox
{
private string _realText;
public override string Text
{
get { return _realText; }
set // <--- Not invoked when TextChanged
{
if (value != _realText)
{
_realText = value;
base.Text = _maskPartial(_realText);
//I want to make this _maskPartial irrelevant
}
}
}
protected override void OnTextChanged(EventArgs e)
{
//Always called. Manually invoke Text.set here? How?
base.OnTextChanged(e);
}
private string _maskPartial(string txt)
{
if (txt == null)
return string.Empty;
if (_passwordChar == default(char))
return txt;
if (txt.Length <= _lengthShownLast)
return txt;
int idxlast = txt.Length - _lengthShownLast;
string result = _lpad(_passwordChar, idxlast) + txt.Substring(idxlast);
return result;
}
}
Here is the Form class
public partial class Form1 : Form
{
private ShowPartialTextBox txpText;
private void InitializeComponent()
{
txpText = new ShowPartialTextBox();
txpText.Text "123";
txpText.TextChanged += new System.EventHandler(this.txpText_TextChanged);
}
private void txpText_TextChanged(object sender, EventArgs e)
{
label1.Text = txpText.Text; //always shows 123
}
}
I use _maskPartial. It is altering the displayed Text, while still preserving its real content. I want this custom TextBox to "almost" simulate PasswordChar property, with showing the last x characters.
Easy to see when you set a breakpoint on the Text property setter. You assume that typing in the text box will call the setter. It doesn't. One fix is this:
protected override void OnTextChanged(EventArgs e) {
_realText = base.Text;
base.OnTextChanged(e);
}
But you'll have to make that work with _maskPartial(), it surely isn't irrelevant.