How to check propertychanged only once in each Timer tick? - c#

I use 2 Timers, 1 Check all my Citect tags for changed values each second. The other one is a KeepAlive Timer for a TCP Connection.
This is how i call the Timers:
Timer timer = new Timer(ctApiCtrl.CheckUpdateAllInputTags, "updatetags", 1000, 1000);
Timer timer2 = new Timer(mepCtrl.KeepAlive, "test", 3000, 3000);
Now due the nature of how the existing systems works together is with writing and reading PLC/DSK tags. So if my service wants to detect these changes it needs to periodically "poll" or check these values.
This is the class where those tags comes from:
public class TagWithValue : INotifyPropertyChanged
{
public string TagName { get; set; }
private string tagValue;
public TagCategory TagCategory { get; set; }
public string TagValue
{
get { return tagValue; }
set
{
if(tagValue != value)
{
tagValue = value;
RaisePropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
So when the property TagValue changes it detects it and fire x.
public void CheckUpdateAllInputTags(object objectInfo)
{
VDGSenseController.Authenticate();
if (listOfTags.Video == null || listOfTags.Audio == null)
{
Logger.Info("Empty Tag list , reinitliazing all tags");
InitiliazeAllTags();
}
foreach (var tag in Tags)
{
try
{
var value = TagRead(tag.TagName); //read Tag value
tag.TagValue = value;
tag.PropertyChanged += d_PropertyChanged;
}
catch (Exception ex)
{
Logger.Warn(ex,"Importing tag unsuccesfull");
continue;
}
}
Tags = Tags.Where(t => !string.IsNullOrWhiteSpace(t.TagValue)).Distinct().ToList();
}
Here lies the problem of this snippet.
private void d_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
//trigger wanneer prop value is changed
var tagchanged = (CtModel.TagWithValue)sender;
Logger.Info($"Tag: {tagchanged.TagName} with value: {tagchanged.TagValue} has changed.");
}
The problem i have with this code is that it fires multiple times the propertychanged method in the same timer tick.
Note the logging that contains Tag: DK_OM_2A
How can i change this so it only fires once each timer tick?

I fixed it by moving the logic from the propertychanged in a different method and in the new code it goes like this:
private void d_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
//trigger wanneer prop value is changed
var tagchanged = (CtModel.TagWithValue)sender;
tagsWithChangedValue.Add(tagchanged);
tagsWithChangedValue = tagsWithChangedValue.Distinct().ToList();
}
#region UpdateTagsPropertyChanged
public void UpdateTagsPropertyChanged(List<CtModel.TagWithValue> listofchangedtags)
{
if(listofchangedtags.Count == 0)
{
return; // if list is empty, go back
}
foreach(var tagchanged in listofchangedtags)
{
logger.Info($"Tag: {tagchanged.TagName} with value: {tagchanged.TagValue} has changed.");
switch (tagchanged.TagName)
{
default:
break;
}
}
tagsWithChangedValue.Clear(); //clears list for the next cycle
}
#endregion

Related

C# Get an object per index on an infinite list of the same object

I have an object which contain a list of this object.
when the user click on a button I want to add a new object
to one of the object in the list per an index. for example if the index is equal to 2
I want to add an object to the object.object.object(new object()).
how am I trying to get the correct object inside the inifinte object.
any better method then this one?
private void addToCommand(int indexTreeView, ClassForTest.CommadStructure CS)
{
if (_indexTreeView == 0)
{
_OCommandStructure.Add(CS);
}
else if (_indexTreeView==1)
{
_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure.Add(CS);
}
else if (_indexTreeView == 2)
{
_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure[_OCommandStructure[_OCommandStructure.Count - 1].SubCommandStructure.Count-1].SubCommandStructure.Add(CS);
}
}
public class CommadStructure : INotifyPropertyChanged
{
private string _Controller;;
private ObservableCollection<CommadStructure> _SubCommandStructure;
public string NameOfCommand
{
get => _NameOfCommand;
set
{
_NameOfCommand = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<CommadStructure> SubCommandStructure
{
get => _SubCommandStructure;
set
{
_SubCommandStructure = value;
// NotifyPropertyChanged();
}
}
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> childNodes = new ObservableCollection<object>();
foreach (var group in this.SubCommandStructure)
childNodes.Add(group);
return childNodes;
}
}
public CommadStructure()
{
NameOfCommand = "";
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

How to stop INotify from updating twice?

I am updating a Datagrid and when a user inputs a number that already exists I want notify the user they the number already exists and then clear the value from the datagrid.
I know why this is happening, but I can't figure out how to stop this or how to make a work around.
This is very simplified code: Using EF code first with MVVM model.
public partial class StaffMasterData
{
public System.Guid Id { get; set; } // ID (Primary key)
public int? StaffNo { get; set; } // StaffNo
public StaffMasterData()
{
InitializePartial();
}
partial void InitializePartial();
}
Entity extension class for StaffMasterData :
public partial class StaffMasterData : INotifyPropertyChanged
{
partial void InitializePartial()
{
Id = Guid.NewGuid();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
And the method to save the data:
public void SaveMasterData(StaffMasterData nwRowData)
{
using (var db = CreateDbContext())
{
//MasterDataBinding is the observableCollection
//the datagrid is being bound to.
var staffNoExists = MasterDataBinding.Any(p => p.StaffNo == nwRowData.StaffNo);
if (!staffNoExists)
{
db.StaffMasterDatas.AddOrUpdate(nwRowData);
db.SaveChanges();
}
else
{
Alerts.Error("Staff Number exists");
nwRowData.StaffNo = null;
}
}
}
And the assinging of the collection changed event:
public class ShiftManagerViewModel : INotifyPropertyChanged
{
private ObservableCollection<StaffMasterData> _mMasterDataBinding = new ObservableCollection<StaffMasterData>();
public ObservableCollection<StaffMasterData> MasterDataBinding
{
get { return _mMasterDataBinding; }
set
{
if (value != _mMasterDataBinding)
{
_mMasterDataBinding = value;
OnPropertyChanged();
}
}
}
public ShiftManagerViewModel()
{
_mMasterDataBinding.CollectionChanged += collectionChanged_Event;
}
private void collectionChanged_Event(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged += propertyChanged_Event;
}
}
if (e.OldItems != null && e.OldItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.OldItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged -= propertyChanged_Event;
}
}
}
public void propertyChanged_Event(object sender, PropertyChangedEventArgs e)
{
if (sender is StaffMasterData)
{
SaveMasterData((StaffMasterData)sender);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
As it is probably very clear, when running through this line of code nwRowData.StaffNo = null; , it fires the event again as the collection has been modified which then in turn runs through the messageBox code and it pops up twice.
Honestly I have hit a brick wall with this and any point in the right direction would be appreciated.
You could use a flag that determines whether to actually call the SaveMasterData method. Set this flag to false just before you set the StaffNo property to null and then set it back to true immediately afterwards:
private bool _handle = true;
public void SaveMasterData(StaffMasterData nwRowData)
{
using (var db = CreateDbContext())
{
//MasterDataBinding is the observableCollection
//the datagrid is being bound to.
var staffNoExists = MasterDataBinding.Any(p => p.StaffNo == nwRowData.StaffNo);
if (!staffNoExists)
{
db.StaffMasterDatas.AddOrUpdate(nwRowData);
db.SaveChanges();
}
else
{
Alerts.Error("Staff Number exists");
_handle = false;
nwRowData.StaffNo = null;
_handle = true;
}
}
}
public void propertyChanged_Event(object sender, PropertyChangedEventArgs e)
{
if (!_handle && sender is StaffMasterData)
{
SaveMasterData((StaffMasterData)sender);
}
}

WPF Adding item to listview update View only at the end of Function

I'm currently creating a WPF form which retrieve some manual information and then perform some activities through PowerShell.
Activity is a class that implement INotifyPropertyChanged in order to reflect property changes (for example when an activity fails, its status changes from "running" to "Error").
I've created a listView (datagrid) which itemsSource is an ObservableCollection named Sequence. Sequence is declared in mainWindows class.
Activity Class is that :
public class Activity : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int step;
public int Step
{
get { return this.step; }
set
{
if (this.step != value)
{
this.step = value;
this.NotifyPropertyChanged("Step");
}
}
}
private String group;
public String Group
{
get { return this.group; }
set
{
if (this.group != value)
{
this.group = value;
this.NotifyPropertyChanged("Group");
}
}
}
private String activityName;
public String ActivityName
{
get { return this.activityName; }
set
{
if (this.activityName != value)
{
this.activityName = value;
this.NotifyPropertyChanged("ActivityName");
}
}
}
private Model.Status status;
public Model.Status Status
{
get { return this.status; }
set
{
if (this.status != value)
{
this.status = value;
this.NotifyPropertyChanged("Status");
}
}
}
private String cmdlet;
public String Cmdlet
{
get { return this.cmdlet; }
set
{
if (this.cmdlet != value)
{
this.cmdlet = value;
this.NotifyPropertyChanged("Cmdlet");
}
}
}
private Dictionary<String, Object> parameters;
public Dictionary<String, Object> Parameters
{
get { return this.parameters; }
set { this.parameters = value; }
}
public Activity(int _Step, String _ActivityName, String _Group , Model.Status _Status, String _Cmdlet, Dictionary<String, Object> _Parameters)
{
this.Step = _Step;
this.Group = _Group;
this.ActivityName = _ActivityName;
this.Status = _Status;
this.Cmdlet = _Cmdlet;
this.Parameters = _Parameters;
}
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
internal void Run(ref Runspace _rs)
{
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = _rs;
//ps.AddCommand(this.cmdlet).AddParameter("Site", this._Selection.SelectedSite);
ps.AddCommand(this.cmdlet).AddParameters(this.Parameters);
Collection<PSObject> results = ps.Invoke();
if (results.Count > 0 && ps.HadErrors == false)
{
foreach (PSObject item in results)
{
this.status = (Model.Status)Enum.Parse(typeof(Model.Status), item.ToString(), true);
}
}
else
{
this.Status = Model.Status.Error;
}
}
}
}
Deployment start when I click on the Last "Next" button.
A function Deploy is started :
private void DeploySite()
{
// Build list of activities
//
int i = 0;
Dictionary<String, Object> _params = new Dictionary<string,object>();
_params.Add("Site", this._Selection.SelectedSite);
this.Sequence.Add(new Model.Activity(i++, "Update System Discovery Method", "Configure Administration Node", Model.Status.NoStatus, "Update-SystemDiscoveryMethod", _params));
this.Sequence.Add(new Model.Activity(i++, "Update User Discovery Method", "Configure Administration Node", Model.Status.NoStatus, "Update-SystemDiscoveryMethod", _params));
// Start Execution of activities
//
foreach (Model.Activity activity in this.Sequence)
{
activity.Status = Model.Status.Running;
activity.Run(ref rs);
}
}
My problem is that the view is updated only when every activities are finished, in other words when the DeploySite() function ends.
I would like to view progression of each activities when there are executed.
Any help will be greatly appreciated.
Since you are continuously doing work in the UI thread in DeploySite(), there is no chance for the View to do anything. You should do the work in a background thread, and only do the UI updates themselves in the UI thread.
Run your activities in a new thread an dispatch results/updates to the UI thread. Current implementation is blocking the UI, as mentioned by #Daniel Rose.
Here, I hope this meta code helps you solve it.
private void DeploySite()
{
// Fire up a new Task
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
int i1 = i;
// Dispatch
System.Windows.Application.Current.Dispatcher.BeginInvoke(
new Action(() => Sequence.Add(i1)), null);
Thread.Sleep(100);
}
});
}

bind listbox to index C#

I dont know if it possible but what I want is something like that
In WinForm listbox1 has a list of lines(copied from file)
In another Thread and class I run on a List that contains the same lines each line I parse and DoSomething
once I finish with that line I want the index in the listbox to change
from my basic and limited understanding my approach was with an Event to fire in form and than maybe using Invoke (for not to cross thread )
Is there is a way to somehow bind to index of the listbox somehow with my for/foreach loop ?
class form
{
listBoxScript.SetSelected(ScriptCounter, true);<--bind the ScriptCounter?
}
in another Class
class RunScript
{
//..
public void RunScriptList()
{
ScriptCounter = 0 ;
foreach ( var cell in ScriptList)
{
ScriptCounter ++;
//DoSomething
}
}
}
Make sure you implement INotifyPropertyChanged in RunScript class. Here's a complete sample:
class RunScript : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int scriptCounter;
private ISynchronizeInvoke invoker;
public RunScript(ISynchronizeInvoke invoker)
{
if (invoker == null) throw new ArgumentNullException("invoker");
this.invoker = invoker;
}
public async void RunScriptList()
{
ScriptCounter = 0;
foreach (var cell in Enumerable.Range(1, 15))
{
ScriptCounter++;
await Task.Delay(1000);
//DoSomething
}
}
public int ScriptCounter
{
get { return scriptCounter; }
set
{
scriptCounter = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
Action action = () => handler(this, new PropertyChangedEventArgs(propertyName));
invoker.Invoke(action, null);
}
}
}
private RunScript rs;
public Form1()
{
InitializeComponent();
rs = new RunScript(this)
var binding = new Binding("SelectedIndex", rs, "ScriptCounter", false, DataSourceUpdateMode.OnPropertyChanged);
listBox1.DataBindings.Add(binding);
}
private void button1_Click(object sender, EventArgs e)
{
rs.RunScriptList();
}
Note I have used async/await in RunScriptList, If you do it in another thread you need to fire PropertyChanged event in main thread to avoid cross thread exception.

