5

次の例外が発生しました: InvalidOperationException : 別のスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません。

所有者とは別のスレッドでビルドされたウィンドウの所有者を設定しようとすると。

適切なスレッドからしか UI オブジェクトを更新できないことはわかっていますが、別のスレッドからのものである場合、所有者を設定できないのはなぜですか? 別の方法でできますか?入力エントリを持つことができるのは進行状況ウィンドウだけにしたいです。

これは、バグが発生するコードの部分です。

    public partial class DlgProgress : Window
{
    // ******************************************************************
    private readonly DlgProgressModel _dlgProgressModel;

    // ******************************************************************
    public static DlgProgress CreateProgressBar(Window owner, DlgProgressModel dlgProgressModel)
    {
        DlgProgress dlgProgressWithProgressStatus = null;
        var listDlgProgressWithProgressStatus = new List<DlgProgress>();
        var manualResetEvent = new ManualResetEvent(false);
        var workerThread = new ThreadEx(() => StartDlgProgress(owner, dlgProgressModel, manualResetEvent, listDlgProgressWithProgressStatus));
        workerThread.Thread.SetApartmentState(ApartmentState.STA);
        workerThread.Start();
        manualResetEvent.WaitOne(10000);
        if (listDlgProgressWithProgressStatus.Count > 0)
        {
            dlgProgressWithProgressStatus = listDlgProgressWithProgressStatus[0];
        }

        return dlgProgressWithProgressStatus;
    }

    // ******************************************************************
    private static void StartDlgProgress(Window owner, DlgProgressModel progressModel, ManualResetEvent manualResetEvent, List<DlgProgress> listDlgProgressWithProgressStatus)
    {
        DlgProgress dlgProgress = new DlgProgress(owner, progressModel);
        listDlgProgressWithProgressStatus.Add(dlgProgress);
        dlgProgress.ShowDialog();
        manualResetEvent.Set();
    }

