работа с потоками в winforms [duplicate]

На всякий случай, кто-то посещает этот вопрос в 2017 году или позже и ищет удобный для запоминания способ, вот подробное сообщение в блоге на Доступ к вложенным объектам в JavaScript без бамбука

Невозможно прочитать свойство «foo» ошибки undefined

Доступ к вложенным объектам с использованием массива Уменьшить

Возьмем эту примерную структуру

const user = {
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

Чтобы иметь доступ к вложенным массивам, вы можете написать собственный массив, уменьшающий использование.

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'address', 0, 'city']);
// this will return the city from the first address item.

Существует также отличная обработка минимальной библиотеки typy что делает все это для вас.

С типичным кодом ваш код будет выглядеть следующим образом:

const city = t(user, 'personalInfo.address[0].city').safeObject;

Отказ от ответственности: я являюсь автором этого пакета.

25
задан David Schmitt 19 November 2008 в 20:49
поделиться

7 ответов

Я привел пример использования System.Windows.Threading.Dispatcher в Windows Form в ответе на вопрос «Параллельное программирование с использованием TPL на WinForms», поскольку предыдущий ответ на ваш вопрос :

Если вы уверены, что находитесь в потоке пользовательского интерфейса (например, в обработчике button.Click), Dispatcher.CurrentDispatcher предоставляет диспетчер потоков UI, который вы можете использовать для отправки из фонового потока в пользовательский интерфейс как обычно.

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

  • button.Click обработчик не гарантирует, что он включен в поток пользовательского интерфейса;
  • , если вы не используете поток пользовательского интерфейса, по-прежнему можно использовать disparcher из потока пользовательских интерфейсов формы WinForms

. Можно получить диспетчер потока WinForm UI:

Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;

в любом обработчике событий нажатия кнопки или в другом месте (в конструкторе форм)

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

private void button1_Click(object sender, EventArgs e)
{
  Dispatcher dispUI = Dispatcher.CurrentDispatcher;
  for (int i = 2; i < 20; i++)
  {
    int j = i;
    var t = Task.Factory.StartNew
           (() =>
      {
        var result = SumRootN(j);
        dispUI.BeginInvoke
            (new Action
                 (() => richTextBox1.Text += "root " + j.ToString()
                       + " " + result.ToString() + Environment.NewLine
                 )
             , null
            );
      }
           );
}
9
ответ дан Community 28 August 2018 в 11:09
поделиться
  • 1
    using System.Threading; "Тип или пространство имен" Диспетчер " Не удалось найти .... & quot; – n00dles 1 June 2017 в 14:56
  • 2
    Вы укажете "button.Click handler не гарантирует, что будет в потоке пользовательского интерфейса & quot; то «Любой может получить диспетчер потока WinForm UI [...] в любом обработчике событий нажатия кнопки». Как все иначе? Как мы можем гарантировать, что мы находимся в потоке пользовательского интерфейса, не доходя до диспетчера в конструкторе формы? – NDUF 6 June 2018 в 03:15

Иногда компонент Timer полезен и легко настраивается в WinForms, просто устанавливает его интервал и затем включает его, а затем убедитесь, что первое, что вы делаете в обработчике событий Tick, - это отключить себя.

Я думаю, что Timer запускает код в своем собственном потоке, поэтому вам может понадобиться выполнить BeginInvoke (вызываемый объектом WinForm [this]) для запуска вашего действия.

private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;

public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
  handler = (s, e) =>
   {
     webBrowser.DocumentCompleted -= handler; //must do first

     this.imageFilename = imageFilename;
     this.exit = exit;

     timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
     timerScreenshot.Enabled = true;
   };

  webBrowser.DocumentCompleted += handler;
  Go(address); //if address == null, will use URL from UI
}

private void timerScreenshot_Tick(object sender, EventArgs e)
{
  timerScreenshot.Enabled = false; //must do first

  BeginInvoke((Action)(() => //Invoke at UI thread
  { //run in UI thread

    BringToFront();
    Bitmap bitmap = webBrowser.GetScreenshot();

    if (imageFilename == null)
      imageFilename = bitmap.ShowSaveFileDialog();

    if (imageFilename != null)
    {
      Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
      bitmap.Save(imageFilename);
    }

    bitmap.Dispose(); //release bitmap resources

    if (exit)
      Close(); //this should close the app, since this is the main form

  }), null);
}

