Trigger event on value change C# - c#

I'm trying to monitor a value and when it is changed, to update a text field after performing some calculations with a result.
The value I'm trying to monitor comes from an AGauge property (custom control). I want to update the text field when the AGauge.Value changes.
I've looked at questions such as This One but I don't really understand how this works, or what I need to change to get the result I'm looking for.
Can anyone better explain what I need to do in order for this to work?
The AGuage.Value is a float type, incase your wondering.
Thanks in advance.
Update 1
I have now added the following code to my project:
public class AGuage
{
private float _value;
public float Value
{
get
{
return this._value;
}
set
{
this._value = value;
this.ValueChanged(this._value);
}
}
public void ValueChanged(float newValue)
{
}
}
And can get the ValueChanged to fire using the following:
AGuage n = new AGuage();
n.Value = Pressure_Gauge.Value;
Which fires everytime the Pressure_Gauge.Value is updated.
The issue, or last hurdle, I am facing now is this part:
public void ValueChanged(float newValue)
{
Form1.Pressure_Raw.text = "Working";
}
I want to update the label's text on form1 usingthe above method, however I get an error saying: An object reference is required for the nonstatic field, method, or property.
I'm not sure how to do this, I've read some information about Static properties, but how would I update the label's text value from within this?
Thanks.

This might help. You could add an event and subscribe to it in your form.
For example:
public class AGauge {
// You can either set the Value this way
public float Value {
get {return this.Value;}
set
{
// (1)
// set "Value"
this.Value = value;
// raise event for value changed
OnValueChanged(null);
}
}
// create an event for the value change
// this is extra classy, as you can edit the event right
// from the property window for the control in visual studio
[Category("Action")]
[Description("Fires when the value is changed")]
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e)
{
// (2)
// Raise the event
if (ValueChanged != null)
ValueChanged(this,e);
}
}
public Form1 : Form {
// In form, make your control and add subscriber to event
AGauge ag = new AGauge();
// (3)
ag.ValueChanged += UpdateTextBox;
// (4)
public void UpdateTextBox(object sender, EventArgs e)
{
// update the textbox here
textbox.Text = ag.Value;
}
}
Here's how this works:
At (3) you add a subscriber to the ag.ValueChanged event as described HERE.
When you go to change ag.Value, you get to (1), where Value is changed and OnValueChanged is called. This gets you to (2), where the ValueChanged event is raised. When this happens, all subscribers to that event are "notified" and call their respective methods. So when you get to (2), (4) ends up getting called because "UpdateTextBox" was set as a subscriber to the ValueChanged event. It's a bit tricky, but it is very useful.
Or if you want to continue with how you're trying to do it, you need to do this:
public class AGuage
{
private float _value;
// create object of Form1 for reference
private Form1 form1;
// pass reference to form1 through constructor
public AGauge(Form1 form1)
{
// assign
this.form1 = form1;
}
public float Value
{
get
{
return this._value;
}
set
{
this._value = value;
this.ValueChanged(this._value);
}
}
public void ValueChanged(float newValue)
{
// use the form1 reference
this.form1.Pressure_Raw.Text = "Working";
}
}
And then do this:
// if creating the AGauge object in Form1, pass "this" to the object
AGuage n = new AGuage(this);
I highly recommend you don't do it this way as this breaks the generics rule for OOP. Which means, if you try to use this AGauge control anywhere else other than in Form1, it will not work the same way. I recommend doing it with events like I have described above. It's much more universal.

You need to make your AGauge implement INotifyPropertyChanged and notify the property changing on Value. There's enough information on Google on how to do this and has been discussed hundreds of times in StackOverflow.
Then, you will need to use a Binding to bind your textbox to the AGauge value. Since you need to convert, you'll need to provide formatting and optionally parsing.
This should be something like:
var binding = new Binding("Text", myAgaugeControl, "Value");
binding.Format += BindingFormat;
binding.Parse += BindingParse;
myTextBox.DataBindings.Add(binding);
BindingFormat and BindingParse should be the converters. Format would be for converting the gauge's value to the textbox string. The most simple:
void BindingFormat(object sender, ConvertEventArgs e)
{
e.Value = e.Value.ToString();
}
BindingParse would be the opposite: if the textbox text changes, you need to parse the text and convert it to a value AGauge can understand. I'll let you figure this out.
More information on Binding, Format and Parse

