Владелец набора WPF для окна, созданного в собственном потоке пользовательского интерфейса - PullRequest
3 голосов
/ 06 октября 2010

У меня есть следующий код, который запускает окно WPF в своем собственном потоке пользовательского интерфейса:

// Create the dedicated UI thread for AddEditPair window
Thread addEditPairThread = new Thread(() =>
{
    // Initialise the add edit pair window
    addEditPair = new AddEditPair(this);
    addEditPair.PairRecordAdded += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordAdded);
    addEditPair.PairRecordEdited += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordEdited);

    // Force AddEditPair to run on own UI thread
    System.Windows.Threading.Dispatcher.Run();
});
addEditPairThread.IsBackground = true;
addEditPairThread.Name = "AddEditPair";
addEditPairThread.SetApartmentState(ApartmentState.STA);
addEditPairThread.Start();

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

Исключение, которое я получаю:

The calling thread cannot access this object because a different thread owns it.

Я понимаю, что означает ошибка и почему она происходит, поэтому я реализовал следующее:

// If invoke is not required - direct call
if (addEditPair.Dispatcher.CheckAccess())
    method();
// Else if invoke is required - invoke
else
    addEditPair.Dispatcher.BeginInvoke(dispatcherPriority, method);

Но я все еще получаю ту же ошибку. Теперь я в замешательстве!

Есть идеи у кого-нибудь? Любая помощь будет оценена.

Ответы [ 4 ]

4 голосов
/ 06 октября 2010

Почему вы создаете окна в отдельном потоке?

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

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

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

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

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

  • Поэтому я не должен выполнять блокировку или длительные операции из окна, а использовать фоновый поток.

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

  • А так как окна в одном потоке не будут мешать друг другу, а многопоточность усложняет работу, то нет причин иметь больше одного потока пользовательского интерфейса.

Примечание: только один поток пользовательского интерфейса, который фактически отображает пользовательский интерфейс, очень хорошо иметь фоновые потоки, использующие WPF, которые никогда не откроют окно (пример: создайте большой FixedDocument в фоновом режиме). Я не вызываю эти потоки пользовательского интерфейса, Также я ничего не сказал о количестве фоновых тем.

1 голос
/ 25 февраля 2016

Если вы действительно хотите установить владельца в другом потоке, чем вы должны использовать функцию user32.

     [DllImport("user32.dll")]
     static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

  public static void SetOwnerWindowMultithread(IntPtr windowHandleOwned, IntPtr intPtrOwner)
  {
            if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
            {
                SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
            }
 }

Код для получения обработчика WPF:

 public static IntPtr GetHandler(Window window)
        {
            var interop = new WindowInteropHelper(window);
            return interop.Handle;
        }

Обратите внимание, что окно должнобыть инициализированным перед вызовом многопоточного владельца!

 var handler = User32.GetHandler(ownerForm);

        var thread = new Thread(() =>
        {
                var window = new DialogHost();
                popupKeyboardForm.Show();
                SetOwnerWindowMultithread(GetHandler(popupKeyboardForm), handler);
                Dispatcher.Run();
        });

        thread.IsBackground = true;
        thread.Start();

Ps Также можно использовать SetParent:

[DllImport("user32.dll", SetLastError = true)]
            static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
0 голосов
/ 07 октября 2010

Кажется, что-то вроде проверки происходит в свойстве Owner окна, которое проверяет, были ли окна созданы в одном потоке.

Мое решение для преодоления этого заключается в реализации моего собственного свойства типа Main Window и сохранении ссылки на него из конструктора следующим образом:

private MainWindow _main;

public AddEditPair(MainWindow main)
    : base(false)
{
    InitializeComponent();

    // Initialise local variables
    _main = main;
}

public MainWindow Main
{
    get
    {
        return _main;
    }
}

Теперь у меня есть доступ к главному окну.

0 голосов
/ 06 октября 2010

Попытка установить одно окно как родительское окно для другого потока невозможна в WPF без замораживания окон (что, я не уверен, возможно), так как каждое из окон не сможет получить доступ к другие данные.

Есть ли веская причина для создания окон на отдельных потоках? В основном вам следует хорошо создавать окна в одних и тех же потоках пользовательского интерфейса и использовать фоновые рабочие для обработки длительных операций.

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