2013-02-11 3 views
0

Без использования дополнительных потоков я бы просто хотел показать ярлык «Загрузка» или что-то похожее на пользователя, когда читается или записывается большой объем данных. Если я, однако, попытаюсь изменить любые элементы пользовательского интерфейса до, вызвав метод ввода-вывода, приложение замерзает на некоторое время, а затем отображает сообщение «Загрузка» после, все работы уже выполнены. Это явно не помогает. Как я могу гарантировать, что любые изменения пользовательского интерфейса применяются и отображаются до, вызывающих метод ввода-вывода?Как заставить приложение ждать обновления моего интерфейса?

 DataSet ds = STT_Import.ImportExcelToDataSet(filePath); 

     bool result = false; 

     if (ds != null) 
     { 
      int cellCount = ds.GetTotalCellCount(); 

      if (Popup.ShowMessage(string.Format("Your file contains {0} cells. Inserting data will take approximately {1} seconds. Do you want to continue?", 
       cellCount, CalculateTime(cellCount)), "Confirm", MessageType.Confirm) == MessageBoxResult.Yes) 
      { 
       // Tell user the application is working: 
       StatusLabel.Content = "Writing to database..."; 

       // Do actual work after user has been notified: 
       result = DB.StoreItems(_currentType, ds); 
      } 
     } 

Я пытался искать ответы, но не смогли найти ничего, что ответили на мой конкретный вопрос, поэтому я прошу прощения, если вопрос был задан ранее.

+0

Ну есть Application.DoEvents(), но я считаю, что Verboten (у него есть много вопросов). Я всегда говорю, что вы должны делать многопоточность правильно - в этом случае, используя BackgroundWorker. –

+0

Я согласен с Мэтью .... Используйте BackgroundWorker ... http://stackoverflow.com/questions/5483565/how-to-use-wpf-background-worker – PGallagher

+0

Вы не можете быть уверены, что пользователь не будет взаимодействовать с пользовательским интерфейсом после того, как начнется длительный запуск ввода-вывода. –

ответ

1

При работе с WPF, вы можете использовать Dispatcher очереди команд на потоке пользовательского интерфейса при различных DispatcherPriorities

Это позволит вам в очередь ваш затянувшийся процесс в потоке пользовательского интерфейса после все в очереди DispatcherPriority.Render или DispatcherPriority.Loaded.

Например, код может выглядеть следующим образом:

// Tell user the application is working: 
StatusLabel.Content = "Writing to database..."; 

// Do actual work after user has been notified: 
Dispatcher.BeginInvoke(DispatcherPriority.Input, 
    new Action(delegate() { 
     var result = DB.StoreItems(_currentType, ds);  // Do Work 
     if (result) 
      StatusLabel.Content = "Finished"; 
     else 
      StatusLabel.Content = "An error has occured"; 
    })); 

Следует отметить, однако, что его обычно считается плохой дизайн запирать приложения пока что-то работает.

Лучшим решением было бы запустить длительный процесс в фоновом потоке и просто отключить форму заявки во время ее запуска. Есть много способов сделать это, но мое личное предпочтение использует Task Parallel Library для его простоты.

В качестве примера, ваш код, чтобы использовать фоновый поток будет выглядеть примерно так:

using System.Threading.Tasks; 

... 

// Tell user the application is working: 
StatusLabel.Content = "Writing to database..."; 
MyWindow.IsEnabled = False; 

// Do actual work after user has been notified: 
Task.Factory.StartNew(() => DB.StoreItems(_currentType, ds)) 
    // This runs after background thread is finished executing 
    .ContinueWith((e) => 
    { 
     var isSuccessful = e.Result; 

     if (isSuccessful) 
      StatusLabel.Content = "Finished"; 
     else 
      StatusLabel.Content = "An error has occured"; 

     MyWindow.Enabled = true; 
    }); 
+0

Ваш первый вариант по-прежнему блокирует поток пользовательского интерфейса. 'DB.StoreItems' - это длинный метод, который блокирует, поэтому его вызов внутри вызова Dispatcher.BeginInvoke блокирует поток пользовательского интерфейса в течение его продолжительности. Вы блокируете поток пользовательского интерфейса для немного более короткого интервала, но вы не сократили его на * достаточно *, чтобы работать. Если бы это была задача, связанная с ЦП, которую он контролировал, вы потенциально могли бы повторно подвергнуть ее периодическому вызову «BeginInvoke» и поддерживать отзывчивость пользовательского интерфейса, но здесь это не так. Для второго бита вы используете другой поток, который OP сказал, это то, что он не хочет делать. – Servy