C# - How to make use of "INotifyPropertyChanged" to update calculated fields in a collection class?

I have an Item class with two properties "quantity" and "price" and implements INotifyPropertyChanged
public class Item:INotifyPropertyChanged
{
private event PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { _propertyChanged += value; }
remove { _propertyChanged -= value; }
}
void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (_propertyChanged != null)
{
_propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int QuantityOnHand
{
get
{
return this._quantityOnHand;
}
set
{
if (value > 0)
{
this._quantityOnHand = value;
NotifyPropertyChanged();
}
else
{
throw new System.ArgumentException("Quantity must be a positive value!");
}
}
}
.....
}
And I have a collection class of items named "Inventory" with a property of TotalRetailPrice:
public class Inventory {
private List<Item> _inventoryList = new LinkedList<Item>();
public decimal TotalRetailPrice
{
get
{
decimal totalRetailPrice = 0M;
foreach (var item in _inventoryList)
{
totalRetailPrice += item.QuantityOnHand * item.RetailPrice;
}
return totalRetailPrice;
}
}
I am trying to find out a way to automatically update this property TotalRetailPrice, whenever I change either the quantity or the price of any item(s) in the list. How can I do that? Right now with my code, every time I tried to get this totalRetailPrice property, I will have to go through the list and recalculate it.
thanks!
Since the interface INotifyPropertyChanged exposes an event called PropertyChanged you can just subscribe to that in the 'inventory' class.
You will also want to listen for changed events in the list since you will need to know when items are added/removed so you can add/remove event handlers as necessary. I'd suggest using ObservableCollection<T> as this supports some 'collection changed' events. Is there any reason you are using LinkedList<T>?
e.g.
public class Inventory
{
private ObservableCollection<Item> _inventoryList = new ObservableCollection<Item>();
public decimal _total;
// You probably want INPC for the property here too so it can update any UI elements bound to it
public decimal Total { get { return _total; } set { _total = value; } }
// Constructor
public Inventory()
{
WireUpCollection();
}
private void WireUpCollection()
{
// Listen for change events
_inventoryList.CollectionChanged += CollectionChanged;
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Check what was added - I'll leave this to you, the e.NewItems are the items that
// have been added, e.OldItems are those that have been removed
// Here's a contrived example for when an item is added.
// Don't forget to also remove event handlers using inpc.PropertyChanged -= Collection_PropertyChanged;
var inpc = e.NewItems[0] as INotifyPropertyChanged;
if(inpc != null)
inpc.PropertyChanged += Collection_PropertyChanged;
}
private void Collection_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RecalculateTotal();
}
private void RecalculateTotal()
{
// Your original code here which should feed the backing field
}
}
Check out the MSDN docs here:
http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
For info on ObservableCollection<T>. The events section is what you are after. Also note you can use anonymous functions to handle the events if you prefer the syntax or want to capture variables in a certain scope etc. It helps to understand them fully (not sure what's available for Java as I've not really touched it save a couple of Android mess-about projects) so it might be worth reading up as there are a small caveats to be aware of when capturing, but that's another story!
e.g.
_inventoryList.CollectionChanged += (o,e) =>
{
// Anonymous method body here
// o = the first param (object sender), e = args (NotifyCollectionChangedEventArgs e)
};
As it was told in another answer in order to "observe" collection items you have to use ObservableCollection which has a special event CollectionChanged. It is raised when the list is changed somehow (item added, removed, replaced). However, that event won't be raised (obviously) when some property of the existing item is changed, for example, inventory.InventoryList[0].QuantityOnHand = 8;.
So, to get the solution worked you need to observe both the collection changes (CollectionChanged event) and each collection item changes (PropertyChanged event). Though, implementing that logic correctly is not so easy. The Charlen answer is just a sketch of the solution but not the full solution.
It might be easier to use DependenciesTracking lib which solves the issue. See the example below:
public class Item : INotifyPropertyChanged
{
private int _quantityOnHand;
private decimal _retailPrice;
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public int QuantityOnHand
{
get => this._quantityOnHand;
set
{
if (_quantityOnHand == value) return;
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "QuantityOnHand must be a positive value");
_quantityOnHand = value;
OnPropertyChanged();
}
}
public decimal RetailPrice
{
get => _retailPrice;
set
{
if (_retailPrice == value) return;
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "RetailPrice must be a positive value");
_retailPrice = value;
OnPropertyChanged();
}
}
}
public class Inventory : INotifyPropertyChanged
{
private static readonly IDependenciesMap<Inventory> _dependenciesMap =
new DependenciesMap<Inventory>()
.AddDependency(i => i.TotalRetailPrice,
i => i.InventoryList?.Sum(item => item.QuantityOnHand * item.RetailPrice) ?? 0.0m,
i => i.InventoryList.EachElement().QuantityOnHand, i => i.InventoryList.EachElement().RetailPrice);
private ObservableCollection<Item>? _inventoryList;
private decimal _totalRetailPrice;
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public ObservableCollection<Item>? InventoryList
{
get => _inventoryList;
set
{
if (_inventoryList == value) return;
_inventoryList = value;
OnPropertyChanged();
}
}
public decimal TotalRetailPrice
{
get => _totalRetailPrice;
private set
{
if (value == _totalRetailPrice) return;
_totalRetailPrice = value;
OnPropertyChanged();
}
}
public Inventory()
{
_dependenciesMap.StartTracking(this);
}
}
public class Tests_SO_20767981
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test_SO_20767981()
{
var inventory = new Inventory();
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(0.0m));
inventory.InventoryList = new ObservableCollection<Item>();
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(0.0m));
inventory.InventoryList.Add(new Item { QuantityOnHand = 3, RetailPrice = 5 });
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(15));
inventory.InventoryList.Add(new Item { QuantityOnHand = 1, RetailPrice = 7 });
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(22));
inventory.InventoryList[0].QuantityOnHand = 8;
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(47));
inventory.InventoryList[0].RetailPrice = 12;
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(103));
inventory.InventoryList.RemoveAt(1);
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(96));
var newInventoryList = new ObservableCollection<Item>
{
new Item() { QuantityOnHand = 10, RetailPrice = 0.5m},
new Item() { QuantityOnHand = 6, RetailPrice = 1.5m}
};
inventory.InventoryList = newInventoryList;
Assert.That(inventory.TotalRetailPrice, Is.EqualTo(14m));
}
}

Categories

Resources