Это мой первый пост, вот он... У меня есть класс Job, который подключается к нескольким таблицам в базе данных в конструкторе и получает элемент из каждой таблицы. Доступ к базе данных осуществляется через веб-службы SOAP. Объект создается нажатием кнопки. Когда класс создается в main, в него передаются строка поиска и настраиваемый элемент графического интерфейса индикатора выполнения с текстом.
Индикатор выполнения является пользовательским элементом управления и имеет метод Update() для обновления текста сообщения и хода выполнения.
Я пробовал несколько способов обновить графический интерфейс (индикатор выполнения) из конструктора и нашел этот пост (Как использовать фоновый рабочий WPF) очень полезным, однако графический интерфейс обновляется только после завершения всего метода.
Я попытался удалить методы из конструктора класса Job и вызвать их из основного потока и обновить графический интерфейс между вызовами методов, я попытался использовать диспетчер для обновления графического интерфейса и даже ожидал их как задачу. Я также попытался напрямую обновить значения пользовательского элемента управления. Тем не менее, графический интерфейс обновляется только после завершения метода поиска.
Есть ли лучший способ сделать это? Я слишком много делаю в конструкторе класса Job? Даже до создания объекта графический интерфейс не обновляется.
private async void AddMasterJob_Click(object sender, RoutedEventArgs e)
{
SearchMethod();
}
//Search method is used in multiple places.
private async void SearchMethod()
{
//This does not change the GUI until the whole method is complete.
await Task.Run(() => CurrentProgressControl.Visibility = Visibility.Visible);
await Task.Run(() => CurrentProgressControl.BringIntoView());
//Checks if record exists during constructor
var tempJob = new Job(SearchBox.Text, CurrentProgressControl);
//Only assign _masterJob if search was successful.
if (tempJob.MasterJob != null)
{
//Try updating the GUI fields directly
await Task.Run(() => CurrentProgressControl.progressBar.Value = 25);
await Task.Run(() => CurrentProgressControl.label.Content = "Getting SubJobs");
//Gets the subjobs and creates an array of subjob objects
tempJob.GetSubJobs();
//Try updating using the update method built into the custom control
CurrentProgressControl.Update("Getting Planning Lines and Ledger Entries.", 45);
//Get the planning lines and ledger entries
tempJob.GetPlanningLinesandLedgerEntries();
CurrentProgressControl.Update("Creating the Estimate Line List.", 60);
//Combines Subjobs, Planning Lines, and Ledger Entries
//Calls CalculateTable(_tableLines)
tempJob.CreateEstimateLineList();
CurrentProgressControl.Update("Separating into Labor and Material.", 75);
//Populate _laborLines and _materialLines
tempJob.SeparateTableLines(tempJob.TableLines);
CurrentProgressControl.Update("Creating SubTotals.", 85);
//Insert the subtotal Lines
tempJob.CreateSubtotalLines(tempJob.LaborLines);
tempJob.CreateSubtotalLines(tempJob.MaterialLines);
CurrentProgressControl.Update("Calculating Totals.", 95);
//Calculate the total estimate column and subtotal lines
tempJob.CalculateTable(tempJob.MaterialLines);
tempJob.CalculateTable(tempJob.LaborLines);
//Calculate the totals for the whole Job
tempJob.CalculateTotals();
CurrentProgressControl.Update("Completed Successfully.", 100);
_masterJob = tempJob;
RaisePropertyChanged("MasterJob");
}
}
Графический интерфейс обновляется после завершения searchMethod, но не раньше. Некоторые из методов конструктора внутри метода поиска занимают несколько секунд, поэтому я ожидаю, что графический интерфейс будет обновляться несколько раз, но вместо этого будет только одно обновление, которое говорит о завершении.