What you need to do is create a custom setter for the Value property. Every time the value is set your code will call your hook method which I called ValueChanged(). In that method you can perform your calculations and then set the text field to the result.
public class AGuage
{
private float _value;
public float Value
{
get
{
return this._value;
}
set
{
this._value = value;
this.ValueChanged(this._value);
}
}
public void ValueChanged(float newValue)
{
// Action to perform on value change
// Update a text field after performing some calculations with a result.
}
}

A nice and clean option is to use Microsoft's Reactive Framework (NuGet "Rx-WinForms"). It lets you work with observables (as opposed to enumerables) in a LINQ-like manner.
Your class would look like this:
public class AGuage
{
private float _value;
private Subject<float> _values = new Subject<float>();
public float Value
{
get { return _value; }
set
{
_value = value;
_values.OnNext(value);
}
}
public IObservable<float> Values
{
get { return _values.AsObservable(); }
}
}
Now you can do things like this:
var aGuage = new AGuage();
var query =
from value in aGuage.Values
where value > 5.0f && value < 20.0f //filtering
select value * 150f + 45.3f; //computation
var subscription =
query.Subscribe(value =>
{
/* do something with the filtered & computed value */
});
aGuage.Value = 2.1f; // query.Subscribe doesn't fire
aGuage.Value = 12.4f; // query.Subscribe DOES fire
aGuage.Value = 202.1f; // query.Subscribe doesn't fire
If you want to shut down the subscription to the values just call subscription.Dispose().

Related

How do I trigger an event in the setter of a property?

