WPF Binding not updating from DispatcherTimer - c#

The following is a stripped back version of a problem I'm encountering. It's a fairly common issue, but I'm struggling to find the solution.
I've an instantiated class which I've bound to an item on my main window. This class contains a DispatcherTimer which is used to update a value. In the example given it's incrementing this value by 1 every second.
I'd expect the bound item on my form to reflect this change by updating its value accordingly, however it never updates.
From reading other responses to similar questions on StackOverflow I've a feeling this is due to the nature of the main UI thread running separately to the thread which is causing the increment.
I'm banging my head against a wall though trying to get this binding to update with each call of my DispatcherTimer.
The following is the form element I'm wanting to update every second:
<TextBox Text="{Binding val}" Width="100"/>
Next, this is the instantiation of the class containing the timer and my applications configuration:
BasicTimer basictimer;
public MainWindow()
{
InitializeComponent();
basictimer = new BasicTimer();
DataContext = basictimer;
}
Lastly, here's the class I've created. When created it configures a timer which it uses to update a value every second. Each time this value is updated I'd expect the main UI to be notified of the change and update accordingly. However, this message doesn't seem to be getting through.
class BasicTimer: INotifyPropertyChanged
{
DispatcherTimer _timer;
uint _val = 10;
public uint val
{
get
{
return _val;
}
set
{
if(_val!=value)
{
_val = value;
OnPropertyChanged("Value");
}
}
}
public BasicTimer()
{
_timer = new DispatcherTimer();
_timer.Tick += new EventHandler(TimerTick);
_timer.Interval = new TimeSpan(0, 0, 1);
_timer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
val++;
Console.WriteLine(val);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
I think I've managed to avoid the usual pitfalls of forgetting INotifyPropertChanged, and other bound values from other models are working just fine. It's just this property which is being updated via a thread that I'm having trouble with. I've also tried creating a similar timer using a simple Timer but I'm having the same problem.
Any thoughts would be very much appreciated, thanks!

I believe your problem is in the call to OnPropertyChanged:
uint _val = 10;
public uint val
{
get
{
return _val;
}
set
{
if(_val!=value)
{
_val = value;
OnPropertyChanged("Value");
}
}
}
That should be
OnPropertyChanged("val");
The string in the call to OnPropertyChanged has to match the name of the property.
EDIT
The reason you want the name passed to OnPropertyChanged to always match the name of the property is because the data binding subscribes to your object's PropertyChanged event and is watching for the value in that string in the parameter passed to its event handler. If the name passed doesn't match the name it is looking for, it ignores the notification. It only updates the value of the control bound to that property when the names match.
As Aron mentioned in the comments, you can use the CallerMemberAttribute in your OnPropertyChanged method to ensure the property name is always passed to the method properly. According to the answer to this StackOverflow question, your method would look like this:
protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
You would then call it with no parameter from the property's setter and the name will always be correct.
As the answer to the linked question says, this code compiles into IL code that is identical to what is produced if you hard code the string in your call, so this trick will always work and will be just as fast.

Related

How to subscribe to events fired from any collection element in C#?

I have made the following Dictionary, and I would like to be able to subscribe to the event fired from any of its' elements, in order to know which dictionary elements' properties were changed.
Here is my class:
public class BlockInput : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private int _value;
public int Value
{
get
{
return _value;
}
set
{
_value = Value;
NotifyPropertyChanged("Value");
}
}
}
I create a concurrent dictionary like the following:
public ConcurrentDictionary<string, BlockInput> Inputs;
How would this be achieved, in order for me to find every time that one of the BlockInput values were changed/event for each element fired?
Thanks for your time.
I don't believe you have another way than manually subscribing to all events yourself:
foreach (BlockInput item in Inputs.Values) {
item.PropertyChanged += BlockInput_PropertyChanged;
}
private void BlockInput_PropertyChanged(object sender, PropertyChangedEventArgs e) {
var blockInput = sender as BlockInput; // Get the item that was changed
// Do stuff
}
You would have to subscribe to all added items and unsubscribe from removed ones as well if you plan to add or remove items from the Dictionary.
In practical use of this Interface (wich is primarily MVVM) usually this is enough. You have the GUI classes do all the plumbing work of subscribing to events. You only need to provide 3 Change Notifications:
The one of each property of BlockInput. You did that in your example code.
The one if something is added or removed from the Collection. That is what ObservableCollection<BlockInput> will take care off. It is also the only thing the OC will take care off.
The one on the property exposing the ObservableCollection<BlockInput>. The OC is notoriously bad at bulk modifications, so often you need to prepare a new instance in code, with Exposing being the last step.
If you do not have a MVVM use case, please leave a comment. I can think of 2 ways on top of manually subscribing to each Event.

How do I correctly implement INotifyPropertyChanged for my bool property and bind to CheckBox.IsChecked?

Novice here. I've been trying to wrap my head around databinding, and wanted to do try out two-way binding of a checkbox in the view to a boolean in a separate class that I've called "State". The point is to ensure that they are always in sync.
So I've made a checkbox in the view and bound it to the aforementioned boolean property in the State-class, accompanied by a button that bypasses the checkbox and toggles the boolean property directly (aptly labeled 'Ninja!'). The point was to test that the checkbox' databinding reacts when the property changes. However, I can't for the best of me figure out how the OnPropertyChanged-method is supposed to be invoked when the property changes.
Here's what I have so far:
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
And the code for the "State"-class I've made:
namespace TestTwoWayBinding
{
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged(Checked);
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Checked));
}
}
}
}
And the code-behind on the view for initialization and handling the events:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
From what I gather, OnPropertyChanged expects a String propertyName, but I don't know what that would entail here. When I put in the name of the property (Checked), then that naturally refers to a boolean, not a string. What am I not getting? And what else am I doing wrong, as the checkbox doesn't register the property change when I change it through the button?
The two answers which suggest you pass the string literal "Checked" will work, but IMHO aren't the best way to do it. Instead, I prefer using [CallerMemberName] when implementing the OnPropertyChanged() method. (I have no idea what that third answer is all about…it doesn't appear to have anything to do with this question, and I'd guess it was just copy/pasted from somewhere else).
Here's an example of how I'd write your State class:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The key here is that the parameter marked with [CallerMemberName] will automatically be filled in with the correct value from the caller, simply by not passing any value. The default value of null is there just so the compiler will allow the caller to not pass a value.
Note that I also simplified the Toggle() method. There's no need to use an if statement to transform one bool value into another; that's what the Boolean operators are there for.
I also changed the OnPropertyChanged() method so that it's thread-safe, i.e. won't crash if some code unsubscribes the last handler from the PropertyChanged event between the time the event field is compared to null and the time the event is actually raised. Typically, this is a non-issue as these properties are nearly always accessed only from a single thread, but it's easy enough to protect against and is a good habit to get into.
Note that in C# 6, you have the option of just writing PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); for the method body. Not everyone is using the new compiler 100% of the time yet, so I just mention that as an optional choice for you.
Naturally, you also need to set the DataContext correctly, as shown in one of the other answers:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
Though, personally, I'm not sure I'd bother with the constructor. You appear to have no other code that would set checkBox.IsChecked, so it seems to me that you're always going to get the default value anyway. Besides, you can't create your view model class in XAML if it doesn't have a parameterized constructor; in the future, you may prefer to configure your DataContext like that. E.g.:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
And in the window's constructor:
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}
See also the related Q&A Automatically INotifyPropertyChanged. The question there is really about something different — they want to implement the interface without having to explicitly write anything in the property setter — but for better or worse, the answers they got are really more about your scenario, where it's just a question of simplifying the property setter implementation rather than making it completely automatic.
I have to admit, I would've thought there would have been another question already with which to mark yours as a duplicate. And I did find lots of related questions. But nothing that focuses directly on just "how do I implement and use a view model that implements INotifyPropertyChanged?", which is really what your question seems to be about.
Addendum:
I did some more searching, and while none of these seem like they would be considered exact duplicates per se, they all have good information that help address the question about implementing INotifyPropertyChanged:
Use of Attributes… INotifyPropertyChanged
INotifyPropertyChanged for model and viewmodel
BindableBase vs INotifyChanged
How to write “ViewModelBase” in MVVM (WPF)
You are real close. You need to make 2 small changes and your test works:
Assign the DataContext of your Window to the _state variable.
Put the string "Checked" into the OnPropertyChanged and pass propertyName to the PropertyChangedEventArgs in the OnPropertyChanged method.
So your MainWindow ctor becomes:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
and the State class file looks like:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As a novice, I recommend you learn more about Model-View-ViewModel MVVM Design Pattern. It is a common pattern with WPF and helps encourage separation of concerns (keeping your business logic out of your user interface logic)
The OnPropertyChanged method expects the Checked property's name as argument - at the moment, you're passing its value!
This means, change the Checked property declaration to:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}