    // ******************************************************************
    private DlgProgress(Window owner, DlgProgressModel dlgProgressModel)
    {
        if (owner == null)
        {
            throw new ArgumentNullException("Owner cannot be null");
        }

        InitializeComponent();
        this.Owner = owner; // Can't another threads owns it exception
4

4 に答える 4

2

主にハンス・パッサンの提案に基づいて作成しました。重要なことですが、私は IntPtr で "ToInt32" を使用しているため、このコードは 32 ビットでしか機能しないはずです。

これはコードです:

WindowHelper 関数:

        // ******************************************************************
    private const int GWL_HWNDPARENT = -8; // Owner --> not the parent

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

    // ******************************************************************
    public static void SetOwnerWindow(Window owned, IntPtr intPtrOwner)
    {
        try
        {
            IntPtr windowHandleOwned = new WindowInteropHelper(owned).Handle;
            if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
            {
                SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
            }
        }
        catch (Exception ex)
        {
            Debug.Print(ex.Message);
        }
    }

    // ******************************************************************

呼び出し機能:

  using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
using HQ.Util.General.Threading;
using HQ.Util.Unmanaged;

namespace HQ.Wpf.Util.Dialog
{
    /// <summary>
    /// Interaction logic for DlgProgressWithProgressStatus.xaml
    /// </summary>
    public partial class DlgProgress : Window
    {
        // ******************************************************************
        private readonly DlgProgressModel _dlgProgressModel;

        // ******************************************************************
        public static DlgProgress CreateProgressBar(Window owner, DlgProgressModel dlgProgressModel)
        {
            DlgProgress dlgProgressWithProgressStatus = null;
            var listDlgProgressWithProgressStatus = new List<DlgProgress>();
            var resetEvent = new ManualResetEvent(false);

            IntPtr windowHandleOwner = new WindowInteropHelper(owner).Handle;
            dlgProgressModel.Owner = owner;
            dlgProgressModel.IntPtrOwner = windowHandleOwner;

            var workerThread = new ThreadEx(() => StartDlgProgress(dlgProgressModel, resetEvent, listDlgProgressWithProgressStatus));
            workerThread.Thread.SetApartmentState(ApartmentState.STA);
            workerThread.Start();
            resetEvent.WaitOne(10000);
            if (listDlgProgressWithProgressStatus.Count > 0)
            {
                dlgProgressWithProgressStatus = listDlgProgressWithProgressStatus[0];
            }

            return dlgProgressWithProgressStatus;
        }

        // ******************************************************************
        private static void StartDlgProgress(DlgProgressModel progressModel, ManualResetEvent resetEvent, List<DlgProgress> listDlgProgressWithProgressStatus)
        {
            DlgProgress dlgProgress = new DlgProgress(progressModel);
            listDlgProgressWithProgressStatus.Add(dlgProgress);
            resetEvent.Set();
            dlgProgress.ShowDialog();
        }

        // ******************************************************************
        private DlgProgress(DlgProgressModel dlgProgressModel)
        {
            if (dlgProgressModel.Owner == null)
            {
                throw new ArgumentNullException("Owner cannot be null");
            }

            InitializeComponent();
            // this.Owner = owner; // Can't another threads owns it exception

            if (dlgProgressModel == null)
            {
                throw new ArgumentNullException("dlgProgressModel");
            }

            _dlgProgressModel = dlgProgressModel;
            _dlgProgressModel.Dispatcher = this.Dispatcher;
            _dlgProgressModel.PropertyChanged += _dlgProgressModel_PropertyChanged;
            DataContext = _dlgProgressModel;
        }

        // ******************************************************************
        // Should be call as a modal dialog
        private new void Show()
        {
            throw new Exception("Should only be used as modal dialog");
        }

        // ******************************************************************
        void _dlgProgressModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {

            //          if (e.PropertyName == "IsJobCanceled" || e.PropertyName == "IsJobCompleted" || e.PropertyName == "IsProgressCompleted")
            // Faster if we don't check strings and check condition directly 
            {
                if (_dlgProgressModel.HaveConditionToClose())
                {
                    if (_dlgProgressModel.IsJobCanceled == true)
                    {
                        SetDialogResult(false);
                    }
                    else
                    {
                        SetDialogResult(true);
                    }
                }
            }
        }

        // ******************************************************************
        private void SetDialogResult(bool result)
        {
            this._dlgProgressModel.Dispatcher.BeginInvoke(new Action(() =>
                {
                    this.DialogResult = result;
                }), DispatcherPriority.Background);
        }

        // ******************************************************************
        private bool _isFirstTimeLoaded = true;

        private Timer _timer = null;
        // ******************************************************************
        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            if (_isFirstTimeLoaded)
            {
                WindowHelper.SetOwnerWindow(this, _dlgProgressModel.IntPtrOwner);
                Dispatcher.BeginInvoke(new Action(ExecuteDelayedAfterWindowDisplayed), DispatcherPriority.Background);
                _isFirstTimeLoaded = false;

                if (_dlgProgressModel.FuncGetProgressPercentageValue != null)
                {
                    TimerCallback(null);
                    _timer = new Timer(TimerCallback, null, _dlgProgressModel.MilliSecDelayBetweenCall, _dlgProgressModel.MilliSecDelayBetweenCall);
                }
            }
        }

        // ******************************************************************
        private void TimerCallback(Object state)
        {
            Dispatcher.BeginInvoke(new Action(() =>
                {
                    _dlgProgressModel.ValueCurrent = _dlgProgressModel.FuncGetProgressPercentageValue();
                }));
        }

        // ******************************************************************
        private void ExecuteDelayedAfterWindowDisplayed()
        {
            if (_dlgProgressModel._actionStarted == false)
            {
                _dlgProgressModel._actionStarted = true;
                Task.Factory.StartNew(ExecuteAction);
            }
        }

        // ******************************************************************
        private void ExecuteAction()
        {
            _dlgProgressModel.ExecuteAction();
            _dlgProgressModel._actionTerminated = true;
            _dlgProgressModel.IsJobCompleted = true;
        }

        // ******************************************************************
        private void CmdCancel_Click(object sender, RoutedEventArgs e)
        {
            this._dlgProgressModel.IsJobCanceled = true;
        }

        // ******************************************************************
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (! _dlgProgressModel.HaveConditionToClose())
            {
                e.Cancel = true;
                return;
            }

            WindowHelper.SetOwnerWindow(this, 0);

            this.CmdCancel.IsEnabled = false;
            this.CmdCancel.Content = "Canceling...";
            this._dlgProgressModel.Dispose();
        }

        // ******************************************************************
    }
}
于 2013-02-15T16:04:17.447 に答える
-1

所有者を設定することではありません。別のスレッドから WPF でコントロールを操作する場合は、デリゲートを作成し、これをコントロールのディスパッチャーに渡す必要があります。

if(Control.Dispatcher.CheckAccess())
{
    //The control can be accessed without using the dispatcher.
    Control.DoSomething();
}
else{
     //The dispatcher of the control needs to be informed
     MyDelegate md = new MyDelegate( delegate() { Control.DoSomething(); });
     Control.Dispatcher.Invoke(md, null);
}

この投稿を参照してください。

于 2013-02-15T13:52:58.140 に答える