Как запустить поток для обновления графического интерфейса? - PullRequest
2 голосов
/ 21 декабря 2010

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

Итак вопрос в том, как запустить поток, чтобы он не мешал GUI, т. Е. Чтобы GUI всегда былв актуальном состоянии (во время обработки я изменяю данные, а графический интерфейс отображает некоторые их фрагменты)?

Вот как я начинаю поток правильно:

        var thread = new Thread(doLearn);
        thread.IsBackground = true;
        thread.Start();

Редактировать 1

Jon:

  1. Я вообще не использую никаких блокировок
  2. Нет вызовов присоединения
  3. Поток пользовательского интерфейса остается один - он просто сидит там

Обработка - это большой цикл с математическими операциями, даже без выделения памяти, на стороне пользовательского интерфейса у меня есть элементы управления с привязкой (WPF) к данным, например, номер текущей итерации основного цикла.Следует обновлять каждый раз, когда основной цикл "тикает".Счетчик цикла - это свойство, которое запускает OnPropertyChanged с каждым изменением (классическая привязка WPF).

Редактировать 2 - Почти там!

Хорошо, так что Джон ударил по гвоздю в голову (кого это удивляет? ;-D) - спасибо!Проблема заключается в смене счетчика.Когда я использовал вместо Counter локальный счетчик, обновился графический интерфейс - я имею в виду, что я мог перемещать окна, но ... я не мог видеть отображение счетчика.

Что у меня здесь - WPFGUI, с такой привязкой данных

<TextBlock Text="{Binding Path=Counter"/>

и у меня есть свойство Counter, которое при каждом изменении отправляет событие PropertyChanged.Один из слушателей наверняка GUI.

Итак, Jon answer является верным «ответом», но от POV хорошего дизайна не совсем, потому что если часть GUI должна получить информацию о Counter и обновить отображениекаждые (скажем) 3 секунды, зачем кому-то использовать привязку данных?Для меня такой подход лишает законной силы идею связывания данных.

Теоретически я мог бы передать в поток обработки диспетчер GUI и выполнить всю отправку в потоке GUI, и это могло бы работать (я не пробовал)но это означало бы тесную связь не-GUI-части и GUI-части.

Пока что я не знаю, как сделать это «правильным» способом.На данный момент лучше всего создать TimerDispatcher, но не на стороне графического интерфейса, а внутри библиотеки обработки, и немедленно обновить значение счетчика, но время от времени выполнять всю отправку (хотя я еще не пробовал).

Небольшое замечание: у меня на самом деле больше свойств, связанных, например, IsRunning, который изменяется в начале и в конце обработки.И эти изменения действительно влияют на дисплей правильно, но изменение счетчика вызывает около 3000 уведомлений в течение 3-4 секунд.Так что похоже на проблему глушения.Я сделал еще один тест - я частично убил привязку данных, поэтому уведомления отправлялись, но GUI их не «получал», а слушал их.В этом случае графический интерфейс пользователя также был заморожен.

Итак, я все еще слушаю все советы - спасибо заранее за обмен.

Редактировать 3

сага продолжается здесь:

Как выполнить обработку и обновлять графический интерфейс с помощью привязки данных?

Ответы [ 4 ]

3 голосов
/ 21 декабря 2010

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

  • Вы блокируете в потоке пользовательского интерфейса и блокируете такую ​​же блокировку в другом потоке?
  • Вы звоните Join напоток из вашего потока пользовательского интерфейса?
  • Вы выполняете какую-то другую тяжелую работу в потоке пользовательского интерфейса?

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

РЕДАКТИРОВАТЬ: Хорошо, теперь вы добавили это:

Счетчик цикла - это свойство, которое запускает OnPropertyChanged при каждом изменении (классическая привязка WPF).

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

Я предлагаю вам использовать такой подход, как:

  • Периодически обновлять счетчик с помощью Dispatcher.BeginInvoke
  • Имейте «счетчик пользовательского интерфейса» и «рабочий счетчик» - и скопируйте значение из «рабочего счетчика» в «счетчик пользовательского интерфейса» в потоке пользовательского интерфейса через DispatcherTimer, по существу опрашивая его.
3 голосов
/ 21 декабря 2010

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

0 голосов
/ 23 декабря 2010

Я столкнулся с той же ситуацией и решил ее двумя способами ...

  1. Используйте поток в другом классе и вызовите его в основном приложении ur, создав поток, либо в егоконструктор ИЛИ в любом методе.

  2. если вы хотите сделать это в том же классе, то создайте поток, который вызывает вашу функцию, и эта функция должна вызывать делегат.

См. Примеры:

     public partial class Form1 : Form
    {
        private delegate void TickerDelegate();
        TickerDelegate tickerDelegate1;

        public Form1()
        {
            InitializeComponent();
        }
   //first solution
   // This button event call other class having Thread

       private void button1_Click(object sender, EventArgs e) 
        {
            f = new FormFileUpdate("Auto File Updater", this);
            f.Visible = true;
            this.Visible = false;         
        }

        // Second Solution
        private void BtnWatch_Click(object sender, EventArgs e)
        {
            tickerDelegate1 = new TickerDelegate(SetLeftTicker);
            Thread th = new Thread(new ThreadStart(DigitalTimer));
            th.IsBackground = true;
            th.Start();
         }

        private void SetLeftTicker()
        {
            label2.Text=DateTime.Now.ToLongTimeString();
        }


        public void DigitalTimer()
        {
            while (true)
            {
                label2.BeginInvoke(tickerDelegate1, new object[] {});
                Thread.Sleep(1000);
            }
        }
    }
0 голосов
/ 21 декабря 2010

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

Мне очень неприятно говорить вам, но тогда вы НЕ поместили это в отдельную ветку. Это просто.

Здесь был постер, у которого некоторое время назад была похожая проблема, и из-за ошибки в вызывающем коде он в основном выполнял всю обработку до начала потока, причем поток jsut возвращал результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...