+0

@Servy Это намеренно. Первый пример кода обновит UI * до * блокировки приложения, что звучит так, как хочет OP. И если вы читаете строку сразу после первого примера, я говорю, что не рекомендуется запирать приложение таким образом и предлагать пересмотреть использование фонового потока. Я рекомендую TPL для его простоты и предлагаю образец кода, чтобы пойти с ним. – Rachel

+0

Вот почему я не спустил вниз, я просто прокомментировал. – Servy

0

Подхода вы используете не является эффективным способом, так что я хотел бы предложить, чтобы пойти с асинхронным программированием или вкручиваются

Асинхронного программирование:

Visual Studio 2012 представляет упрощенный подход, асинхронное программирование, которая использует поддержку асинхронной в .NET Framework 4.5 и Windows Runtime. Компилятор выполняет сложную работу, которую разработчик использовал, и ваше приложение сохраняет логическую структуру, которая напоминает синхронный код. В результате вы получаете все преимущества асинхронного программирования с небольшой долей усилий. Поддержка .NET Framework 4.5
Это сэкономит ваше время внедрения системы .Threading и очень эффективно для выполнения этой задачи такой же, как ваш, где мы должны ждать какой-нибудь операции

http://msdn.microsoft.com/en-ca/library/vstudio/hh191443.aspx
http://go.microsoft.com/fwlink/?LinkID=261549

или

Threading:

Преимущество потоковой передачи - это возможность создавать приложения, которые используют более одного потока выполнения. Например, процесс может иметь пользовательский интерфейс, который управляет нить взаимодействием с пользователем и рабочими потоками, которые выполняют другие задачи, в то время как пользовательский интерфейс поток ожидает пользователя input.Support .Net fremework 4.0 или Старой

http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx

+0

Обратите внимание, что для эффективной работы (с учетом ограничений OP) его БД должна предоставить асинхронную версию метода, который ему нужен. – Servy

+0

Если он уже использует 4.5 framework, он будет работать в противном случае, все старые версии уже поддерживают Threading – Garry

+0

Я использую Visual Studio 2010 в любом случае, поэтому это было бы невозможно. – firant

0

Вы пытаетесь решить проблему неправильно. То, что вы должны делать здесь, - это выполнить трудоемкую задачу в рабочем потоке; таким образом, ваш пользовательский интерфейс будет оставаться отзывчивым и текущий вопрос станет спорным.

Существует несколько способов разгрузить задачу в рабочий поток; среди наиболее удобных являются using the thread pool и asynchronous programming.

+0

Асинхронное программирование не требует выполнения работы в потоке, отличном от UI. На самом деле, когда долго работающей задачей является сетевой IO, нет необходимости в другом потоке, поэтому этот метод может использоваться при выполнении ограничений OP. – Servy

0

Невозможно сохранить свой пользовательский интерфейс без использования дополнительных потоков, если ваша база данных не предоставляет асинхронную версию метода, который вы используете. Если это делает, предложите асинхронную версию метода, тогда вам просто нужно это использовать. (Имейте в виду, что async делает не означает, что он использует любые другие потоки. Вполне возможно создать асинхронный метод, который никогда не использует дополнительные потоки, и это точно, что сделано с большинством сетевых методов ввода-вывода.) Специфика того, как делать это зависит от типа используемой структуры базы данных и того, как вы ее используете.

Если ваша инфраструктура базы данных не предоставляет методы async, единственный способ сохранить отзывчивость пользовательского интерфейса - выполнить длительную операцию (ы) в потоке, отличном от UI.

0

Если вы не хотите, чтобы пользовательский интерфейс был отзывчивым, я использую индикатор занятости.
Есть более красивые курсоры - это приложение для дома.

using (new WaitCursor()) 
{ 
    // very long task 
    Search.ExecuteSearch(enumSrchType.NextPage); 
} 

public class WaitCursor : IDisposable 
{ 
    private Cursor _previousCursor; 

    public WaitCursor() 
    { 
     _previousCursor = Mouse.OverrideCursor; 

     Mouse.OverrideCursor = Cursors.Wait; 
    } 

    #region IDisposable Members 

    public void Dispose() 
    { 
     Mouse.OverrideCursor = _previousCursor; 
    } 

    #endregion 
} 
Смежные вопросы