вы можете увидеть выше в действии в инструменте WebCapture ( http://gallery.clipflair.net/WebCapture , исходный код: http://ClipFlair.codeplex.com , см. раздел «Инструменты / Веб-камера»), который захватывает скриншоты с веб-сайтов. BTW, если вы хотите вызвать исполняемый файл из командной строки, убедитесь, что вы перешли в «Свойства проекта» и на вкладке «Безопасность» отключите защиту ClickOnce (иначе он не сможет получить доступ к командной строке)

0
ответ дан George Birbilis 28 August 2018 в 11:09
поделиться

Вы можете использовать Dispatcher даже в приложении WinForms.

Если вы уверены, что находитесь в потоке пользовательского интерфейса (например, в обработчике button.Click), Dispatcher.CurrentDispatcher дает вам поток пользовательского интерфейса диспетчер, который позже можно использовать для отправки из фоновых потоков в поток пользовательского интерфейса, как обычно.

17
ответ дан Martin Konicek 28 August 2018 в 11:09
поделиться

Dispatcher - это компонент WPF, а не компонент WinForms.

Если вы хотите отправлять рабочие элементы в поток пользовательского интерфейса, вам придется либо использовать Control.BeginInvoke, как вы уже нашли, или реагировать на ResetEvents / WaitObjects по потокам.

Обычно вызывать рабочие элементы в потоке пользовательского интерфейса - это плохо, если только это не произведение пользовательского интерфейса (то есть обновление содержимого элемента управления или что-то еще), и в этом случае Control .BeginInvoke () будет достаточным.

12
ответ дан OJ. 28 August 2018 в 11:09
поделиться
  • 1
    Это да или нет? И как WinForms взаимодействуют в этом? – David Schmitt 24 December 2009 в 11:20

Использовать фоновый рабочий поток, поскольку это сообщение о сообщении пользовательского интерфейса, Эта статья MSDN , хотя в основном о WPF указывает, что BWT является интерфейсом пользователя, даже для оконных форм.

1
ответ дан Preet Sangha 28 August 2018 в 11:09
поделиться

Взгляните на backgrounder и посмотрите, подходит ли оно вашим потребностям.

0
ответ дан rosenfield 28 August 2018 в 11:09
поделиться
  • 1
    A) backgrounder - GPL. Поэтому это полезно только для проектов, которые также являются GPL. B) Если вы используете .NET 4 или более позднюю версию, эта функциональность встроена в API. См. System.Threading.Tasks.Task . – Hank Schultz 3 November 2015 в 17:57

У меня была аналогичная проблема с использованием класса зависимостей Oracle, который работает в своем потоке в Winforms,

Когда событие OnChange запускалось из Oracle Dependency, я хотел показать изменения в DataGridView, просто установив DataSource в eventargs. Подробности (который по существу является DataTable), и он бросает: System.InvalidOperationException не был обработан кодом пользователя. Message = Работа с кросс-потоками недействительна: Control 'dataGridView1' обращается к потоку, отличному от потока, на котором он был создан.

Пользователь StackOverflow Брайан Пейрис (bpeiris@gmail.com), мой коллега показал мне так:

void dep_OnChange(object sender, OracleNotificationEventArgs arg)
         {
         Console.WriteLine("Notification received");

         int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString());
         InfoSum x = (InfoSum)infoSum;
         foreach (DataRow dr in arg.Details.Rows)
            {
            Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x)));
            Console.WriteLine(string.Format("ontable={0}  Rowid={1},info={2}", dr.Field<string>("ResourceName"), dr.Field<string>("rowid"), dr.Field<Int32>("info")));
            }
         // Following  will throw cross-thread 
         // dataGridView1.DataSource = arg.Details;
         // instead of line above use the following
         dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details));
         IsNotified = true;
         }

      }
1
ответ дан TonyP 28 August 2018 в 11:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: