I'm programming in c#(WPF). I have some Boolean variables in my class like isConnected or isBusy. I want to define a event and event handler for them to when my boolean variables are changed, I run a method.
I searched and find some things but I can't understand them.
could you help me to write it?
Update 1:
finally I write it, but I get StackOverFlowExeception which may be caused by recursion.
what is wrong?
public event EventHandler IsConnectedChanged;
public bool IsConnected
{
get { return IsConnected; }
set
{
IsConnected = value;
CheckAndCallHandlers();
}
}
private void CheckAndCallHandlers()
{
EventHandler handler = IsConnectedChanged;
if (IsConnected)
handler(this, EventArgs.Empty);
}
Wrap the variable in Properties, and then in the setter for the properties you can call a method that checks to see if both are true. When that condition is met, you can then do the extra work:
public class SomeClass
{
private bool _isConnected;
private bool _isBusy;
public event EventHandler SomeCustomEvent;
public bool IsConnected
{
get { return _isConnected; }
set
{
_isConnected = value;
CheckAndCallHandlers();
}
}
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
CheckAndCallHandlers();
}
}
private void CheckAndCallHandlers()
{
var handler = SomeCustomEvent;
if(IsConnected && IsBusy && handler != null)
handler(this, EventArgs.Empty);
}
}
make it a property
bool _isConnected;
bool isConnected
{
get { return _isConnected; }
set {
if (value != _isConnected) //it's changing!
{
doSomething();
}
_isConnected = value; //Could do this inside the if but I prefer it outside because some types care about assignment even with the same value.
}
}
Related
I have an hierarchy of classes. The pertinent bits are included below. I was expecting that when FilterCritetionInteger.CriterionValue is changed, via WPF binding, that I would get a notification in my FilterCriterionCollection. But, while the FilterCriterionInteger.CriterionValue setter is called, the FilterCriterionCollection.Criterion_PropertyChanged method is never called.
Apparently I am misunderstanding something, or have something connected incorrectly. I'm just not finding it. Why is my notification not happening? (Please let me know if there is more context needed, anywhere.)
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler pcHandler = this.PropertyChanged;
if (pcHandler != null)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
pcHandler(this, args);
}
}
}
FilterCriterionBase
public abstract class FilterCriterionBase : ViewModelBase
{
public abstract bool IsValid { get; }
}
FilterCriterionInteger
public class FilterCriterionInteger : FilterCriterionBase
{
private string _criterionValue = "0";
public string CriterionValue
{
get { return _criterionValue; }
set
{
_criterionValue = value;
OnPropertyChanged("IsValid");
}
}
public int? MaxValue { get; private set; } = null;
public override bool IsValid
{
get
{
if (int.TryParse(_criterionValue, out int i))
{
if (MaxValue.HasValue && (i > MaxValue)) return false;
}
else
{
return false;
}
return true;
}
}
}
FilterCriterionCollection
public class FilterCriteriaCollection : ViewModelBase
{
public FilterCriteriaCollection()
{
Criteria.CollectionChanged += Criteria_CollectionChanged;
}
~FilterCriteriaCollection()
{
Criteria.CollectionChanged -= Criteria_CollectionChanged;
}
public ObservableCollection<FilterCriterionBase> Criteria { get; private set; } = new ObservableCollection<FilterCriterionBase>();
private void Criteria_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (FilterCriterionBase fb in e.NewItems)
{
fb.PropertyChanged += Criterion_PropertyChanged;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (FilterCriterionBase fb in e.NewItems)
{
fb.PropertyChanged -= Criterion_PropertyChanged;
}
break;
}
}
private bool _isValid;
public bool IsValid
{
get { return _isValid; }
private set { _isValid = value; OnPropertyChanged("IsValid"); }
}
private void Criterion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
using (FilterCriterionBase criterion = sender as FilterCriterionBase)
{
switch (e.PropertyName)
{
case "IsValid":
{
bool isvalid = true;
foreach(FilterCriterionBase fcb in Criteria)
{
isvalid = fcb.IsValid;
if (!isvalid) break;
}
IsValid = isvalid;
break;
}
}
}
}
}
So, as it turns out, I am a moron. The code, above, does need a little tweaking but it is, in general, sound. My primary issue was that I had some leftover code, in my testing sandbox, from testing some JSON serialization/deserialization. I realized, after much head banging, that I was sending deserialized objects that had lost their event notification context and, therefore, were no longer in the loop.
Would it be proper protocol to just delete this post, since it doesn't really require an answer beyond double checking your inputs...?
I have a class STimer that has a List in it. The serviceDetail is monitored on a timer very often, and rarely changes, but when it does change I want to get the fact that the data changed without looping through the list due to processing power. Maybe this is a duplicate question that I just don't know how to search for it, but I have been trying. Here is a code Sample:
class STimers
{
public class ServiceDetail
{
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
<-- Update STimers._dataChanged to true -->
}
}
}
}
public List<ServiceDetail> _serviceMonitors = new List<ServiceDetail>();
public bool _dataChanged = false;
}
I could do a .Find on the list to return all of the _serviceMonitors._runningStateChanged=true, but that seems like a lot of work parsing the List every time the timer fires, when likely only 1 out of 1,000 loops will actually have a change.
Is this even possible, or do I need to move the check for changes out of the class?
You could get this by adding an event to your ServiceDetail class
public class ServiceDetail
{
public event EventHandler<ListChangedEventArgs> ListChanged;
private int _serviceKey;
private bool _isRunning = true;
private bool _runningStateChanged = false;
private void OnListChanged(ListChangedEventArgs e){
if (ListChanged != null) ListChanged(this, e);
}
public bool isRunning
{
get { return _isRunning; }
set
{
//Check to see if the data is the same, if so, don't change, if not, change and flag as changed
if(_isRunning = value) { return; }
else
{
_isRunning = value;
_runningStateChanged = true;
OnListChanged(new ListChangedEventArgs(this));
<-- Update STimers._dataChanged to true -->
}
}
}
}
And define your ListChangedEventArgs class like this
public class ListChangedEventArgs:EventArgs
{
public ServiceDetail serviceDetail { get; set; }
public ListChangedEventArgs(ServiceDetail s)
{
serviceDetail = s;
}
}
And then register to the event for each servicedetail added to the list
s.ListChanged += (sender, args) => YourFunction();
Hope it helps
I'm trying to fire an event when a bool has actually changed value. I read that using a custom set and get method is one of the ways to do this. But I keep getting NullReferenceException: Object reference not set to an instance of an object
Public class Ball : MonoBehaviour {
public delegate void ballEvents();
public static event ballEvents OnBallStop;
private bool _previousValue = true;
private bool _ballStopped = true;
public bool BallHasStopped {
get {return _ballStopped;}
set {
_ballStopped = value;
if (_ballStopped != _previousValue){
_previousValue = _ballStopped;
if(value == true) {
print("stop");
OnBallStop(); // this causes the error
} else {
print("moving");
}
}
}
}
When an event has no handlers it is null, and so cannot be invoked. You can check for null and not invoke it unless there are handlers.
Lets say I have sales price, down payment amount, down payment percent and loan amount. When any of these properties are changed by the user the others need to be updated to reflect the new values. How do you deal with this type of infinite property change events?
When flow control is necessary across multiple attributes, I'll institute a flow control variable - a boolean - and in each property that's being changed, I'll add a test to see if I'm under flow control or not.
private bool controlledChange = false;
public property int MyVal1
{
set
{
_myVal1 = value;
if(!controlledChange)
{
controlledChange = true;
MyVal2 -= 1;
controlledChange = false;
}
}
}
public property int MyVal2
{
set
{
_myVal2 = value;
if(!controlledChange)
{
controlledChange = true;
MyVal1 += 1;
controlledChange = false;
}
}
}
This way whatever property is changed can initiate changes across the other properties, but when they get changed, they will no NOT to initiate their own set of changes in turn.
You should also look to make as many of those properties read only as possible, if they can have calculated results, so that you limit how the object can be changed.
THe easiest way is to only raise a change event if the property has really changed:
public decimal SalePrice {
get{
return salePrice;
}
set {
if (salePrice != value) {
salePrice = value; // putting as first statement prevents the setter
// to be entered again ...
RaiseSalePriceChange();
// Set other properties
}
}
}
I'm not sure I completely understand, since I don't know what you mean by 'infinite'
This may be a good use case for actually backing your properties with fields. That way, you can trigger events on Property sets, but internally set the fields one at a time without triggering N events.
class MyClass
{
private string m_Name;
private int m_SomeValue;
public string Name
{
get { return m_Name; }
set
{
if (value != m_Name)
{
m_Name = value;
m_SomeValue++;
// Raise Event
}
}
}
public int SomeValue
{
get { return m_SomeValue; }
set
{
if (m_SomeValue != value)
{
m_SomeValue = value;
// Raise Event
}
}
}
If INotifyPropertyChanged is really needed to notify external objects, so I would just centralise everything. Like this:
private double salesPrice;
private double downPaymentAmount;
private double downPaymentPercent;
private double loanAmount;
public double SalesPrice
{
get
{
return salesPrice;
}
set
{
if (salesPrice != value)
{
salesPrice = value;
// maybe you would rather use a RecalculateForSalePriceChanged() method
RecalculateDownPaymentAmount();
RecalculateDownPaymentPercent();
RecalculateLoanAmount();
propertiesChanged();
}
}
}
public double DownPaymentAmount
{
get
{
return downPaymentAmount;
}
set
{
if (downPaymentAmount != value)
{
downPaymentAmount = value;
// see above
RecalculateDownPaymentPercent();
RecalculateLoanAmount();
RecalculateSalesPrice();
propertiesChanged();
}
}
}
public double DownPaymentPercent
{
get
{
return downPaymentPercent;
}
set
{
if (downPaymentPercent != value)
{
downPaymentPercent = value;
// see above
RecalculateDownPaymentAmount();
RecalculateLoanAmount();
RecalculateSalesPrice();
propertiesChanged();
}
}
}
public double LoanAmount
{
get
{
return loanAmount;
}
set
{
if (loanAmount != value)
{
loanAmount = value;
// see above
RecalculateDownPaymentAmount();
RecalculateDownPaymentPercent();
RecalculateSalesPrice();
propertiesChanged();
}
}
}
private void propertiesChanged()
{
RaisePropertyChanged("SalesPrice", "DownPaymentAmount", "DownPaymentPercent", "LoanAmount");
}
Maybe you can concentrate the recalculations in less methods or even a single one, but I do not know how you calculate them. But certainly you have to keep a specific order when recalculating the values.
Since they only operate on fields and do not change the properties, there will be no PropertyChanged-feedback-loop.
Hope this helps and I did not misunderstood what you wanted.
What the OP wanted was something like following
class A : INotifyPropertyChanged
{
private int field1;
public int Property1
{
get { return field1; }
set
{
field1 = value;
field2++;
RaisePropertyChanged("Property1");
RaisePropertyChanged("Property2");
}
}
private int field2;
public int Property2
{
get { return field2; }
set
{
field2 = value;
field1++;
RaisePropertyChanged("Property1");
RaisePropertyChanged("Property2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What he might be doing was handling other properties in the setter of each property he mentioned thus leading to cyclic invocation of setters.
Vijay
I created a property
public int PK_ButtonNo
{
get { return PK_ButtonNo; }
set { PK_ButtonNo = value; }
}
Now I want to add events to this property for value changing and changed.
I wrote two events. Here I want both the events to contain changing value as well as changed value.
i.e
When user implements the event. He must have e.OldValue, e.NewValue
public event EventHandler ButtonNumberChanging;
public event EventHandler ButtonNumberChanged;
public int PK_ButtonNo
{
get { return PK_ButtonNo; }
private set
{
if (PK_ButtonNo == value)
return;
if (ButtonNumberChanging != null)
this.ButtonNumberChanging(this,null);
PK_ButtonNo = value;
if (ButtonNumberChanged != null)
this.ButtonNumberChanged(this,null);
}
}
How will I get the changing value and changed value when I implement this event.
Add the following class to your project:
public class ValueChangingEventArgs : EventArgs
{
public int OldValue{get;private set;}
public int NewValue{get;private set;}
public bool Cancel{get;set;}
public ValueChangingEventArgs(int OldValue, int NewValue)
{
this.OldValue = OldValue;
this.NewValue = NewValue;
this.Cancel = false;
}
}
Now, in your class add the changing event declaration:
public EventHandler<ValueChangingEventArgs> ButtonNumberChanging;
Add the following member (to prevent stackoverflow exception):
private int m_pkButtonNo;
and the property:
public int PK_ButtonNo
{
get{ return this.m_pkButtonNo; }
private set
{
if (ButtonNumberChanging != null)
ValueChangingEventArgs vcea = new ValueChangingEventArgs(PK_ButtonNo, value);
this.ButtonNumberChanging(this, vcea);
if (!vcea.Cancel)
{
this.m_pkButtonNo = value;
if (ButtonNumberChanged != null)
this.ButtonNumberChanged(this,EventArgs.Empty);
}
}
}
The "Cancel" property will allow the user to cancel the changing operation, this is a standard in a x-ing events, such as "FormClosing", "Validating", etc...