I read some about DataBinding, mostly complicated things like SQL or whatever XAML and stuff.
All I want my programm to do is, if the "value" of a variable changes just write it in a textbox or label. (using WindowsForms)
So far what I have:
namespace DataBinding_Test
{
public partial class Form1 : Form
{
BindingSource bs = new BindingSource();
Class1 test = new Class1();
public Form1()
{
InitializeComponent();
test.name = "Hello";
bs.DataSource = test;
label1.DataBindings.Add(new Binding("Text", bs, "name", false, DataSourceUpdateMode.OnPropertyChanged));
}
private void button1_Click(object sender, EventArgs e)
{
test.name = textBox1.Text;
}
}
}
Class1 just has a public property name. On startup lable1 will show my "Hello" string. Then on button click the name property will change. On debug I saw the actual DataSource of "bs" contains the new property value, but the label will not show anything...
Is there any realtivly easy way to do this?
The Backround is: periodically there will be a polling of sensor data throug RS232. If the value of one sensor changes I want to show this in label or textbox. Now a backroundthreaded timer will need invokes and stuff to access the GUI thread; thought this would be easier with databinding but seems not :P
Thanks to all, great site, great work! :)
Another way to make things work without implementing INotifyPropertyChanged
class Class1
{
private string name;
public string Name
{
get { return name; }
set
{
//Check if you are assigning the same value. Depends on your business logic
//here is the simplest check
if (Equals(name, value))
return;
name = value;
OnNameChanged();
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged()
{
var handler = NameChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}
The trick is to have event with the name combined by name of property and Changed suffix and to raise it whenever value of your property is changed
In order your code would work you should implement INotifyPropertyChanged interface in your binded class. Without it your binding simply doesn't know, when the change occures. There you should implenent the logic, according to which you would notify your subscribers about which when something changed in your class (the setter part) and what has changed (PropertyChangedEventArgs). See example for your class:
class Class1: INotifyPropertyChanged
{
private string name = "";
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
And change the property name from "name" to "Name" in your binding:
label1.DataBindings.Add(new Binding("Text", bs, "Name", false, DataSourceUpdateMode.OnPropertyChanged));
// create winforms project on form1 drag a textbox (testbox1)
// and a button (button1) with a button click event handler
// this updates the textbox when the button is clicked
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
MyClass Myobj = new MyClass();
public Form1()
{
InitializeComponent();
/* propertyname, datasource, datamember */
textBox1.DataBindings.Add("Text", Myobj, "Unit");
}
public class MyClass : INotifyPropertyChanged
{
private int unit = 3;
/* property change event */
public event PropertyChangedEventHandler PropertyChanged;
public int Unit
{
get
{
return this.unit;
}
set
{
if (value != this.unit)
{
this.unit = value;
NotifyPropertyChanged("Unit");
}
}
}
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Myobj.Unit += 4;
}
}
}
I created an extension method for this that I would like to share
Usage
private void Form1_Load(object sender, EventArgs e)
{
ResultLabel.Bind(NameTextBox);
WarningLabel.Bind(NameTextBox,i => i.Length == 0 ? "field required!" : "");
SendButton.Bind(NameTextBox, i => SendButton.Enabled = !(i.Length == 0));
}
Extension
public static class Extention
{
public static void Bind(this Control owner, Control dataSource)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
owner.Text = dataSource.Text;
dataSource.TextChanged += delegate (Object o, EventArgs e) { owner.Text = sender.Text; };
}
}
public static void Bind(this Control owner, Control dataSource, Func<string,string> onChange)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
owner.Text = onChange(sender.Text);
dataSource.TextChanged += delegate (Object o, EventArgs e) { owner.Text = onChange(sender.Text); };
}
}
public static void Bind(this Control owner, Control dataSource, Action<string> onChange)
{
List<EventInfo> fields = dataSource.GetType().GetEvents().ToList();
int index = fields.FindIndex(item => item.Name == "TextChanged");
if (index >= 0)
{
Control sender = dataSource as Control;
onChange(sender.Text);
dataSource.TextChanged += delegate (Object o, EventArgs e) { onChange(sender.Text); };
}
}
}
I'm not sure if that is what you want but you can can write whatever you variable contains into the Textbox or Label by using the control.Text property.
textBox1.Text ="Some other Text"
or
string variable = "Hello 2";
textBox1.Text = variable;
Why dou you want to use Databinding? Its mutch easier this way.
Related
I am trying to understand the way INotifyPropertyChanges works.
I notice in the docs example that the Property Changed event is declared.
public event PropertyChangedEventHandler PropertyChanged;
I can see where the event gets invoked from within the NotifyPropertyChanged method.
But I can't see where it gets subscribed to.
The example after pasting it into my project is as follows
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Either change the following namespace to the name of your project,
// or name your project with the following name when you create it.
namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.ClientSize = new System.Drawing.Size(284, 261);
this.Name = "Form1";
this.Load += new System.EventHandler(this.Form1_Load_1);
this.ResumeLayout(false);
}
private void Form1_Load_1(object sender, EventArgs e)
{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
}
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}
So, I'm doing a school project atm. The application needs to be able to calculate the area of squares, circles etc.
I have one form for each figure to calculate the area off. Right now I have a "main menu" and three forms (one for each figure) and I want to be able to assign a variable LatestResult within one form and access it from the main menu form.
Note: I want to be able to do this without "loading the variable into the new form" like this: Form1 FormMainMenu = new Form1(LatestResult)
I've been trying to work with Get & Set in my Variables.cs class, but I can't seem to make it work right, if it's possible to do it with this.
EDIT: Put my code in the post
My Variables.cslooks like this:
public static string latestresult2
{
get
{
return latestresult2;
}
set
{
latestresult2 = value;
}
}
And then I assign the value upon a button click in one form:
Variables.latestresult2 = breddeR + " * " + længdeR + " = " + resultat;
breddeR and længdeR are the int variables for my calculation and resultat is the result.
At last I try to do this in another form:
label1.Text = Variables.latestresult2;
EDIT 2:
From my MainView form
private void button1_Click(object sender, EventArgs e)
{
Form2 FormTrekant = new Form2();
FormTrekant.Show();
}
You may use the INotifyPropertyChanged interface to facilitate this. This interface works with both WinForms and WPF.
public class Form2 : Form, INotifyPropertyChanged
{
public string latestresult2;
public event PropertyChangedEventHandler PropertyChanged;
public string LatestResult2
{
get
{
return latestresult2;
}
set
{
latestresult2 = value;
this.OnPropertyChanged("LatestResult2");
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Form3 : Form, INotifyPropertyChanged
{
public string latestResult3;
public event PropertyChangedEventHandler PropertyChanged;
public string LatestResult3
{
get
{
return latestresult3;
}
set
{
latestresult3 = value;
this.OnPropertyChanged("LatestResult3");
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The INotifyPropertyChanged interface allows you to subscribe to another objects property changes. In the above code, the second form will raise the event when ever its LatestResult2 property has had its value changed.
Now you just have your Form1 subscribe to it.
public class MainForm : Form
{
private Form2 secondaryForm;
private Form3 thirdForm;
public string LatestValue {get; set;}
public void Form2ButtonClick() // Assume this happens in a button click event on MainWindow.
{
this.secondaryForm = new Form2();
this.secondaryForm.PropertyChanged += this.LatestValueChanged;
this.secondaryForm.Closing += this.ChildWindowClosing;
}
public void Form3ButtonClick() // Assume this happens in a button click event on MainWindow.
{
this.thirdForm = new Form3();
this.thirdForm.PropertyChanged += this.LatestValueChanged;
this.thirdForm.Closing += this.ChildWindowClosing;
}
private void LatestValueChanged(object sender, PropertyChangedEventArgs e)
{
// Do your update here.
if (sender == this.secondaryForm)
{
this.LatestValue = this.secondaryForm.LatestValue2;
}
else if (sender == this.thirdForm)
{
this.LatestValue = this.thirdForm.LatestValue3;
}
}
// Clean up our event handlers when either of the children forms close.
private void ChildWindowClosing(object sender, ClosingWindowEventHandlerArgs args)
{
if (sender == this.secondaryForm)
{
this.secondaryForm.Closing -= this.ChildWindowClosing;
this.secondaryForm.PropertyChanged -= this.LatestValueChanged;
}
else if (sender == this.thirdForm)
{
this.thirdForm.Closing -= this.ChildWindowClosing;
this.thirdForm.PropertyChanged -= this.LatestValueChanged;
}
}
}
Your MainWindow can now react to changes within Form2, without having to pass values around. One thing to note, is that you will want to unsubscribe from the event when the Form2 is closed. Otherwise you will leak.
You can specify the instance of the main view to the other view. This way, you can access the properties of the main view.
Some code to explaain;
public class MainView
{
public string LatestResult { get; set; }
}
public class ChildView
{
private readonly MainView MainView;
public ChildView(MainView mainView)
{
this.MainView = mainView;
}
public void Calculation()
{
//Some calculation
this.MainView.LatestResult = "Some result";
}
}
Now this code can be used like this:
var mainView = new MainView();
var childView = new ChildView(mainView);
childView.Calculation();
//mainView.LatestResult == "Some result"
I have a Windows Forms ListBox data-bound to a BindingList of business objects. The ListBox's displayed property is a string representing the name of the business object. I have a TextBox that is not data-bound to the name property but instead is populated when the ListBox's selected index changes, and the TextBox, upon validation, sets the business object's name property and then uses BindingList.ResetItem to notify the BindingList's bound control (the ListBox) to update itself when the TextBox's text value is changed by the user.
This works great unless the name change is only a change in case (i.e. "name" to "Name"), in which case the ListBox doesn't get updated (it still says "name", even though the value of the underlying business object's name property is "Name").
Can anyone explain why this is happening and what I should do instead? My current workaround is to use BindingList.ResetBindings, which could work for me but may not be acceptable for larger datasets.
Update 9/27/2011: Added a simple code example that reproduces the issue for me. This is using INotifyPropertyChanged and binding the textbox to the binding list. Based on How do I make a ListBox refresh its item text?
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WinformsDataBindingListBoxTextBoxTest
{
public partial class Form1 : Form
{
private BindingList<Employee> _employees;
private ListBox lstEmployees;
private TextBox txtId;
private TextBox txtName;
private Button btnRemove;
public Form1()
{
InitializeComponent();
FlowLayoutPanel layout = new FlowLayoutPanel();
layout.Dock = DockStyle.Fill;
Controls.Add(layout);
lstEmployees = new ListBox();
layout.Controls.Add(lstEmployees);
txtId = new TextBox();
layout.Controls.Add(txtId);
txtName = new TextBox();
layout.Controls.Add(txtName);
btnRemove = new Button();
btnRemove.Click += btnRemove_Click;
btnRemove.Text = "Remove";
layout.Controls.Add(btnRemove);
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
_employees = new BindingList<Employee>();
for (int i = 0; i < 10; i++)
{
_employees.Add(new Employee() { Id = i, Name = "Employee " + i.ToString() });
}
lstEmployees.DisplayMember = "Name";
lstEmployees.DataSource = _employees;
txtId.DataBindings.Add("Text", _employees, "Id");
txtName.DataBindings.Add("Text", _employees, "Name");
}
private void btnRemove_Click(object sender, EventArgs e)
{
Employee selectedEmployee = (Employee)lstEmployees.SelectedItem;
if (selectedEmployee != null)
{
_employees.Remove(selectedEmployee);
}
}
}
public class Employee : INotifyPropertyChanged
{
private string name;
private int id;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
public int Id
{
get { return id; }
set
{
id = value;
OnPropertyChanged("Id");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
}
Update 9/28/2011: The problem seems to be internal to the ListBox control, specifically the way it decides (not) to update an item if its string representation is equivalent to the new value, ignoring case differences. As far as I can tell this is hard coded into the control with no way to override it.
Think I found the problem:
I just hope this concept will be helpfull to solve your problem
I have a TextBox, the user input is compared to string "Name". My button click event is:
private void btn_Click(object sender, EventArgs e)
{
if(txt.Text == "Name")
MessageBox.Show("Value is Same");
}
If you write "name" in textbox, condition will be false. If you type type "Name" in textbox, condition will be true.
Now try changing the btn click:
using System.Globalization;
private void btn_Click(object sender, EventArgs e)
{
TextInfo ChangeCase = new CultureInfo("en-US", false).TextInfo;
string newText = ChangeCase.ToTitleCase(txt.Text);
if (newText == "Name")
MessageBox.Show("Value is Same");
}
now you type "name" or "Name" condition is true.
Remember it will just capaitalize the first letter of the string suplied. "my name" will outputted as "My Name".
And if your condition says:
if(txt.Text == "name")
MessageBox.Show(Value is Same);
Then you can try something like
string newText = (txt.Text).ToLower();
if(newText == "name")
MessageBox.Show(Value is Same);
Here the supplied string will outputted in the lower case always.
Hope it helps.
This really is the same problem as when renaming files or directories while only case is different. I suggest the same work-around that I found earlier:
if (oldValue.ToUpper() == newValue.ToUpper()){
ListBox1.Items[i] = newValue + "_tmp"; // only adding stuff to force an update
ListBox1.Items[i] = newValue; // now the new value is displayed, even only case has changed
}
Now for your question, I suggest you try to check if the setter is changing a value only in lower/upper case (a.ToUpper() == b.ToUpper()). If true, then first give a extra change, before the intended change, something like:
name = value + "_tmp";
OnPropertyChanged("Name");
name = value;
OnPropertyChanged("Name");
Hope this helps.
I want to bind a custom property of a windows form to a second property, so when I update the former the latter gets the same value.
This is the simplest example of what I'm trying to do:
public partial class Form2 : Form
{
public string MyTargetProperty { get; set; }
public string OtherProperty { get; set; }
public Form2()
{
InitializeComponent();
this.DataBindings.Add("MyTargetProperty", this, "OtherProperty");
}
private void button1_Click(object sender, EventArgs e)
{
MyTargetProperty = "test";
Console.WriteLine("OtherProperty " + OtherProperty);
}
}
When I click button1 I should be able to see that 'OtherProperty' has the same value as 'MyTargetProperty'. Am I doing something wrong? Do I miss something?
Your form needs to implement INotifyPropertyChanged for the MyTargetProperty.
Example:
class FooForm : Form, INotifyPropertyChanged
{
private int myTargetProperty;
public int MyTargetProperty
{
get { return this.myTargetProperty; }
set
{
this.myTargetProperty = value;
this.OnPropertyChanged(
new PropertyChangedEventArgs("MyTargetProperty"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
var evt = this.PropertyChanged;
if (evt != null)
evt(this, e);
}
}
Then you need to add the binding like this:
this.DataBindings.Add(
"OtherProperty",
this,
"MyTargetProperty",
false,
DataSourceUpdateMode.Never);
This will perform a one way binding. The MyTargetProperty will never be updated when the OtherProperty changes. If you need a two way binding you change the DataSourceUpdateMode and also implement a PropertyChanged for OtherProperty.
I have written a control in C# that derives from System.Windows.Forms.Control. I have added a property Selected to which I want to databind to a business entity using a BindingSource.
I’ve implemented the PropertyNameChanged pattern by adding a SelectedChanged event that I fire when the Selected property is changed.
This is my code:
public partial class RateControl : Control
{
[Category("Property Changed")]
public event EventHandler SelectedChanged;
public int Selected
{
get
{ return m_selected; }
set
{
if (m_selected != value)
{
m_selected = value;
OnSelectedChanged();
Invalidate();
}
}
}
protected virtual void OnSelectedChanged()
{
if (this.SelectedChanged != null)
this.SelectedChanged(this, new EventArgs());
}
}
When I bind to the Selected property, I see the event being subscibed to. The event is also fired when the property changes.
However the business entity is not updated. I don’t even see the getter of the Selected property being accessed.
What am I missing?
Have you got the binding's update mode set to DataSourceUpdateMode.OnPropertyChanged? Either via binding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;, or using one of the DataBindings.Add(...) overloads.
The following works for me to push values to the business object...
using System;
using System.Diagnostics;
using System.Windows.Forms;
class MyForm : Form
{
public MyForm()
{
MyBusinessObject obj = new MyBusinessObject();
Button btn = new Button();
btn.Click += delegate { Foo++; };
DataBindings.Add("Foo", obj, "Bar", false, DataSourceUpdateMode.OnPropertyChanged);
DataBindings.Add("Text", obj, "Bar");
Controls.Add(btn);
}
private int foo;
public event EventHandler FooChanged;
public int Foo
{
get { return foo; }
set
{
if (foo != value)
{
foo = value;
Debug.WriteLine("Foo changed to " + foo);
if (FooChanged != null) FooChanged(this, EventArgs.Empty);
}
}
}
}
class MyBusinessObject
{
private int bar;
public event EventHandler BarChanged;
public int Bar
{
get { return bar; }
set
{
if (bar != value)
{
bar = value;
Debug.WriteLine("Bar changed to " + bar);
if (BarChanged != null) BarChanged(this, EventArgs.Empty);
}
}
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}