Conversion of List to ObservableCollection Freezing the UI in WPF - c#

ObservableCollection<Transaction> transactions;
public ObservableCollection<Transaction> Transactions
{
get { return transactions; }
set
{
transactions = value;
RaisePropertyChanged("transactions");
}
}
private Property selectedProperty;
public Property SelectedProperty
{
get { return selectedProperty; }
set
{
selectedProperty = value;
RaisePropertyChanged("SelectedProperty");
Task.Run(async () => await GetTransactions());
}
}
private async Task GetTransactions()
{
try
{
IsProgressing = true;
if (SelectedProperty == null)
{
Transactions = null;
return;
}
var tranList = new List<Transaction>();
decimal balance = 0;
foreach (var tran in await context.Transactions
.Where(t => t.PropertyId == SelectedProperty.Id)
.OrderBy(t => t.Date).ToListAsync())
{
if (!tran.IsMisc)
{
balance = balance - tran.AmountPaid + tran.AmountDue;
}
if (tran.IsRentStart)
{
balance = 0;
}
tran.Balance = balance;
tranList.Add(tran);
}
Transactions = new ObservableCollection<Transaction>(
tranList.OrderByDescending(t => t.Date));
OutstandingAmount = Transactions.Select(t => t.Balance)
.FirstOrDefault() + SelectedProperty.OutstandingAmount;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
finally
{
IsProgressing = false;
}
}
On calling the GetTransactions() on SelectedProperty change of the dropdown, the UI freezes until the records are shown in the DataGrid.
I think conversion of List<Transaction> to ObservableCollection<Transaction> is the cause. I have tried many methods like Task.Run(), using Dispather.BeginInvoke but nothing is working.
Getting values from the database `Context.Transaction.Where()....ToListAsync()) does not freeze the UI, I have checked that. On removing the 'List' to 'ObservableCollection' conversion the UI is not freezing.

Related

Task async with neo4jclient is not adding relationships in loop for all nodes

I am calling 2 async methods in loop.
First one is to create node and second is to add relation.
its creating node all time but its not creating all relationships
Eg - if there are 4 degrees , its creating 3 degrees but its not creating relationship with all 4. even if JobPost and JobDegrees both nodes are present.
Why its not creating relationship?
DegreesList - here I am passing array of an object.
Here is the code -
private void Map_Job_DEGREES(GUID JobId, dynamic DegreesList)
{
try
{
foreach (var objdegree in DegreesList)
{
string degreename = Convert.ToString(objdegree.name);
degreename = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(degreename);
JobDegrees objnew1 = new JobDegrees();
objnew1.name = degreename;
Task.Run(async () => await repo.Insert_JobDegrees(objnew1));
Task.Run(async () => await repo.CreateRelationship_DEGREES_INCLUDED(JobId, degreename));
}
}
catch { }
}
public class JobPostingNeo4jRepository
{
public async Task Insert_JobDegrees(JobDegrees obj1)
{
try
{
await _graphClient.ConnectAsync();
await _graphClient.Cypher
.Merge("(obj1:JobDegrees { name: $name })")
.OnCreate()
.Set("obj1 = $obj1")
.WithParams(new
{
name = obj1.name,
obj1
})
.ExecuteWithoutResultsAsync();
}
catch (Exception ex)
{
throw ex;
}
}
public async Task CreateRelationship_DEGREES_INCLUDED(Guid JobId, string name)
{
try
{
await _graphClient.ConnectAsync();
await _graphClient.Cypher
.Match("(obj1:JobPost)", "(obj2:JobDegrees)")
.Where((JobPost obj1) => obj1.Id == JobId)
.AndWhere((JobDegrees obj2) => obj2.name == name)
.Merge("(obj1)-[:DEGREES_INCLUDED { createddate: $createddate }]->(obj2)")
.WithParams(new { createddate = DateTime.UtcNow })
.ExecuteWithoutResultsAsync();
}
catch (Exception ex)
{
throw ex;
}
}
}

Unintentionally the same data records in the UI list more than once

I have a somewhat unusual problem.
I wrote a damage meter for a game and have the problem, if I attack 2 opponents at the same time in the game, I am shown several times in the list.
To get the data from the game, I use PhotonParser, which reads out the network data.
(First of all, it's nothing illegal.)
public class AlbionPackageParser : PhotonParser
{
private readonly HealthUpdateEventHandler HealthUpdateEventHandler;
public AlbionPackageParser(TrackingController trackingController, MainWindowViewModel mainWindowViewModel)
{
HealthUpdateEventHandler = new HealthUpdateEventHandler(trackingController);
}
protected override void OnEvent(byte code, Dictionary<byte, object> parameters)
{
var eventCode = ParseEventCode(parameters);
if (eventCode == EventCodes.Unused)
{
return;
}
Task.Run(async () =>
{
switch (eventCode)
{
case EventCodes.HealthUpdate:
await HealthUpdateEventHandlerAsync(parameters).ConfigureAwait(false);
return;
}
});
}
private static EventCodes ParseEventCode(IReadOnlyDictionary<byte, object> parameters)
{
if (!parameters.TryGetValue(252, out var value))
{
return EventCodes.Unused;
}
return (EventCodes)Enum.ToObject(typeof(EventCodes), value);
}
private async Task HealthUpdateEventHandlerAsync(Dictionary<byte, object> parameters)
{
var value = new HealthUpdateEvent(parameters);
await HealthUpdateEventHandler.OnActionAsync(value);
}
}
public class HealthUpdateEventHandler
{
private readonly TrackingController _trackingController;
public HealthUpdateEventHandler(TrackingController trackingController)
{
_trackingController = trackingController;
}
public async Task OnActionAsync(HealthUpdateEvent value)
{
await _trackingController.CombatController.AddDamageAsync(value.ObjectId, value.CauserId, value.HealthChange, value.NewHealthValue);
}
}
public class HealthUpdateEvent
{
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType);
public long CauserId;
public int CausingSpellType;
public EffectOrigin EffectOrigin;
public EffectType EffectType;
public double HealthChange;
public double NewHealthValue;
public long ObjectId;
public GameTimeStamp TimeStamp;
public HealthUpdateEvent(Dictionary<byte, object> parameters)
{
ConsoleManager.WriteLineForNetworkHandler(GetType().Name, parameters);
try
{
if (parameters.ContainsKey(0))
{
ObjectId = parameters[0].ObjectToLong() ?? throw new ArgumentNullException();
}
if (parameters.ContainsKey(1))
{
TimeStamp = new GameTimeStamp(parameters[1].ObjectToLong() ?? 0);
}
if (parameters.ContainsKey(2))
{
HealthChange = parameters[2].ObjectToDouble();
}
if (parameters.ContainsKey(3))
{
NewHealthValue = parameters[3].ObjectToDouble();
}
if (parameters.ContainsKey(4))
{
EffectType = (EffectType) (parameters[4] as byte? ?? 0);
}
if (parameters.ContainsKey(5))
{
EffectOrigin = (EffectOrigin) (parameters[5] as byte? ?? 0);
}
if (parameters.ContainsKey(6))
{
CauserId = parameters[6].ObjectToLong() ?? throw new ArgumentNullException();
}
if (parameters.ContainsKey(7))
{
CausingSpellType = parameters[7].ObjectToShort();
}
}
catch (ArgumentNullException ex)
{
ConsoleManager.WriteLineForError(MethodBase.GetCurrentMethod()?.DeclaringType, ex);
Log.Error(MethodBase.GetCurrentMethod()?.DeclaringType, ex);
}
catch (Exception e)
{
ConsoleManager.WriteLineForError(MethodBase.GetCurrentMethod()?.DeclaringType, e);
Log.Error(MethodBase.GetCurrentMethod()?.DeclaringType, e);
}
}
}
Most important file:
public class CombatController
{
public async Task AddDamageAsync(long objectId, long causerId, double healthChange, double newHealthValue)
{
if (!IsDamageMeterActive || objectId == causerId)
{
return;
}
var gameObject = _trackingController?.EntityController?.GetEntity(causerId);
if (gameObject?.Value == null
|| gameObject.Value.Value?.ObjectType != GameObjectType.Player
|| !_trackingController.EntityController.IsUserInParty(gameObject.Value.Value.Name)
)
{
return;
}
if (GetHealthChangeType(healthChange) == HealthChangeType.Damage)
{
var damageChangeValue = (int)Math.Round(healthChange.ToPositiveFromNegativeOrZero(), MidpointRounding.AwayFromZero);
if (damageChangeValue <= 0)
{
return;
}
gameObject.Value.Value.Damage += damageChangeValue;
}
if (GetHealthChangeType(healthChange) == HealthChangeType.Heal)
{
var healChangeValue = healthChange;
if (healChangeValue <= 0)
{
return;
}
if (IsMaxHealthReached(objectId, newHealthValue))
{
return;
}
gameObject.Value.Value.Heal += (int)Math.Round(healChangeValue, MidpointRounding.AwayFromZero);
}
if (gameObject.Value.Value?.CombatStart == null)
{
gameObject.Value.Value.CombatStart = DateTime.UtcNow;
}
if (await IsUiUpdateAllowedAsync() && !IsUiUpdateActive)
{
await UpdateDamageMeterUiAsync(_mainWindowViewModel.DamageMeter, _trackingController.EntityController.GetAllEntities());
}
}
private static bool IsUiUpdateActive;
public async Task UpdateDamageMeterUiAsync(AsyncObservableCollection<DamageMeterFragment> damageMeter, List<KeyValuePair<Guid, PlayerGameObject>> entities)
{
IsUiUpdateActive = true;
var highestDamage = entities.GetHighestDamage();
var highestHeal = entities.GetHighestHeal();
_trackingController.EntityController.DetectUsedWeapon();
foreach (var healthChangeObject in entities)
{
if (_mainWindow?.Dispatcher == null || healthChangeObject.Value?.UserGuid == null)
{
continue;
}
var fragment = damageMeter.ToList().FirstOrDefault(x => x.CauserGuid == healthChangeObject.Value.UserGuid);
if (fragment != null)
{
await UpdateDamageMeterFragmentAsync(fragment, healthChangeObject, entities, highestDamage, highestHeal).ConfigureAwait(true);
}
else
{
await AddDamageMeterFragmentAsync(damageMeter, healthChangeObject, entities, highestDamage, highestHeal).ConfigureAwait(true);
}
Application.Current.Dispatcher.Invoke(() => _mainWindowViewModel.SetDamageMeterSort());
}
if (HasDamageMeterDupes(_mainWindowViewModel.DamageMeter))
{
await RemoveDuplicatesAsync(_mainWindowViewModel.DamageMeter);
}
IsUiUpdateActive = false;
}
private async Task UpdateDamageMeterFragmentAsync(DamageMeterFragment fragment, KeyValuePair<Guid, PlayerGameObject> healthChangeObject, List<KeyValuePair<Guid, PlayerGameObject>> entities, long highestDamage, long highestHeal)
{
var itemInfo = await SetItemInfoIfSlotTypeMainHandAsync(fragment.CauserMainHand, healthChangeObject.Value?.CharacterEquipment?.MainHand).ConfigureAwait(false);
fragment.CauserMainHand = itemInfo;
// Damage
if (healthChangeObject.Value?.Damage > 0)
{
fragment.DamageInPercent = (double)healthChangeObject.Value.Damage / highestDamage * 100;
fragment.Damage = (long)healthChangeObject.Value?.Damage;
}
if (healthChangeObject.Value?.Dps != null)
{
fragment.Dps = healthChangeObject.Value.Dps;
}
// Heal
if (healthChangeObject.Value?.Heal > 0)
{
fragment.HealInPercent = (double)healthChangeObject.Value.Heal / highestHeal * 100;
fragment.Heal = (long)healthChangeObject.Value?.Heal;
}
if (healthChangeObject.Value?.Hps != null)
{
fragment.Hps = healthChangeObject.Value.Hps;
}
// Generally
if (healthChangeObject.Value != null)
{
fragment.DamagePercentage = entities.GetDamagePercentage(healthChangeObject.Value.Damage);
fragment.HealPercentage = entities.GetHealPercentage(healthChangeObject.Value.Heal);
_trackingController.EntityController.SetPartyCircleColor(healthChangeObject.Value.UserGuid, itemInfo?.FullItemInformation?.CategoryId);
}
}
private async Task AddDamageMeterFragmentAsync(AsyncObservableCollection<DamageMeterFragment> damageMeter, KeyValuePair<Guid, PlayerGameObject> healthChangeObject,
List<KeyValuePair<Guid, PlayerGameObject>> entities, long highestDamage, long highestHeal)
{
if (healthChangeObject.Value == null
|| (double.IsNaN(healthChangeObject.Value.Damage) && double.IsNaN(healthChangeObject.Value.Heal))
|| (healthChangeObject.Value.Damage <= 0 && healthChangeObject.Value.Heal <= 0))
{
return;
}
var mainHandItem = ItemController.GetItemByIndex(healthChangeObject.Value?.CharacterEquipment?.MainHand ?? 0);
var itemInfo = await SetItemInfoIfSlotTypeMainHandAsync(mainHandItem, healthChangeObject.Value?.CharacterEquipment?.MainHand);
var damageMeterFragment = new DamageMeterFragment
{
CauserGuid = healthChangeObject.Value.UserGuid,
Damage = healthChangeObject.Value.Damage,
Dps = healthChangeObject.Value.Dps,
DamageInPercent = (double)healthChangeObject.Value.Damage / highestDamage * 100,
DamagePercentage = entities.GetDamagePercentage(healthChangeObject.Value.Damage),
Heal = healthChangeObject.Value.Heal,
Hps = healthChangeObject.Value.Hps,
HealInPercent = (double)healthChangeObject.Value.Heal / highestHeal * 100,
HealPercentage = entities.GetHealPercentage(healthChangeObject.Value.Heal),
Name = healthChangeObject.Value.Name,
CauserMainHand = itemInfo
};
damageMeter.Init(Application.Current.Dispatcher.Invoke);
damageMeter.Add(damageMeterFragment);
_trackingController.EntityController.SetPartyCircleColor(healthChangeObject.Value.UserGuid, itemInfo?.FullItemInformation?.CategoryId);
}
// Try 1
private bool IsUiUpdateAllowed(int waitTimeInSeconds = 1)
{
var currentDateTime = DateTime.UtcNow;
var difference = currentDateTime.Subtract(_lastDamageUiUpdate);
if (difference.Seconds >= waitTimeInSeconds && !IsUiUpdateActive)
{
_lastDamageUiUpdate = currentDateTime;
return true;
}
return false;
}
// Try 2
private async Task<bool> IsUiUpdateAllowedAsync()
{
await Task.Delay(100).ConfigureAwait(true);
if (_updateReadyCounter++ < 2)
{
return false;
}
_updateReadyCounter = 0;
return true;
}
}
In the last class "CombatController" I am currently trying to get the problem under control.
The "UpdateDamageMeterUiAsync ()" method should only be triggered once if IsUiUpdateAllowedAsync () is true.
Nevertheless, "UpdateDamageMeterUiAsync ()" is always triggered 2-3 times at the same time, which is why multiple entries are created. All other lists are fine, only the UI object "AsyncObservableCollection _mainWindowViewModel.DamageMeter" has the problem.
Does anyone have any idea how I can tackle the problem?

Signal R connection state is 0 after sometime

I am using #aspnet/signalr to connect to a hub and from the hub i am calling the method to return me some data.
When i first initialize the server the connection is established normally, but if i refresh the window 3 or 4 times the client stop sending connection request to the server.
I tried logging the HubConnection, the connectionState at first is equal to 1 but after the problem accrues the connectionState is alwase equal 0
Here is how i am building the hubConnection:
buildConnection() {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.tradesService.getStockQuotationsHubUrl())
.build();
this.hubConnection.serverTimeoutInMilliseconds = 1000 * 10;
this.hubConnection.onclose(() => setTimeout(() => this.startSignalRConnection(), 2000));}
Here is how i am starting the hubConnection:
startConnection() {
this.hubConnection
.start()
.then(() => {
this.hubConnection.on('updateMethod', (data: any) => {
this.store.push([
{ type: 'update', key: data.stockID, data },
]);
});
this.dataSource = new DataSource({
store: this.store,
reshapeOnPush: true,
});
});}
Note: I am listing to 5 hubs at once in my page but the only one having problems is this one.
[Update]
The problem is from the server because when i restart the server the connection is reestablished between the client and the server, but if the client refresh or quit the page for multiple times the hub does not even try to connect to the client
public class StockQuotationsHub : Microsoft.AspNetCore.SignalR.Hub
{
private string SectorID { get; set; }
private int TradingSession { get; set; }
private int MarketID { get; set; }
public static StockQuotationExt stockQuotationExt { get; set; }
private readonly StockQuotationTicker _stockTicker;
public StockQuotationsHub(StockQuotationTicker stockTicker){
_stockTicker = stockTicker;
}
public override Task OnConnectedAsync(){
return base.OnConnectedAsync();
}
public IEnumerable<StockQuotation> GetAllStockQuotations(string[] stockID, string sectorID, int tradingSession, int marketType){
return _stockTicker.
GetAllStocks(Context.ConnectionId, stockID, sectorID, tradingSession, marketType);
}
public override async Task OnDisconnectedAsync(Exception exception){
await base.OnDisconnectedAsync(exception);
}
and here is my stock ticker class:
public IEnumerable<StockQuotation> GetAllStocks(string connectionId, string[] stockID, string sectorID, int tradingSession, int marketType)
{
_stocks = new List<StockQuotation>();
_stocks = Task.Run(async () => await GetStockQuotationModelAsync("", 0, 1, 0)).Result.ToList();
this.MaxTimeStamp = _stocks.Max(s => s.TStamp);
this.SectorID = sectorID;
this.TradingSession = tradingSession;
this.MarketID = marketType;
AddToGroups(connectionId, stockID);
if (_timer==null)
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
if (stockID.Length == 0)
{
return _stocks;
}
else
{
var stocksList = new List<StockQuotation>();
foreach (var stock in stockID)
{
stocksList.AddRange(_stocks.Where(s => s.StockID == stock).ToList());
}
return stocksList;
}
}
private void AddToGroups(string connectionId, string[] stockID)
{
if (_stocks.Count > 0)
{
if (stockID.Length == 0)
{
Hub.Groups.AddToGroupAsync(connectionId, "ALL");
}
else
{
foreach (var stock in stockID)
{
Hub.Groups.AddToGroupAsync(connectionId, stock);
var s = _stocks.FirstOrDefault(s => s.StockID == stock);
if(s != null)
{
s.Snapshots = new List<double>(GetStockQuotationSnapshots(stock));
}
}
}
}
}
private void AddToGroups(string connectionId, string[] stockID)
{
if (_stocks.Count > 0)
{
if (stockID.Length == 0)
{
Hub.Groups.AddToGroupAsync(connectionId, "ALL");
}
else
{
foreach (var stock in stockID)
{
Hub.Groups.AddToGroupAsync(connectionId, stock);
var s = _stocks.FirstOrDefault(s => s.StockID == stock);
if(s != null)
{
s.Snapshots = new List<double>(GetStockQuotationSnapshots(stock));
}
}
}
}
}
I really appreciated the help.
Eventually the problem was that the project type was MVC, so I changed it to be webApi
and everything worked successfully
Even if you fix it I would like to recommend you to add the following code in order to know exactly the root cause of disconnection.
First of all enable the extended debug info in the service configuration:
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
});
Furthermore, you need to attach to the event Closed in HubConnection like that:
hubConnection.Closed += (exception) =>
{
if (exception == null)
{
Console.WriteLine("Connection closed without error.");
}
else
{
Console.WriteLine($"Connection closed due to an error: {exception}");
}
return null;
};

Should I use delegate to declare an anonymous asynchronous method?

I'm looking for ideas about using delegate with async and await. Is it good or not? I've searched on Google but none of them are similar to mine.
I'm defining a method to change a message status (isRead = true) with SignalR:
enum MessageStatus
{
Failure,
Success
}
delegate Task<MessageStatus> MsgDelegate(string id);
public async Task ChangeMessageStatus(string id)
{
string error = string.Empty;
MsgDelegate msg = async (x) =>
{
try
{
using (var db = new VinaChanelDbContext())
{
var message = db.Messages.SingleOrDefault(m => m.Id == x);
if (message != null)
{
message.IsRead = true;
}
return await db.SaveChangesAsync() > 0 ?
MessageStatus.Success : MessageStatus.Failure;
}
}
catch (Exception e)
{
error = e.Message;
return MessageStatus.Failure;
}
};
switch (await msg(id))
{
case MessageStatus.Success:
Clients.Caller.updateStatus(true);
break;
case MessageStatus.Failure:
Clients.Caller.errorMessage(error);
Clients.Caller.updateStatus(false);
break;
}
}
Is my code weird? Should I use it?
Why use a delegate? What's wrong with:
enum MessageStatus
{
Failure,
Success
}
public async Task ChangeMessageStatus(string id)
{
string error = string.Empty;
var status = MessageStatus.Failure;
try
{
using (var db = new VinaChanelDbContext())
{
var message = db.Messages.SingleOrDefault(m => m.Id == id);
if (message != null)
{
message.IsRead = true;
if (await db.SaveChangesAsync() > 0)
{
status = MessageStatus.Success;
}
}
}
}
catch (Exception e)
{
error = e.Message;
}
switch (status)
{
case MessageStatus.Success:
Clients.Caller.updateStatus(true);
break;
case MessageStatus.Failure:
Clients.Caller.errorMessage(error);
Clients.Caller.updateStatus(false);
break;
}
}
You might want to break that top part out into a private method, but I'm not sure why you would want to make it a delegate...

Task Blocking UI Thread

I have a Primedcommand class within a wpf solution to execute a quick action then execute a task on another thread to prevent the ui thread from being blocked as such:
public void Execute(object parameter)
{
if (CanExecute(parameter))
{
System.Windows.Application.Current.Dispatcher.Invoke(() => { _primer(); });
Task.Factory.StartNew(() => { _execute(parameter); }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
And the PrimedCommand constructor:
public PrimedCommand(Action primer, Action<object> execute, Predicate<object> canExecute)
{
if (primer == null)
throw new ArgumentNullException("primer");
if (execute == null)
throw new ArgumentNullException("execute");
_primer = primer;
_execute = execute;
_canExecute = canExecute;
}
And one of the worker methods at request of #DanPuzey:
Executed action:
private static void AddChoices(ref Settings RunningSettings)
{
if (Processes.ShouldExit) return;
try
{
if (RunningSettings.Initialized)
{
if (Processes.ShouldExit) return;
if (RunningSettings.WorkingDirectory != null)
{
DirectoryInfo workingDir = new DirectoryInfo(RunningSettings.WorkingDirectory);
if (!workingDir.Exists)
{
throw new DirectoryNotFoundException("The Source Directory Didn't Exist");
}
RunningSettings.CurrentStatus.AddMoment(new Moment("Loading Customers"));
Dictionary<string, string> customerNames = new Dictionary<string, string>();
Dictionary<string, string> jobNumbers = new Dictionary<string, string>();
List<DirectoryInfo> availableFolders = new List<DirectoryInfo>();
if (Tools.IsCustomer(workingDir))
{
availableFolders.Add(workingDir);
}
else if (Tools.IsCustomerContainer(workingDir))
{
availableFolders.AddRange(workingDir.EnumerateDirectories().Where(c => Tools.IsCustomer(c)));
}
else if (Tools.IsJob(workingDir))
{
availableFolders.Add(workingDir.Parent);
}
foreach (DirectoryInfo customer in availableFolders)
{
if (Processes.ShouldExit) return;
try
{
RunningSettings.CurrentStatus.AddMoment(new Moment(String.Format(" Loading Jobs For: {0}", customer)));
if (!customerNames.ContainsKey(customer.Name))
{
customerNames.Add(customer.Name, null);
}
foreach (DirectoryInfo job in customer.GetDirectories().Where(j => Tools.IsJob(j)))
{
if (Processes.ShouldExit) return;
try
{
string tempNumber = job.Name.Substring(0, 6);
if (!jobNumbers.ContainsKey(tempNumber))
{
jobNumbers.Add(tempNumber, customer.Name);
}
}
catch (Exception except)
{
ErrorHandling.Handle(except, ref RunningSettings);
}
}
}
catch (Exception excep)
{
ErrorHandling.Handle(excep, ref RunningSettings);
}
}
int count = 0;
int index = 0;
if (customerNames != null && customerNames.Count > 0)
{
RunningSettings.ClearCustomerCollection();
count = customerNames.Count;
foreach (KeyValuePair<string, string> customer in customerNames)
{
if (Processes.ShouldExit) break;
try
{
index++;
RunningSettings.AddCustomer(customer.Key, customer.Value, (index == count));
}
catch (Exception excep)
{
ErrorHandling.Handle(excep, ref RunningSettings);
}
}
RunningSettings.SortCustomers();
}
if (Processes.ShouldExit) return;
count = 0;
index = 0;
if (jobNumbers != null && jobNumbers.Keys.Count > 0)
{
RunningSettings.ClearJobCollection();
count = jobNumbers.Count;
foreach (KeyValuePair<string, string> job in jobNumbers)
{
if (Processes.ShouldExit) break;
try
{
index++;
RunningSettings.AddJob(job.Key, job.Value, (index == count));
}
catch (Exception excep)
{
ErrorHandling.Handle(excep, ref RunningSettings);
}
}
RunningSettings.SortJobs();
}
if (Processes.ShouldExit) return;
RunningSettings.CurrentStatus.AddMoment(new Moment("Loading Customers Complete"));
}
else
{
throw new InvalidOperationException("The Working Directory Was Null");
}
}
else
{
throw new InvalidOperationException("The Settings Must Be Initialized Before Customer Folders Can Be Enumerated");
}
}
catch (Exception ex)
{
ErrorHandling.Handle(ex, ref RunningSettings);
}
}
Cancel action:
public static void Cancel()
{
KeepRunning = false; // Bool watched by worker processes
}
From the let's call it the execute button the primer's job is to set the value of a property showing the user the action is active.
This is fine, however when I click on the cancel button which will update that status property to cancelling then set a field in the worker class to indicate the action is cancelling, the UI takes about 2 seconds to respond to the button click. I've tried Task.Run and Task.Factory.StartNew with a variety of overloads and creating my own worker thread seems to work the best but still not how I want.
What I'm looking for is you click on execute button, status is updated to active(updating the ui) bound to that property then when the cancel button is clicked the status is changed to cancelling and the task to notify the worker sets the appropriate field(the worker thread checks this field often and exits when needed).
What's not working is the worker thread blocks the ui thread from updating to show the user the action is cancelling.(The cancel button is temporarily unable to be clicked after status is set to active)
Also noteworthy is this solution is using mvvm where the status is an enum value the ui binds to with a converter.
Any question just let me know.

Categories

Resources