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
}
Related
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.
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().
I would like to know if there's any condition to check if values from cells were edited or not .
I can do it in other way ( with If value == value ) bla bla..but I need to write a lot to compare the values ..and I need another way .
Thanks
If you are working with a custom object/List bind with an object datasource. You can define a base class,inherit it & mark your objects dirty like
public class ObjectBase
{
private bool _isDirty;
private bool _isNew = true;
protected void MarkDirty()
{
_isDirty = true;
}
protected void MarkAsNew()
{
_isNew = true;
}
public bool IsDirty
{
get { return _isDirty; }
}
}
Then inside your object class you will inherit this base class like
public class myClass:ObjectBase
{
public int Id {get; set;}
public string Name {get; set;}
}
Whenver you update your object, mark them as dirty in presentation layer, then you check for them being dirty in business layer like, if yes update else return 'no changes to update'
public static bool UpdateItem(Employee myEmployee)
{
if(myEmployee.IsDirty)
{
return myEmployeeDB.UpdateItem(myEmployee);
}
}
DataGridViewCell does not track changes. If you need to know that Value has changed for one cell only, use CellValueChanged event and do your stuff there. If you need to know which cells have been modified, so you can enumerate them and do what is necessary, try this:
HashSet<DataGridViewCell> changedCells = new HashSet<DataGridViewCell>();
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if (!changedCells.Contains(dgv[e.ColumnIndex, e.RowIndex]))
{
changedCells.Add(dgv[e.ColumnIndex, e.RowIndex]);
}
}
Don't forget to hook to CellValueChanged event of DataGridView. When you need a list of changed cells, do this:
foreach (DataGridViewCell cell in changedCells)
{
// Your work here
}
This works on UI level only. You will need different approach if your data gets changed by code also.
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...
I check the postal code for about 60 countries. The user enters a postal code which is checked against the rules for that country. The US is an exception where I can auto-fill the value based on other street address information already entered. In some ways it's basically an auto-correction feature. The problem is the auto-filled value never re-displays if it matches the previous .Text value - the 'erroneous' zip code just entered by the user remains displayed!
For example, field contains "12345", user deletes the "5", my property setter puts the value back to "12345", the binding getter returns "12345" (and at this point my _TextChanged event fires) but WPF still displays the user edited text "1234"!
How do I force WPF to redisplay the value?
To simplify the example I have hard-coded the ZipCode value (in the app it is computed in a separate method):
public String ZipCode
{
get { return _address.ZipCode; }
set
{
_address.ZipCode = "12345";
RaisePropertyChanged("ZipCode");
}
}
Here is the XAML:
<TextBox Name="ZipBox"
Text="{Binding Path=ZipCode, UpdateSourceTrigger=PropertyChanged}"
TextChanged="ZipBox_TextChanged" />
WPF knows the field changed cause it always fires this event. But none of those methods force WPF to correctly display "12345".
private void ZipBox_TextChanged(object sender, TextChangedEventArgs e)
{
ZipBox.InvalidateVisual();
ZipBox.InvalidateMeasure();
ZipBox.UpdateLayout();
}
Edit: I also have created a custom static extension method .Refresh(this UIElement uiElement) which calls UIElement.Dispatcher.Invoke() and that also does not force the redraw.
Min Zhu on the Microsoft forum came up with a solution. Here is the code:
bool flagTextChanged = true;
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox txt = sender as TextBox;
if (flagTextChanged && txt != null)
{
flagTextChanged = false;
Binding b = BindingOperations.GetBinding(txt, TextBox.TextProperty);
if (b != null)
{
txt.ClearValue(TextBox.TextProperty);
BindingOperations.SetBinding(txt, TextBox.TextProperty, b);
}
flagTextChanged = true;
}
}
Do you mean this occurs after the user edits the text manually and then leaves the box? If so, the issue may be that the binding system doesn't want to override what the user is typing and there is an implicit assumption that the value being set by the text box Text will match the value if you call get on the property, since no exceptions are being thrown during the set. This is one of the down-sides to 'magic properties' where the set value is mutated rather than validated, as there are many consumers of properties that assume the following is true if the setter doesn't throw an exception:
Instance.X = value;
Debug.Assert(Instance.X == value);
One idea that avoids this problem would be to set a flag that prohibits editing of the ZipCode field if it has been automatically determined (by binding IsReadOnly, perhaps.)
I believe if you put a custom IValueConverter that does nothing but return the passed value, then the TextBox will update correctly.
Otherwise, you should be able to do:
ZipBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
This is an old question, but it's never been properly answered, so here's my suggestion.
You need to implement INotifyPropertyChanged.
Depending if it's a ViewModel or code-behind of a Window (this is a Window example):
public class MyWindow: Window, INotifyPropertyChanged
{
...
}
Then implement a version of INotifyPropertyChanged within that class (there are many out there, but this is the one I use):
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
And then simply use it with a property:
private string _text;
public string Text
{
get { return _text; }
set { SetField(ref _text, value, "Text"); }
}
I'm not particularly familiar with WPF but I do a lot of web programming. You could try firing a post to reload the entire page (or just the textbox with ajax) and that should allow you to set the new value.