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();
}
}
Related
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().
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
}
}
}
I'm wondering as to why my private variable 'name' within my Events class won't change when I access the property from my Leisure class which inherits from Events. I need Leisure to use the properties to change it, and then in my form class, it should be able to read the value of 'name' from events. See below:
public partial class Form1 : Form //Main form class
{
private string eventType; //used for event type selection
private string formEventName; //used to store selected event name
private void itemSend_Click(object sender, EventArgs e)
{
//encapsulation
Events myEv = new Events();
string name=itemInput.Text;
myEv.myEvent(eventType, name);
formEventName = myEv.myName;
txtOutput.Text = "Event name is " + formEventName + "\r\n";
}
class Events:Form1
{
private string name; //private variable for event name
public string myName //used to change property value depending on what eveny type/ event name
{
get { return name; }
set { name = value; }
}
public void myEvent(string eventType, string eventName) //variable recieved from main form class
{
if (eventType == "Leisure")
{
Leisure myLes = new Leisure();
myLes.eventNames(eventName);
}
else
{
//test for other event types
}
}
class Leisure:Events
{
public void eventNames(string eventName)
{
//when adding new items add one with a capital and one without
myEventNames.Add("music");
myEventNames.Add("Music");
if (myEventNames.Contains(eventName))
{
myName = eventName;
}
else
{
MessageBox.Show("item not found, please try again"); //error message
}
}
}
It seems wrong that Events inherits from Form1.
When you say new Events(), you get a new object, unrelated to the existing form, and any changes you make to it have no effect on the existing form. That happens again when you say new Leisure().
The myName property you are using changes the name private field of myLes instance and not the name private field of myEv instance created in the ItemSend_Click.
In an Object Oriented Language when you create an instance of a class, that instance has a copy of every non-static private/public variable declared in the class. So when you write
Leisure myLes = new Leisure();
you are creating an instance of Leisure class, but this instance, while inherithing from Events has a different set of internal variables and not the same variables of the current Event instance (myEv).
Looking at your code I suggest to create a third class called
public class EventFactory
{
public Event CreateEvent(string eventType, string eventName)
{
switch(enventType)
{
case "Leisure":
Leisure myLes = new Leisure();
myLes.eventNames(eventName);
return myLes;
// case Add other specialized events here:
// break;
default:
return null;
}
}
}
change your Events class removing the inheritance from Form1 (not needed as far as I can tell) and the method myEvent
now your ItemSend_Click could be written in this way
private void itemSend_Click(object sender, EventArgs e)
{
Events myEv = new EventFactory().CreateEvent(eventType, itemInput.Text);
formEventName = myEv.myName;
txtOutput.Text = "Event name is " + formEventName + "\r\n";
}
this works because Leisure derives from Events and you could treat every Leisure instance as an Event instance.
You just change myName of the myLes (Leisure) variable in your myEv field, thats why myEv.myName is still empty.
So the problem that you're having is just a small symptom of major fundamental problems in your code that will only continue to manifest themselves as you continue.
I've re-written what you have into something that's more in line with what is more traditionally seen for what you're trying to do. It's not perfect, and I've tried to keep things fairly simple so as to not throw too much at you at once.
public partial class Form1 : Form //Main form class
{
private TextBox itemInput;
private TextBox txtOutput;
private string eventType; //used for event type selection
private string formEventName; //used to store selected event name
private void itemSend_Click(object sender, EventArgs e)
{
string name = itemInput.Text;
try
{
Event myEvent = Event.Create(eventType, name);
txtOutput.Text = "Event name is " + myEvent.Name + "\r\n";
}
catch (ArgumentException ex)//if the event name isn't valid
{
MessageBox.Show(ex.Message);
}
}
}
public abstract class Event
{
public string Name { get; private set; }
public Event(string eventName)
{
Name = eventName;
}
public static Event Create(string eventType, string eventName)
{
if (eventType == "Leisure")
{
Leisure myLes = new Leisure(eventName);
return myLes;
}
// else if { ... } test for other event types
else
{
return null;
}
}
}
public class Leisure : Event
{
private static List<string> myEventNames =
new List<string>() { "music", "Music" };
public Leisure(string eventName)
: base(eventName)
{
if (!myEventNames.Contains(eventName))
{
throw new ArgumentException("Not a valid Leisure event name");
}
}
}
So, let's go over some of the changes. First off, Event doesn't inherit from Form1. It should not do so. An event is not conceptually a type of form at all, let alone that particular type of form. An Event should have no knowledge of any form, in any way, not just inheritance. It's just some other class that Form1 will use, but it could just as easily be used by any other type of class, form or otherwise.
In addition to making Event not inherit from Form, I've made it abstract. It doesn't have any abstract methods, it's just that you shouldn't ever be creating just an plain Event, you should only ever actually create some specific type of event. Being a common base class, it's easiest to prevent accidental creation and to help improve readability by making it abstract.
I've also made Event immutable. Rather than allowing name to be changed at any time, creating an event without giving it a name, and then changing it later, I have configured it such that you need to provide the name and type before creating the event, and then there's no way of changing it once it's created. The Name is set in the constructor, and I have added a static Create method which is where the logic can go for choosing the proper subtype of Event and actually creating it. This is a simple version of the "Factory Pattern". Note that normally I wouldn't pass the type in as a string. I would make it something like an Enum, so that it's easier to tell what the valid options are.
Now onto Leisure. Logically, Leisure really is an Event and should inherit from it. Your problems were steming from the fact that you created an instance of Event, and also an instance of Leisure, and assumed that they shared the same variables. They don't, but that confusion should go away now that you can't ever have an instance of Event.
When a Leisure is created it uses the base class constructor to set Name, since it doesn't have access to set the property itself.
From what I can see myEventNames is just a list of valid names, and it doesn't appear to change between different types of Leisure instances, so it makes sense for it to be static, which means it's shared between all instances and is only created once.
I also moved the MessageBox call out of the Leisure type's constructor. Instead I'm throwing an exception. The main idea here is that you shouldn't mix your UI code with your business code. Event and Leisure are both business objects and shouldn't know anything about what, if any, UI exists. You should be able to use them from a console app, an ASP application, etc. On top of that, since what we're trying to say is that this is an invalid name and the type shouldn't exist, the end result of throwing an Excpetion in the constructor is that the object never becomes "valid". We don't allow the creation of an object that shouldn't exist, as opposed to allowing them to continue using the object anyway.
That exception is caught with the try/catch block in Form1, where it shows the appropriate MessageBox based on the failure to create the 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
}
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();
}
}