6

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.

Community
  • 1
  • 1
LBPLC
  • 1,570
  • 3
  • 27
  • 51
  • The question you linked has a few pretty good suggestions already, the [INotifyPropertyChanged](https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx) in particular. What specifically don't you understand? If that's too tricky you could just update the text field in the property setter of your user control, though this isn't as nice. – DGibbs Jan 25 '16 at 12:14
  • 1
    Please specify in tag are you using `winforms` or `wpf`. Mentioned question is wpf (means won't work in winforms) or what *exactly* is the problem? – Sinatr Jan 25 '16 at 12:15
  • its in winforms, sorry. Which would make sense as to why it isn't working! – LBPLC Jan 25 '16 at 12:16
  • 1
    You need to override the Value property. – string.Empty Jan 25 '16 at 12:18
  • 1
    If you can modify the AGauge, I'd recommend having an event fire every time the property is updated/ – mike Jan 25 '16 at 12:20
  • There is [bindings](http://stackoverflow.com/q/2251075/1997232) in winforms, but a simple event may do for you: define event in custom control, subscribe to it in the form and update corresponding text field. – Sinatr Jan 25 '16 at 12:20

4 Answers4

4

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.

mike
  • 455
  • 2
  • 6
  • Thanks, I have used the "the way you were doing it" approach, but completely take on board that I need to get to grips with the other approach. Luckily, the current method will suit for this project, but I will mess about with the other side of your answer and see what I can come up with. Thanks a lot – LBPLC Jan 25 '16 at 16:19
  • One question, in your first part of the answer, what would the `???` represent in `this.Value = ???;` – LBPLC Jan 25 '16 at 16:20
  • It would be whatever you are change the AGauge.Value to.. I'll update it to hopefully make more sense – mike Jan 25 '16 at 16:52
  • @SilverShotBee, please see updated answer for clarity. – mike Jan 25 '16 at 17:00
1

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

Jcl
  • 27,696
  • 5
  • 61
  • 92
0

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.
    }
}
Seth Denburg
  • 144
  • 1
  • 11
  • It might be cumbersome to have to add references to the textbox on the form to the AGauge control. This is a valid method, but adds overhead to the AGauge and reduces it's portability to other applications. – mike Jan 25 '16 at 12:30
  • I get the error `'AGuage.Value.get' must declare a body because it is not marked abstract, extern, or partial` when attempting this method – LBPLC Jan 25 '16 at 12:41
  • you would also have to pass a reference to the form so that AGauge can access the textbox – mike Jan 25 '16 at 12:46
  • I added the body to the property's get method. – Seth Denburg Jan 25 '16 at 12:46
  • @mike I agree you wouldn't want to reference the form in this class. It's just a way to create a hook when the value is set and puts the answer from the link in the question into the format SilverShotBee would need. – Seth Denburg Jan 25 '16 at 12:50
  • totally agree, this method would work, just might not be the cleanest way to handle it based on object oriented principles – mike Jan 25 '16 at 13:02
  • @SethDenburg I'm not entirely sure I'm understanding how to use this. I've got it in my code, all compiles OK, but how do I actually get it to look at the variable I want monitored? – LBPLC Jan 25 '16 at 13:19
  • @SilverShotBee, again, you have to pass a reference of either the textbox or the form to the AGauge class. Then you would have to change the value of the textbox in the ValueChanged method if you use this implementation. However, I would not recommend this if this AGauge class is used anywhere else other than what you are trying to use if for here as it would violate the generics principle for OOP. – mike Jan 25 '16 at 13:23
  • @SilverShotBee, my answer will allow you to handle this without having to mess with setter for Value. The problem you have now is you are trying to get Form1 by a static reference. You still haven't passed a reference for Form1 to the AGauge control. – mike Jan 25 '16 at 15:51
  • @mike You could expand upon this to have ValueChanged be a delegate method or execute a list of delegate methods to avoid the need to reference the form. – Seth Denburg Jan 25 '16 at 17:11
  • @SethDenburg that is basically what I am trying to accomplish in the first part of my answer, by raising an event when the value is changed, causing all subscribers to be notified. – mike Jan 25 '16 at 17:13
0

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().

Enigmativity
  • 113,464
  • 11
  • 89
  • 172