I have a property by the name "TrendPoint" of type ITrendPoint. I need to raise a "OnTrendPointChanged" event whenever the value of the TrendPoint changes and perform a set of operations.
I've tried the following but can't seem to proceed further:
public class TestClass:ObservablePoint
{
private ITrendPoint trendPoint;
public ITrendPoint TrendPoint{
get{ return trendPoint ;}
set{ this.trendPoint= value;
//Need to Call the event handler
// "OnTrendPointChanged"
}
public void TestJob()
{
//The TrendPoint is being set
// here
TrendPoint=
Services.getTrendPoint();
}
//The event handler
private void
OnTrendPointChanged(object sender,
ValueChangedEventArgs<string> e)
{
switch(e.value):
case "HIGH":
Log("TR HIGH");
case "Low":
Log("TR LOW");
//Other such conditions
.
.
.
}
}
I'm not able to figure out how exactly do I get the eventhandler "OnTrendPointChanged" to execute whenever the value of "TrendPoint" changes. Any help is appreciated!
I'm not clear that there is any point to having an eventhandler in your code at all.
Setting that aside. You don't seem to use sender so that can be null. Maybe you have some code uses that which you're not showing us. In which case you need to pass some instance of whatever sender is going to be in. This is not clear from what you've provided ( naughty ).
So we can pass null, for sender but need a valuechangedeventargs instance to pass.
Something like:
public ITrendPoint TrendPoint{
get{ return trendPoint ;}
set{ this.trendPoint= value;
var vc = new ValueChangedEventArgs<TrendPoint>(this.trendPoint);
OnTrendPointChanged(null, vc);
}
Assuming OnTrendPointChanged is in the same class. Seems to be from what you've provided but maybe it isn't.
If the handler is in some other class then you will need to reference an instance of that, obviously.
How did I know a valuechangedeventargs works like that? I looked it up:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.language.intellisense.valuechangedeventargs-1?view=visualstudiosdk-2022
Hope all this helps.

WPF User Control property not getting value

I am trying to create a very simple WPF User Control to represent a digital clock.
I have a couple of things I want client code to be able to change, e.g. foreground text colour, font etc., so I've made some public properties for them. Some of the code is shown below:
public partial class DigitalClock : System.Windows.Controls.UserControl
{
public string Color { get; set; }
private Timer timer;
private string DisplayString { get { return DateTime.Now.ToString("dd-MM-yy HH:mm:ss"); } }
public DigitalClock()
{
InitializeComponent();
this.timer = new Timer();
this.timer.Tick += new EventHandler(UpdateClock);
this.timer.Interval = 1000;
this.timer.Enabled = true;
this.timer.Start();
UpdateClock(null, null);
try
{
//exception thrown here as this.Color is null
Color color = (Color)ColorConverter.ConvertFromString(this.Color);
tbClock.Foreground = new SolidColorBrush(color);
}
catch (Exception ex)
{
Console.WriteLine(">>>" + ex.Message);
}
}
private void UpdateClock(object sender, EventArgs e)
{
tbClock.Text = DisplayString;
}
}
}
I'm using it on another page like this:
<CustomControls:DigitalClock color="#ff000000" />
There are no syntax errors and the clock appears on the screen, but whenever the code hits the line where it's trying to set the colour, I just get an Object reference is not set to an instance of an object.
I assume this is something to do with the point in time at which the Color property is set, since after the first 'tick' of the timer, the value is no longer null. How do I get around this?
When you insert your control inside another XAML document, the properties that are set from this document will be set after your control is intantiated, which means at the time your constructor is invoked the Colorproperty that you may have set in your other XAML document still has its default value.
To do what you want you can either:
Listen for the Loaded event of your control (see https://msdn.microsoft.com/en-us/library/ms742302%28v=vs.110%29.aspx), this will be invoked after all the properties of the control instance are set (you may want to start your timer here, and stop it in the Unloaded event to make sure it doesn't tick when the control isn't instantiated on screen),
You can write a body for the setter of your Color property to propagate the change:
public string Color
{
set { tbClock.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(value)); }
}
If you want to set the color from another XAML document, you can also provide a property of type Brush instead of color:
public Brush ClockForeground
{
get { return tnClock.Foreground; }
set { tnClock.Foreground = value; }
}
So that in your other XAML document, you can set the color directly by letting the XAML parser translate the color name into a brush automatically:
<local:DigitalClock ClockForeground="Yellow" />
Or better, you can declare a dependency property on your control and use data binding (assuming here tbClock is a TextBlock):
public Brush ClockForeground
{
get { return (Brush)GetValue(ClockForegroundProperty); }
set { SetValue(ClockForegroundProperty, value); }
}
public static readonly DependencyProperty ClockForegroundProperty = DependencyProperty.Register("ClockForeground", typeof(Brush), typeof(DigitalClock));
public DigitalClock()
{
InitializeComponents();
...
BindingOperations.SetBinding(tbClock, TextBlock.ForegroundProperty, new Binding
{
Source = this,
Path = new PropertyPath(ClockForegroundProperty)
});
}
You should not declare properties as CLR properties. You should create instead Dependency Properties which in default allows you binding, validation and many, many more. Check this out: http://www.codeproject.com/Articles/32825/How-to-Creating-a-WPF-User-Control-using-it-in-a-W. In your example handle event Loaded like this:
this.Loaded += DigitalClock_Loaded;
void DigitalClock_Loaded(object sender, RoutedEventArgs e)
{
//your actions
Color color = (Color)ColorConverter.ConvertFromString(this.Color);
}
Properties is not yet bound in constructor.
Instead of asigning the color in the constructor, since it will be always null because the property will not be set until object is instantiated, it seems better to use the setter of the Color property, using a backing field.
Also, note that you should use Dependency Properties in this case, and take advantage of binding.

Getting the previous value of an object at ValueChanged event

I am trying to get the previous value of DateTimePicker when it hits the event ValueChanged. The other possible solution to my problem would be getting whether the user clicked on a value and chose it or it was invoked by some method. My problem is I need to know what caused this event and execute some code only if the previous value was different. I have read this and didn't like the solution to the possible way #2.
So again:
if user clicks
{
execute some code
}
else // if method was invoked
{
do NOT execute
}
OR
if value is NOT the same as previously
{
execute some code
}
else
{
do NOT execute
}
Either of that suits me, but I am unable to find the previous value in the list of available properties nor in EventArgs (which is null :( ). Hope I was clear what I want to achieve. If you ask for the reasons that I need this, it is irrelevant and I cannot edit the other code, just this method.
The ValueChanged-Event, as the name implies, will only be fired when the Value of the DateTimePicker changes.
You do not have to check if the value has changed in your code.
You are stating that you EventArgs is null, but it should be EventArgs.Empty, when used in an unmodified framework.
If you want to do something else with the LastValue you can use a customized DateTimePicker like this.
public class LastDateTimePicker : DateTimePicker {
protected override void OnValueChanged(EventArgs eventargs) {
base.OnValueChanged(eventargs);
LastValue = Value;
IsProgrammaticChange = false;
}
public DateTime? LastValue { get; private set; }
public bool IsProgrammaticChange { get; private set; }
public new DateTime Value {
get { return base.Value; }
set {
IsProgrammaticChange = true;
base.Value = value;
}
}
}
EDIT I have changed my example to met your requirements of checking programmatic changes, as stated in your comment.
The ValueChanged event is fired post validation, after the value has changed. You can't get the value before the change from this event.
If you want to extend the validation of the control then you could use the Validating event.
If you just want to trigger some code after the change then you could write code to store the previous value, somthing like this.
private DateTime oldValue = SomeDateTimePicker.Value;
private SomeDateTimePickerValueChanged(object sender, EventArgs e)
{
if (SomeDateTimePicker.Value != oldValue)
{
//Do Something
}
this.oldValue = SomeDateTimePicker.Value
}

Delegate specifics

I have problem with a delegate in a class on a project that I'm working on. The class is a GUI Component that accepts both a label and a value. The idea here is that a user can specify a label, and then link in a value from anywhere (more specifically, that value's ToString Method) so that every time that value is updated, the GUI Component is as well. This is the basics of how it is set up:
public delegate string GUIValue();
public class GUIComponent
{
GUIValue value = null; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, GUIValue Value)
{
this.text = Text;
this.value += Value;
}
public void Update()
{
this.text = this.label + this.value();
}
}
And then I call it like this
GUIComponent component = new GUIComponent("Label: ",
new GUIValue(this.attribute.ToString));
The Code compiles correctly, and the component does display, and displays the initial value for the attribute given to it, however, it does not update whenever the attribute value is changed.
My question is whether or not I even have this set up right in the first place, and if so why it would not be working. My initial thought is that it only accepts the first value return by the ToString method, since it doesn't take any arguments, but can anyone verify that?
This code:
new GUIValue(this.attribute.ToString)
will not cause the method to be called every time the attribute changes. You'd have to store the delegate and call it each time someone changes "attribute". Something like:
private event GUIValue attributeChanged = () => this.attribute.ToString();
private String attribute;
// This is a property that sets the value of attribute
public String Attribute { get { return attribute; } set { attribute = value; attributeChanged(); } }
// Now you can initialize the component using:
// GUIComponent component = new GUIComponent("Label: ", this.attributeChanged);
A delegate needs to be invoked.
What you have there is value referencing this.attribute.ToString method.
This means that when you'll call this.value() then the that function will be called.
When you change the value of this.attribute you probably did so by referencing it to a different object containing a different value.
So i guess that what you're experiencing is that every time you call update() then the old value appears. That is because that the old object isn't destroyed by the garbage collector because you are holding a reference to it via the delegate.
When you changed the attribute's value then the GUI delegate still holds the old object's method and not the new one's.
You have half of it. I think what's happening is that although you can initially get the value, your GuiComponent is not told by whatever class actually has the method given as the GUIValue delegate that the value has actually changed and to re-get it. The normal method of telling other objects that something has happened is an event, to which other objects "subscribe" by passing in delegates that will be run when the event is raised.
Here's how I would structure your code:
public interface IHaveAValueYouNeed
{
string ValueGetter();
event EventArgs ValueChanged;
}
public class GUIComponent
{
public delegate string ValueGetter();
ValueGetter getter; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, IHaveAValueYouNeed getter)
{
this.text = Text;
this.getter += getter.ValueGetter;
getter.ValueChanged += ValueUpdatedHandler;
}
public void Update()
{
this.text = this.label + this.value();
}
public void ValueUpdatedHandler(object sender, EventArgs e)
{
Update();
}
}
Now, when you pass in an implementation of the interface to the component, the component will exchange delegates with the instance, getting a reference to its ValueGetter and subscribing to its event. Implementations of IHaveAValueYouNeed should then raise the event when the value changes (either directly, or because something that would change a calculated value produced by the getter has changed). This way, the object controlling the value can tell people interested in that value that it has changed.
Why not just use ToString?
public class GUIComponent
{
object value = null; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, object Value)
{
this.text = Text;
this.value = Value;
}
public void Update()
{
this.text = this.label + this.value.ToString();
}
}

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