Trigger a custom event for an "external" value change

I'm reading values from a certain process memory. Let's say that I fetch them in the following way:
var foo = memoryService.GetFoo();
var bar = memoryService.GetBar();
Since it doesn't exist any events for memory changes, I would like to create custom events using polling (if you don't have any other suggestions).
Since I don't know when the values might change, the polling interval has to be set to a suitable value. I don't know how to actually write this, but something like this might do (not sure if it compiles):
public class MemoryChange : INotifyPropertyChanged
{
private Timer _timer;
public SomethingChanged(double polingInterval)
{
_timer = new Timer();
_timer.AutoReset = false;
_timer.Interval = polingInterval;
_timer.Elapsed += timer_Elapsed;
_timer.Start();
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
}
}
Do I need to create one class that implements INotifyPropertyChanged for each value (foo and bar in this case)?
Is there any way to make it run on a non blocking thread (using a Task perhaps?)?
Do I have to use polling to make this work?
Any input is much appreciated!
If you have access to your MemoryService from your main view model, then you could define a simple delegate to solve your problem.
In your MemoryService, define the delegate and related property:
public delegate void DataUpdate(object someData);
public DataUpdate OnDataUpdate { get; set; }
In the parent view model, attach a handler for the delegate:
MemoryService memoryService = new MemoryService();
memoryService.OnDataUpdate += MemoryService_OnDataUpdate;
Back in MemoryService when the data is ready:
var foo = memoryService.GetFoo();
// Always check for null
if (OnDataUpdate != null) OnDataUpdate(foo);
Now in the parent view model:
public void MemoryService_OnDataUpdate(object someData)
{
// Do something with the new data here
}
You can find out more about using delegate objects from the Delegates (C# Programming Guide) page on MSDN.
I am not sure in what context you will be using your memory service though I will give it a try to answer your quesiton.
Yes, you will have to implement INotifyPropertyChanged in every class.
Yes there is a way, Google knows it.
You can use polling or you could listen to PropertyChanged event. That would be the callback approach where you get notified when a changes happened.

DependencyProperty ValueChanging Event

I'm currently using the following code to be notified when a DependencyProperty's Value has changed:
DependencyPropertyDescriptor propDescriptor = DependencyPropertyDescriptor.FromProperty(property, control.GetType());
propDescriptor.AddValueChanged(control, controlChangedHandler);
This works great and is quite simple, but what I really need now is to be notified when a DependencyProperty's Value is about to change. I thought there would be a DependencyPropertyDescriptor.AddValueChanging() method, but it doesn't seem to exist. Any ideas how I can create this functionality?
I need to be able to cancel the change, fire off some asynchronous backend logic, and only have the control's property really change if the backend logic succeeds.
I solved the problem at hand by implementing wrapping my IODevice in an INotifyPropertyChanged implementation and binding it to the DependencyProperty.
The magic is in the fact that IODeviceWrapper.Value's setter doesn't actually set the value, but rather does the IO. It turns out that when the setter is called by the DependencyProperty it's bound to, the change hasn't yet been committed to the DependencyProperty's value. Hence, IODeviceWrapper.Value's setter gets called in by the DependencyProperty's sudo-, non-existent ValueChanging event.
At this time, if the DependencyProperty reads from the Value's getter it will get the old value until the IO is complete. When the IO is complete IODeviceWrapper.Value's PropertyChanged event gets fired, and the DependencyProperty then reads the new value.
My flawed design is now working flawlessly. Here's the code in case anyone else is interested. Ignore the naysayers.
public class IODeviceWrapper : INotifyPropertyChanged
{
public IODeviceWrapper(IODevice ioDevice)
{
_ioDevice = ioDevice;
_ioDevice.ValueChanged += ValueChanged;
}
private IODevice _ioDevice;
public event PropertyChangedEventHandler PropertyChanged;
private void ValueChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
public int Value
{
get { return _ioDevice.Value; }
set
{
//Do ansynchronous IO
Task task = new Task(() => _ioDevice.DoIO(value));
task.Start();
}
}
}

Bind a label to a "variable"

Say I have a global variable INT named X. Since X is global, we can assume that anything can modify its value so it is being changed everytime.
Say I have a Label control named "label". Here's what I want to accomplish:
I want to "bind" the value of label.Text to variable X. In such a way that when variable X is changed, it will be reflected back to label.Text.
Now, I don't want to write event listeners and play with delegates with this one (I want the least amount of code as possible). Is there a way to use the DataBinding component for this one? or any other novel techniques?
If you want to use the Databinding infrastructure, and reflect the changes made to a value, you need a way to notify the UI about the changes made to the binding value.
So the best way to do that is to use a property and implement the INotifyPropertyChanged interface, like this:
class frmFoo : Form, INotifyPropertyChanged
{
private string _foo;
public string Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged("Foo");
}
}
protected virtual void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Also remember that you need to setup the binding on the label first:
public frmFoo()
{
InitializeComponent();
lblTest.DataBindings.Add(new Binding("Text", this, "Foo"));
}
For a multi-threaded program (so almost every windows forms program) iCe's answer is not a good one, because it won't let you change the label anyway (you will get some cross-threading error). The simplest way to fix the problem is creating property in setter:
private string _labelText;
public string labelText
{
get { return _labelText; }
set
{
_labelText = value;
updateLabelText(_labelText); //setting label to value
}
}
where updateLabelText(string) is thread safe:
delegate void updateLabelTextDelegate(string newText);
private void updateLabelText(string newText)
{
if (label1.InvokeRequired)
{
// this is worker thread
updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
label1.Invoke(del, new object[] { newText });
}
else
{
// this is UI thread
label1.Text = newText;
}
}
I don't think you'd be able to bind to a public variable. A variable by itself doesn't have the ability to notify listeners of a change in its value.
That is why you need to wrap the variable in a property. In the setter you raise an event to notify the UI controls that are bound to it, so that they can refresh and display the new value. The framework has a mechanism for this - INotifyPropertyChanged - try this link for a how-to.
Create a property for X. In setter update the label.Text property.
private int _x;
public int X {
get
{
return _x;
}
set
{
_x = value;
label.Text = _x.ToString();
}
}

Categories

Resources