I'm using a BindingSource to connect a single large data-structure to numerous controls (no SQL, just one data-structure, lots of controls). Old-style Windows Forms application, Visual Studio 2012 Express. I have managed to wrap numerous GUI components with properties to work around the lack of direct support for control binding of things like radio button groups, multi-select listboxes, the title bar, etc. All this works great, updates flow nicely in both directions between the GUI and the data-structure.
I need to track if any changes to the data-structure have been made via any control on the GUI, but I can't see how to do this (I did look at previous related questions here)... This is needed to provide a simple indication of "changes made but not saved", with an asterisk in the title bar and a warning if the user tries to exit the application without saving changes.
Thanks in advance for any help !
You'll have to implement the INotifyPropertyChanged interface from within your object classes, then catch whenever a change occurs through proper event handlers for your type class within your DataSource BindingSource property.
Here's an example:
using System;
using System.ComponentModel;
namespace ConsoleApplication1
{
internal class Program
{
class Notifications
{
static void Main(string[] args)
{
var daveNadler = new Person {Name = "Dave"};
daveNadler.PropertyChanged += PersonChanged;
}
static void PersonChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Something changed!");
Console.WriteLine(e.PropertyName);
}
}
}
public class Person : INotifyPropertyChanged
{
private string _name = string.Empty;
private string _lastName = string.Empty;
private string _address = string.Empty;
public string Name
{
get { return this._name; }
set
{
this._name = value;
NotifyPropertyChanged("Name");
}
}
public string LastName
{
get { return this._lastName; }
set
{
this._lastName = value;
NotifyPropertyChanged("LastName");
}
}
public string Address
{
get { return this._address; }
set
{
this._address = value;
NotifyPropertyChanged("Address");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Related
I wanna bind a TextBox to a class property, so when this property changes, my TextBox changes automatically too (Windows Forms).
I have a class like this:
class Device : INotifyPropertyChanged
{
private string can_rpm;
public string Can_rpm
{
get { return can_rpm; }
set { can_rpm = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
\\lots of other codes
}
My main form has some code like this (with a textbox called 'tbTest'):
private void Form1_Load(object sender, EventArgs e)
{
Device device= device = new Device();
tbTest.DataBindings.Clear();
tbTest.DataBindings.Add(new Binding("Text",device,"Can_rpm",true,DataSourceUpdateMode.OnPropertyChanged));
\\lots of other stuff
}
My problem: My textBox never updates! A have some other code that updates the 'Can_rpm' property, but nothing shows on my textbox.text. BUT, if I change the empty value of my textbox to something else, my property DOES change too!
So it's working 'one way', but not the other!
I've searched here and googled it, but all I find is examples that does what is already done in my code, but mine doesn't work.
Thanks for helping if you can.
Try with this:
tbTest.DataBindings.Add(nameof(TextBox.Text), device, nameof(Device.Can_rpm));
I've tested the code with your Device class code and this code in the form constructor:
var device = new Device();
this.textBox1.DataBindings.Add(nameof(TextBox.Text), device, nameof(Device.Can_rpm));
device.Can_rpm = "Hello";
After that, my textbox has "Hello" text.
UPDATE
You need update controls always in the thread in which they was created, usually in the main thread. I use a Form extension methods to do that:
public static class FormExtends
{
public static void RunInMyThread(this Form form, Action operation)
{
if (form.InvokeRequired)
{
form.BeginInvoke(operation);
}
else
{
operation();
}
}
}
With the previous extension, you can do (in your Form code) your updates in this way:
this.RunInMyThread(() => device.Can_rpm = "Hello");
Another way to do that:
public class Device : INotifyPropertyChanged
{
private static SynchronizationContext GuiContext = SynchronizationContext.Current;
private string can_rpm;
public string Can_rpm
{
get { return can_rpm; }
set { can_rpm = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
GuiContext.Post(
s => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)),
null);
}
}
}
GuiContext is initialized in the main thread so it runs the code in that thread. If you change your PropertyChanged event to throw in the Post of that context, you don't need take care about where your device properties are changed because the notiy always run in the main thread.
I have a class that gathers information about a machine (this is an example - in total GetInfo() may take minutes to run):
public class Scanner
{
public void GetInfo()
{
this.Name = GetName();
this.OS = GetOS();
}
public string Name { get; set; }
public string OS { get; set; }
public string Status { get; set; }
private string GetName() { this.Status = "Getting computer name"; /*More operations*/ }
private string GetOS() { this.Status = "Getting OS"; /*More operations*/ }
}
This is called by a form that needs to provide status feedback to the user.
TextBox1.Text = scanner.Status;
My question is, to achieve this, what is the best way to implement threading so that the application remains responsive?
I have got a BackgroundWorker running in the form and calling GetName(), GetOS() etc itself and that works fine, but the code isn't very repeatable - to keep maintenance low I want to keep a GetInfo() method in the class so if I need to run a scan from elsewhere theres only one piece of code that knows about how to.
I could move the BackgroundWorker in to GetInfo(), but then how would the form be able to check the Status property without doing a loop and locking up the UI?
Maybe have a BackgroundWorker in the form run GetInfo() and then run another Worker that would check Status and update the form if a change is detected?
This is my first proper go at Threading and can't get my head around what, I think, is a fairly common task so any help would be appreciated.
Note: I'm also well open to suggestions for other ways to implement the Status property - hopefully you get what I'm trying to achieve.
/edit: Some clarification.
Scanner.GetInfo() would be called manually, for example on a form button click. GetInfo() would then start populating the objects properties as it goes gathering information, and might take 5 minutes to complete.
I need to be able to keep the user up to date on its status, and the only way I can think of that happening (with my current knowledge) is for GetInfo() to update a 'Scanner.Status' property, which the form can then check at interval/within a loop and update the UI when it changes.
How about using INotifyPropertyChanged with a thread in the class as follows:
public class Scanner : INotifyPropertyChanged
{
private string _Name, _OS;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get
{
return _Name;
}
set
{
if (value != _Name)
{
_Name = value;
NotifyPropertyChanged("Name");
}
}
}
public string OS
{
get
{
return _OS;
}
set
{
if (value != _OS)
{
_OS = value;
NotifyPropertyChanged("OS");
}
}
}
public void GetInfo()
{
_Name = string.Empty;
_OS = string.Empty;
new Thread(() => this.Name = GetName()).Start();
new Thread(() => this.OS = GetOS()).Start();
}
private void NotifyPropertyChanged(string pName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
}
private string GetName()
{
return "long name operation here";
}
private string GetOS()
{
return "long OS operation here";
}
}
Then in your form you could use the following:
Scanner m_Scanner = new Scanner();
public void Main()
{
m_Scanner.PropertyChanged += UpdateGUI;
m_Scanner.GetInfo();
}
private void UpdateGUI(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "OS")
txtOs.Text = m_Scanner.OS;
else if (e.PropertyName == "Name")
txtName.Text = m_Scanner.Name;
}
I have a WPF application that displays a window with various information in it. In my code I create an instance of a custom class that I created which reads information from RFID card reader. To keep it simple - every now and then someone would swipe their card using the card reader which would generate a string that I successfully capture using my custom class.
The problem that I have is that I need to return that value to the window application so that I can update the information displayed in the window based on the value read. This is not as simple as calling a function in the custom class and returning a value as I don't know when exactly someone would swipe their card.
One solution that I could think of was to make a timer and pool the custom class every second or so to check if someone swiped their card, however, I don't think that's an effective solution.
Since I'm relatively new to WPF I'm assuming that the right way to do it is using INotifyProperyChanged but I'm unsure how to do it. Open to any other suggestions as well, thank you!
Create an event on your CardReader class that you can listen to on your ViewModel.
class CardInfo
{
public string CardDetails { get; set; }
}
class CardSwipedEventArgs
: EventArgs
{
public CardInfo SwipedCard { get; set; }
}
interface ICardReader
{
event EventHandler<CardSwipedEventArgs> CardSwiped;
}
class MyViewModel : INotifyPropertyChanged
{
private ICardReader _cardReader;
private string _lastCardSwiped;
public ICardReader CardReader
{
get
{
return _cardReader;
}
set
{
_cardReader = value;
_cardReader.CardSwiped += OnCardSwiped;
}
}
private void OnCardSwiped(object sender, CardSwipedEventArgs e)
{
LastCardSwiped = e.SwipedCard.CardDetails;
}
public string LastCardSwiped
{
get
{
return _lastCardSwiped;
}
set
{
_lastCardSwiped = value;
this.OnPropertyChanged("LastCardSwiped");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Thank you all for your posts. Using events was definitely the way but it wasn't easy to understand how they worked. Your feedback definitely helped but this article helped me understand how events worked best and how to implement them so I could deal with the issue successfully:
http://www.codeproject.com/Articles/9355/Creating-advanced-C-custom-events
Create an event on the class that reads the data from RFID.
public class CardSweepedEventArgs : EventArgs {
private readonly string _data;
public string Data { get { return _data; } }
public CardSweepedEventArgs(string data) {
_data = data;
}
}
public class YourReadinClass {
public EventHandler<CardSweepedEventArgs> CardSweeped;
// rest of logic.
}
In your class then subscribe to the event and do the necessary.
I was wondering if I can use automatic properties and still be able to fire events on property changed. Here are my current classes. (The actual User class got way more properties/fields of course).
public delegate void UserEventHandler(object sender, EventArgs e);
public class User
{
public event UserEventHandler Changed;
private string _UserName;
public string UserName
{
get
{
return _UserName;
}
private set
{
_UserName = value;
this.OnChanged(EventArgs.Empty);
}
}
protected void OnChanged(EventArgs e)
{
if (Changed != null)
{
Changed(this, e);
}
}
}
So I was wondering if there is a way I could take advantage of the automatic properties and still be able to fire the OnChanged events.
In other words : Are semi-automatic properties possible?
You can use PostSharp.
Example
Very late to the party, but this question still appears on google.
There's a package which works in much the same way as the PostSharp example, but is free: Fody.Propertychanged.
The project's README, and the wiki pages it links to, do a very good job of explaining it.
I modified your code a little for the event can be accessed and using the ready made EventHandler.
public class User
{
public event EventHandler AgeChanged;
private string _UserName;
public string UserName
{
get
{
return _UserName;
}
set
{
_UserName = value;
this.OnAgeChanged(this,EventArgs.Empty);
}
}
protected virtual void OnAgeChanged(object sender, EventArgs e)
{
if (AgeChanged != null)
{
AgeChanged(sender, e);
}
}
}
How to Set Events:
var user = new User();
//subscribe to events
user.AgeChanged+= (s,e) => Console.WriteLine("UserNamed changed to {0}",user.UserName);
//modify UserName and now event is fired
user.UserName="Jack";
see a working demo
I have a listbox which is databound to a collection of objects.
I want to modify the way the items are displayed to show the user which one of these objects is the START object in my program.
I tried to do this the following way, but the listbox does not automatically update.
Invalidating the control also didn't work.
The only way I can find is to completely remove the databindings and add it back again. but in my case that is not desirable.
Is there another way?
class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get
{
if (PersonManager.Instance.StartPerson == this)
return _name + " (Start)";
return _name;
}
set
{
_name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public Person(string name)
{
Name = name;
}
}
This is the class wich manages the list and the item that is the start
class PersonManager
{
public BindingList<Person> persons { get; set; }
public Person StartPerson { get; set; }
private static PersonManager _instance;
public static PersonManager Instance
{
get
{
if (_instance == null)
{
_instance = new PersonManager();
}
return _instance;
}
}
private PersonManager()
{
persons = new BindingList<Person>();
}
}
In the form I use the following code
private void button1_Click(object sender, EventArgs e)
{
PersonManager.Instance.StartPerson = (Person)listBox1.SelectedItem;
}
I'm pretty sure that the problem is that, when you do this, you're effectively making the Person.Name properties "get" accessor change the value (and act like a set accessor as far as the UI is concerned).
However, there is nothing that's updating the bindings to say that this is happening. If PropertyChanged got called when you set start, I believe this would update.
It's clunky, but the way you have it written, I believe you could add this and make it work (NOTE: I didn't test this, so it ~may~ have issues):
private void button1_Click(object sender, EventArgs e)
{
Person newStart = (Person)listBox1.SelectedItem;
if (newStart != null)
{
PersonManager.Instance.StartPerson = newStart;
newStart.Name = newStart.Name; // Dumb, but forces a PropertyChanged event so the binding updates
}
}