Custom NumericUpDown ValueChanged event in WPF - c#

Since WPF doesn't contain a NumericUpDown control as known from WinForms, I implemented my own and take care of upper and lower value bounds as well as other validation.
Now, the WinForms NumericUpDown held aValueChanged event which would be nice to implement it too. My question is: How can I lift the TextChangedEvent of a TextBox to my main application? Delegates? Or are there any other preferred ways to implement this?

I would personally prefer to use a delegate for this purpose, as I can set my own input parameters for it. I would do something like this:
public delegate void ValueChanged(object oldValue, object newValue);
Using object as the data type would allow you to use different numerical types in the NumericUpDown control, but then you'd have to cast it to the correct type each time... I'd find this a bit of a pain, so if your control would only use one type, int for instance, then you could change your delegate to this:
public delegate void ValueChanged(int oldValue, int newValue);
Then you would need a public property for users of the control to attach handlers:
public ValueChanged OnValueChanged { get; set; }
Used like so:
NumericUpDown.OnValueChanged += NumericUpDown_OnValueChanged;
...
public void NumericUpDown_OnValueChanged(int oldValue, int newValue)
{
// Do something with the values here
}
Of course, that's no good unless we actually call the delegate from inside the control and let's not forget to check for null in case no handler has been attached:
public int Value
{
get { return theValue; }
set
{
if (theValue != value)
{
int oldValue = theValue;
theValue = value;
if (OnValueChanged != null) OnValueChanged(oldValue, theValue);
NotifyPropertyChanged("Value"); // Notify property change
}
}
}

Related

Trigger event on value change 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().

Create an event to watch for a change of variable

Let's just say that I have:
public Boolean booleanValue;
public bool someMethod(string value)
{
// Do some work in here.
return booleanValue = true;
}
How can I create an event handler that fires up when the booleanValue has changed? Is it possible?
Avoid using public fields as a rule in general. Try to keep them private as much as you can. Then, you can use a wrapper property firing your event. See the example:
class Foo
{
Boolean _booleanValue;
public bool BooleanValue
{
get { return _booleanValue; }
set
{
_booleanValue = value;
if (ValueChanged != null) ValueChanged(value);
}
}
public event ValueChangedEventHandler ValueChanged;
}
delegate void ValueChangedEventHandler(bool value);
That is one simple, "native" way to achieve what you need. There are other ways, even offered by the .NET Framework, but the above approach is just an example.
INotifyPropertyChanged is already defined to notify if property is changed.
Wrap your variable in property and use INotifyPropertyChanged interface.
Change the access of the BooleanValue to private and only allow changing it through one method for consistency.
Fire your custom event in that method
.
private bool _boolValue;
public void ChangeValue(bool value)
{
_boolValue = value;
// Fire your event here
}
Option 2: Make it a property and fire the event in the setter
public bool BoolValue { get { ... } set { _boolValue = value; //Fire Event } }
Edit: As others have said INotifyPropertyChanged is the .NET standard way to do this.
Perhaps take a look at the INotifyPropertyChanged interface. You're bound to come across it's use again in future:
MSDN: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
CallingClass.BoolChangeEvent += new Action<bool>(AddressOfFunction);
In your class with the bool property procedure:
public event Action<bool> BoolChangeEvent;
public Boolean booleanValue;
public bool someMethod(string value)
{
// Raise event to signify the bool value has been set.
BoolChangeEvent(value);
// Do some work in here.
booleanValue = true;
return booleanValue;
}
No it is not possible* to get notified about for changes in value of a variable.
You can achieve almost what you want by making the value to be a property of some class and fire events on change as you wish.
*) if your code is debugger for a process you can make CPU to notify you about changes - see data chage breakpoints in Visual Studio. This will require at least some amount of native code and harder to implement correctly for manged code due to hance of objects to be moved in memory by GC.

Direct way for firing up some events when a boolean field changes?

I wonder if there is a way to directly control some events when a boolean field is changing from true to false?
something like using delegate?
Actually I have lots of user input controls (check box, text box and etc..) and I am looking for a way around the using of foreach and control.disabled stuff.
Properties are always good to fire up event from within:
private bool check = false;
public bool MyCheckboxChecked
{
get
{
return check;
}
set
{
if (check == true && value == false)
MyEvent("MyCheckboxChecked is about to change from true to false!");
check = value;
}
}
If you want to monitor public fields of controls (ie CheckBox.Checked), you can always hookup for events already provided by them like CheckedChanged.
Use a property to set the field value. Raise a PropertyChanged event in the setter of the property.
Sample code:
bool Flag
{
get { return this.flag; }
set
{
if (this.flag != value)
{
this.flag = value;
// Raise PropertyChanged event here ..
}
}
}
That's right. If you are using WPF, you can implement INotifyPropertyChanged interface and Binding which is very convenient for you need.
Or use action:
private bool isIt;
public Action YourAction{get; set;}
public bool IsIt
{
get{return isIt;}
set{isIt = value; if(YourAction != null) YourAction();}
}
Sure there is, use an event raiser on the set accessor of a property instead of changing directly the member variable...

c# bool.change event

Can I setup an event listener so that when a bool changes a function is called?
You should use properties in C#, then you can add any handling you want in the setter (logging, triggering an event, ...)
private Boolean _boolValue
public Boolean BoolValue
{
get { return _boolValue; }
set
{
_boolValue = value;
// trigger event (you could even compare the new value to
// the old one and trigger it when the value really changed)
}
}
Manually, Yes you can
public delegate void SomeBoolChangedEvent();
public event SomeBoolChangedEvent SomeBoolChanged;
private bool someBool;
public bool SomeBool
{
get
{
return someBool;
}
set
{
someBool = value;
if (SomeBoolChanged != null)
{
SomeBoolChanged();
}
}
}
Not sure however if this is what you are looking for.
The important question here is: when a bool what changes?
Since bool is a value type you cannot pass around references to it directly. So it doesn't make sense to talk about anything like a Changed event on bool itself -- if a bool changes, it is replaced by another bool, not modified.
The picture changes if we 're talking about a bool field or property on a reference type. In this case, the accepted practice is to expose the bool as a property (public fields are frowned upon) and use the INotifyPropertyChanged.PropertyChanged event to raise the "changed" notification.
Look into implementing INotifyPropertyChanged. MSDN has got a great How To on the subject

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();
}
}

Categories

Resources