Не обновляйте пользовательский интерфейс с помощью Task.Run, а вместо этого используйте его для выполнения и ожидания длительных операций:
private async void AddMasterJob_Click(object sender, RoutedEventArgs e)
{
await SearchMethod();
}
private async Task SearchMethod()
{
CurrentProgressControl.Visibility = Visibility.Visible;
CurrentProgressControl.BringIntoView();
var tempJob = new Job(SearchBox.Text, CurrentProgressControl);
if (tempJob.MasterJob != null)
{
CurrentProgressControl.progressBar.Value = 25;
CurrentProgressControl.label.Content = "Getting SubJobs";
await Task.Run(() => tempJob.GetSubJobs());
CurrentProgressControl.Update("Getting Planning Lines and Ledger Entries.", 45);
await Task.Run(() => tempJob.GetPlanningLinesandLedgerEntries());
CurrentProgressControl.Update("Creating the Estimate Line List.", 60);
await Task.Run(() => tempJob.CreateEstimateLineList());
CurrentProgressControl.Update("Separating into Labor and Material.", 75);
await Task.Run(() => tempJob.SeparateTableLines(tempJob.TableLines));
CurrentProgressControl.Update("Creating SubTotals.", 85);
await Task.Run(() =>
{
tempJob.CreateSubtotalLines(tempJob.LaborLines);
tempJob.CreateSubtotalLines(tempJob.MaterialLines);
});
CurrentProgressControl.Update("Calculating Totals.", 95);
await Task.Run(() =>
{
tempJob.CalculateTable(tempJob.MaterialLines);
tempJob.CalculateTable(tempJob.LaborLines);
tempJob.CalculateTotals();
});
CurrentProgressControl.Update("Completed Successfully.", 100);
_masterJob = tempJob;
RaisePropertyChanged("MasterJob");
}
}
Или используйте один вызов Task.Run и обновите пользовательский интерфейс с помощью диспетчера:
private async Task SearchMethod()
{
CurrentProgressControl.Visibility = Visibility.Visible;
CurrentProgressControl.BringIntoView();
var tempJob = new Job(SearchBox.Text, CurrentProgressControl);
if (tempJob.MasterJob != null)
{
CurrentProgressControl.progressBar.Value = 25;
CurrentProgressControl.label.Content = "Getting SubJobs";
await Task.Run(() =>
{
tempJob.GetSubJobs());
Dispatcher.Invoke(() => CurrentProgressControl.Update("Getting Planning Lines and Ledger Entries.", 45));
tempJob.GetPlanningLinesandLedgerEntries();
Dispatcher.Invoke(() => CurrentProgressControl.Update("Creating the Estimate Line List.", 60));
tempJob.CreateEstimateLineList();
Dispatcher.Invoke(() => CurrentProgressControl.Update("Separating into Labor and Material.", 75));
tempJob.SeparateTableLines(tempJob.TableLines);
Dispatcher.Invoke(() => CurrentProgressControl.Update("Creating SubTotals.", 85));
tempJob.CreateSubtotalLines(tempJob.LaborLines);
tempJob.CreateSubtotalLines(tempJob.MaterialLines);
Dispatcher.Invoke(() => CurrentProgressControl.Update("Calculating Totals.", 95));
tempJob.CalculateTable(tempJob.MaterialLines);
tempJob.CalculateTable(tempJob.LaborLines);
tempJob.CalculateTotals();
});
CurrentProgressControl.Update("Completed Successfully.", 100);
_masterJob = tempJob;
RaisePropertyChanged("MasterJob");
}
}
Если возможно, сделайте методы задания асинхронными:
private async Task SearchMethod()
{
CurrentProgressControl.Visibility = Visibility.Visible;
CurrentProgressControl.BringIntoView();
var tempJob = new Job(SearchBox.Text, CurrentProgressControl);
if (tempJob.MasterJob != null)
{
CurrentProgressControl.progressBar.Value = 25;
CurrentProgressControl.label.Content = "Getting SubJobs";
await tempJob.GetSubJobsAsync();
CurrentProgressControl.Update("Getting Planning Lines and Ledger Entries.", 45);
await tempJob.GetPlanningLinesandLedgerEntriesAsync();
CurrentProgressControl.Update("Creating the Estimate Line List.", 60);
await tempJob.CreateEstimateLineListAsync();
CurrentProgressControl.Update("Separating into Labor and Material.", 75);
await tempJob.SeparateTableLinesAsync(tempJob.TableLines);
CurrentProgressControl.Update("Creating SubTotals.", 85);
await tempJob.ob.CreateSubtotalLinesAsync(tempJob.LaborLines);
await tempJob.CreateSubtotalLinesAsync(tempJob.MaterialLines);
CurrentProgressControl.Update("Calculating Totals.", 95);
await tempJob.CalculateTableAsync(tempJob.MaterialLines);
await tempJob.CalculateTableAsync(tempJob.LaborLines);
await tempJob.CalculateTotalsAsync();
CurrentProgressControl.Update("Completed Successfully.", 100);
_masterJob = tempJob;
RaisePropertyChanged("MasterJob");
}
}
Без понятия, откуда это. Создает ли какой-либо из методов Job объекты DependencyObject, например. Элементы пользовательского интерфейса?
Класс Job содержит списки, привязанные в графическом интерфейсе к DataGrid. Я читал о свойстве зависимости и подумал, что мне, возможно, придется реализовать Freezable. Замораживаемая документация Есть ли другой способ сделать это?
Я выделил проблему в строке: RaisePropertyChanged("MasterJob"); Удаление этой строки останавливает ошибку, но также предотвращает обновление графического интерфейса.
Вы случайно не вызываете его из какого-то действия Task.Run?
Я переместил его в конец метода AddMasterJob_Click() после поиска ожидания и заменил tempJob на _masterJob, чтобы удалить копирование в конце, и ошибка все еще сохраняется.
Я изменил его обратно на tempJob и скопировал значение в _masterJob в методе щелчка, при этом tempJob является глобальной переменной. Ошибка не возникала, если я ничего не делал с tempJob, даже с RaisePropertyChanged("MasterJob");. Как только я пытаюсь использовать значение, созданное в задачах (tempJob), возникает ошибка. Это заставляет меня поверить, что это действительно вызвано привязкой, которая должна произойти, и инициализацией, которая должна происходить в том же потоке, но происходит в другом потоке при использовании задачи. Имеет ли это смысл?
В итоге это был подкласс, который использовал класс Job, содержащий элемент Brush. Кисть является объектом зависимости и вызывала ошибки перекрестного потока. Мое решение состояло в том, чтобы создать поле tempJob для нового объекта и скопировать все его значения в _masterJob, а также создать новые элементы кисти для подкласса в основном потоке. Пометил ваш ответ как правильный, потому что он решил вопрос.
Это почти сработало!! Теперь он работает, и графический интерфейс обновляется различными частями метода именно так, как я ожидал, однако теперь, когда метод завершается, я получаю сообщение об ошибке: «System.ArgumentException: «Необходимо создать DependencySource в том же потоке, что и DependencyObject».» Ошибка выдается при выходе из метода AddMasterJob_Click().