使用来自其他线程的WPF扩展工具包MessageBox和好奇的行为

时间:2014-08-14 00:10:45

标签: wpf multithreading messagebox wpftoolkit

我在使用其他线程的WPF扩展工具包(版本2.1.0.0)MessageBox时遇到问题。命名空间是:Xceed.Wpf.Toolkit.MessageBox

我用Toolkit MessageBox替换了我的常规MessageBoxs(System.Windows.MessageBox),并在从另一个线程启动时遇到错误。 System.Windows.MessageBox没有这样的问题。我看到这个帖子报告了问题,但似乎没有后续行动:

https://wpftoolkit.codeplex.com/workitem/21046

我猜测有一个解决方法。这里展示了一个显示问题的例子,但这是我的简单示例:

首先,我包装Toolkit.MessageBox。我之所以这样做主要是因为我正在应用风格(虽然我已经评论说明了这不是问题)

public class CustomMessageBox
{
    //static DummyUserControl1 _ctrl = new DummyUserControl1();

    public static MessageBoxResult Show(string msgText, Style styleArg = null)
    {
        Cursor saveCursor = Mouse.OverrideCursor;
        Mouse.OverrideCursor = null;

        //Style style = styleArg != null ? styleArg : _ctrl.FindResource("MessageBoxStyle1") as Style;
       // MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK, style);

        MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK);
        Mouse.OverrideCursor = saveCursor;
        return result;
    }


}

主窗口上面只有两个按钮,后面是代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }



    private void btnMainThreadMsgBox_Click(object sender, RoutedEventArgs e)
    {
        CustomMessageBox.Show("Hello on main thread");
    }

    private void btnAltThreadMsgBox_Click(object sender, RoutedEventArgs e)
    {

        Thread altThread1 = new Thread(new ThreadStart(AltThread1Proc));

        altThread1.SetApartmentState(ApartmentState.STA);
        altThread1.Priority = ThreadPriority.AboveNormal;
        altThread1.IsBackground = true;

        altThread1.Start();
    }

    public void AltThread1Proc()
    {
        MessageBox.Show("Hello on Alt Thread");
        CustomMessageBox.Show("Hello on alt thread");

    }
}

使用CustomMessageBox.Show(...)在AltThreadProc()中出现问题。我提到的奇怪的行为是这样的:如果你点击主要的thead按钮,然后点击Alt线程按钮,你会收到错误:

无法访问Freezable' System.Windows.Media.SolidColorBrush'跨线程,因为它不能被冻结。

但是,如果您跳过主线程按钮并且只需点击Alt线程按钮,则会收到错误: 调用线程无法访问此对象,因为另一个线程拥有它。

我很好奇" Freezable"错误就是为什么你可以根据看似无害的事件得到不同的错误:点击/不点击在主线程上产生消息框的按钮。

理想情况下,将System.Windows.MessageBox替换为Xceed.Wpf.Toolkit.MessageBox会很好,但是如果要编写某种额外的代码,这可能是可以接受的。文档和我提供的链接提示使用WindowContainer,但我真的看不到你如何做到这一点的任何例子。我被Toolkit MessageBox所吸引,因为它允许用MessageBox做一些很酷的东西(我不会在这里展示),例如应用样式,更改OK,CANCEL按钮的文本等。

非常感谢任何想法。

谢谢, 戴夫


额外信息: 如果您只有一个窗口,User1341210建议很有效。但是,如果你在自己的主题中有第二个窗口,它就不能很好地工作。也许有人可以告诉我我做错了什么。我使用TaskScheduler的建议,但如果使用的TaskScheduler是第二个窗口之一,则代码会抛出异常。也就是说,如果我使用第一个窗口的TaskScheduler,一切正常,但如果我使用第二个窗口的TaskScheduler则抛出异常。这是我的第二个窗口背后的代码:

public partial class AltThreadWindow : Window
{
    private TaskScheduler _ui;


    public AltThreadWindow()
    {
        InitializeComponent();
        _ui = TaskScheduler.FromCurrentSynchronizationContext();

    }

    // This constructor is for passing in the TaskScheduler of the mainwindow and works great
    public AltThreadWindow(TaskScheduler scheduler)
    {
        InitializeComponent();
        _ui = scheduler;
    }

    private void btnWindowsMsgBox_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Standard Windows message box");
    }

    private void btnCustomMsgBox_Click(object sender, RoutedEventArgs e)
    {
        MessageBoxResult result;
        Task.Factory.StartNew(() => { result = CustomMessageBox.Show("Custom MessageBox on separate window"); }, CancellationToken.None,
            TaskCreationOptions.None,
            _ui);
    }


}

注意两个构造函数。默认值为第二个窗口分配TaskScheduler。另一个构造函数允许一个进入主窗口的TaskScheduler 这是我用来从主窗口启动第二个窗口的代码。我再次在另一个线程上启动第二个窗口,然后传入主窗口的TaskScheduler。最好使用第二个窗口的TaskScheduler。

 _altWindowThread = new Thread(new ThreadStart(AltWinThreadProc));
        _altWindowThread.SetApartmentState(ApartmentState.STA);
        _altWindowThread.Priority = ThreadPriority.AboveNormal;
        _altWindowThread.IsBackground = true;

        _altWindowThread.Start();

实际的threadproc:

[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
    public void AltWinThreadProc()
    {
        // Create our context, and install it:
        SynchronizationContext.SetSynchronizationContext(
            new DispatcherSynchronizationContext(
                Dispatcher.CurrentDispatcher));

        _altWindow = new AltThreadWindow(_ui);
        _altWindow.Show();
        System.Windows.Threading.Dispatcher.Run();

    }

请注意,我传入了MainWindow的TaskScheduler。

1 个答案:

答案 0 :(得分:2)

我们在应用程序中遇到了同样的问题(我在codeplex上创建了工作项)。 错误消息非常混乱,我无法为您提供答案。

可是:

我们没有使用单独的WindowContainer来解决它。而是通过UI调度程序调用单独的任务/线程:

Task.Factory.StartNew(
            () => { result = CustomMessageBox.Show(messageText); },
            CancellationToken.None,
            TaskCreationOptions.None,
            _ui);

在从UI上下文执行的方法中分配_ui(例如,窗口/控件的构造函数:

_ui = TaskScheduler.FromCurrentSynchronizationContext();

希望这有助于解决"用Xceed.Wpf.Toolkit.MessageBox替换System.Windows.MessageBox"你的问题的一部分。


如果您希望消息框显示在另一个窗口上,您必须设置"所有者"消息框的属性到另一个窗口。

最好的问候。