【问题标题】:updating a wpf window property from different thread从不同的线程更新 wpf 窗口属性
【发布时间】:2015-12-24 13:14:35
【问题描述】:

不幸的是,我不得不在与主应用程序不同的线程上触发此窗口。然后,每当我的 MakeWindow 类中的字符串输入 _html 发生更改时,我都会尝试对其进行更新。

现在我收到一条错误消息,说我无法执行此操作,因为我是从创建它的不同线程调用它。我怎样才能让它在这里工作?

有没有办法在窗口存在的同一线程上实现该事件侦听器?请记住,_html 将始终在主线程上设置和更新。想法?

谢谢!

我的课程:

namespace Windamow
{
public static class WebBrowserUtility
{
    public static readonly DependencyProperty BindableSourceProperty =
                           DependencyProperty.RegisterAttached("BindableSource", typeof(string),
                           typeof(WebBrowserUtility), new UIPropertyMetadata(null,
                           BindableSourcePropertyChanged));

    public static string GetBindableSource(DependencyObject obj)
    {
        return (string)obj.GetValue(BindableSourceProperty);
    }

    public static void SetBindableSource(DependencyObject obj, string value)
    {
        obj.SetValue(BindableSourceProperty, value);
    }

    public static void BindableSourcePropertyChanged(DependencyObject o,
                                                     DependencyPropertyChangedEventArgs e)
    {
        var webBrowser = (WebBrowser)o;
        webBrowser.NavigateToString((string)e.NewValue);
    }
}


public class Windamow
{

    private DynamoWindow window;
    public static string html;

    internal void ThreadProc()
    {
        string appName = "";
        try
        {
            appName = Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location);

            const string IE_EMULATION = @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";

            using (var fbeKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(IE_EMULATION, true))
            {

                fbeKey.SetValue(appName, 9000, Microsoft.Win32.RegistryValueKind.DWord);

            }
        }

        catch (Exception ex)
        {
            MessageBox.Show(appName + "\n" + ex.ToString(), "Unexpected error setting browser mode!");
        }

        window = new DynamoWindow();
        window.ShowDialog();
    }

    internal Windamow()
    {
        Thread t = new Thread(ThreadProc);
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }

    /// <summary>
    /// asd
    /// </summary>
    /// <param name="launch"></param>
    /// <param name="_html"></param>
    /// <returns></returns>
    public static DynamoWindow MakeWindow(bool launch, string _html)
    {
        if (launch)
        {
            if (MyProject.Utility.WebBrowserUtility.IsWindowOpen<Window>("MainWindow"))
            {
                html = _html;
                return null;
            }
            else
            {
                html = _html;
                Windamow mow = new Windamow();
                return mow.window;
            } 
        }
        else
        {
            return null;
        }
    }
}
}

xaml:

<Window x:Class="Windamow.DynamoWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d"
         xmlns:ns="clr-namespace:Windamow"
         Title="MainWindow"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <!--<WebBrowser x:Name="browser" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>-->
    <WebBrowser x:Name="browser" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ns:WebBrowserUtility.BindableSource="{Binding ReportPage}"/>
</Grid>

xaml.cs:

namespace Windamow
{
/// <summary>
/// Interaction logic for DynamoWindow.xaml
/// </summary>
public partial class DynamoWindow : Window
{
    public DynamoWindow()
    {
        InitializeComponent();
    }
}
}

【问题讨论】:

  • 你为什么要生成一个新线程只是为了打开另一个窗口,一个模态,无论如何?即使是STA。我不明白这一点,这可以说是糟糕的设计
  • @Roy 我正在从另一个应用程序运行它。这就是为什么我需要将它们保留在单独的线程上,因为我想让用户继续在主应用程序中工作。
  • @Roy 如果有办法通过主线程的非模式窗口来实现这一点,那么我非常乐意听取任何建议。提前致谢
  • @Konrad,我不确定一切。谁拥有 ReportPage 属性?它是从另一个线程修改的吗?如果是,您的问题的答案可能是将“webbrowser.Navigate”行更改为“webBrowser.Dispatcher.Invoke(() => { webBrowser.NavigateToString((string)e.NewValue);})”问候跨度>

标签: c# wpf multithreading xaml thread-safety


【解决方案1】:

我上传了一个示例,提取并运行: Browser

它还包含 WinA、WinB 窗口。您可以通过更改 App.xaml 来运行 WinA。并查看 WinA 如何访问/设置 WinB 控件/属性。

  1. 由于您发布了一半代码,因为没有代码说明您如何调用 MakeWindow 函数,以及您决定如何绑定和更改 ReportPage 属性。所以,我制定了一个可行的解决方案。见下图:

  1. 我已经更改了你的 MakeWindow 函数。

  2. 我对您的 DynamoWindow 代码隐藏做了一些补充。

主窗口

<Window x:Class="Windamow.Win32803621"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Win32803621" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="42*"/>
            <RowDefinition Height="79*"/>
            <RowDefinition Height="149*"/>
        </Grid.RowDefinitions>
        <Button x:Name="btnCreateAndOpen" Content="Create browser on new thread" HorizontalAlignment="Left" Margin="67,10,0,0" VerticalAlignment="Top" Click="btnCreateAndOpen_Click"/>
        <TextBox x:Name="tbHtmlInput" HorizontalAlignment="Left" Height="59" Margin="10,10,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="272" AcceptsReturn="True" AcceptsTab="True"/>
        <Button x:Name="btnUpdateBrowser" Content="Update browser" HorizontalAlignment="Left" Margin="88,10,0,0" Grid.Row="2" VerticalAlignment="Top" Width="98" Click="btnUpdateBrowser_Click"/>

    </Grid>
</Window>

MainWindow 代码隐藏

using System;
using System.Windows;
using System.Windows.Controls;

namespace Windamow
{
    /// <summary>
    /// Interaction logic for Win32803621.xaml
    /// </summary>
    public partial class Win32803621 : Window
    {
        public Win32803621()
        {
            InitializeComponent();
            tbHtmlInput.Text = "<a href='http://www.stackoverflow.com'>Click to open StackOverflow.com</a>";
        }

        DynamoWindow browserWindow;

        private void btnCreateAndOpen_Click(object sender, RoutedEventArgs e)
        {
            Windamow.MakeWindow(true, tbHtmlInput.Text);
        }

        private void btnUpdateBrowser_Click(object sender, RoutedEventArgs e)
        {
            browserWindow = Windamow.window;
            browserWindow.ReportPage = tbHtmlInput.Text;
        }
    }
}

用于创建线程/窗口的助手类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Threading;
using System.IO;

namespace Windamow
{
    public static class WebBrowserUtility
    {
        public static readonly DependencyProperty BindableSourceProperty =
                               DependencyProperty.RegisterAttached("BindableSource", typeof(string),
                               typeof(WebBrowserUtility), new UIPropertyMetadata(null,
                               BindableSourcePropertyChanged));

        public static string GetBindableSource(DependencyObject obj)
        {
            return (string)obj.GetValue(BindableSourceProperty);
        }

        public static void SetBindableSource(DependencyObject obj, string value)
        {
            obj.SetValue(BindableSourceProperty, value);
        }

        public static void BindableSourcePropertyChanged(DependencyObject o,
                                                         DependencyPropertyChangedEventArgs e)
        {
            WebBrowser webBrowser = (WebBrowser)o;
            webBrowser.NavigateToString((string)e.NewValue);
        }
    }


    public class Windamow
    {
        public static DynamoWindow window;

        public static string html;

        internal void ThreadProc()
        {
            string appName = "";
            try
            {
                appName = Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location);

                const string IE_EMULATION = @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";

                using (var fbeKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(IE_EMULATION, true))
                {

                    fbeKey.SetValue(appName, 9000, Microsoft.Win32.RegistryValueKind.DWord);

                }
            }

            catch (Exception ex)
            {
                MessageBox.Show(appName + "\n" + ex.ToString(), "Unexpected error setting browser mode!");
            }

            window = new DynamoWindow();
            window.ShowDialog();
        }

        internal Windamow()
        {
            Thread t = new Thread(ThreadProc);
            t.SetApartmentState(ApartmentState.STA);
            t.Start();
        }

        /// <summary>
        /// asd
        /// </summary>
        /// <param name="launch"></param>
        /// <param name="_html"></param>
        /// <returns></returns>
        public static void MakeWindow(bool launch, string _html)
        {
            Windamow mow = new Windamow();
            //return mow.window;

            //if (launch)
            //{
            //    if (MyProject.Utility.WebBrowserUtility.IsWindowOpen<Window>("MainWindow"))
            //    {
            //        html = _html;
            //        return null;
            //    }
            //    else
            //    {
            //        html = _html;
            //        Windamow mow = new Windamow();
            //        return mow.window;
            //    }
            //}
            //else
            //{
            //    return null;
            //}
        }
    }
}

DynamoWindow 代码隐藏类:

namespace Windamow
{
    /// <summary>
    /// Interaction logic for DynamoWindow.xaml
    /// </summary>
    public partial class DynamoWindow : Window, INotifyPropertyChanged
    {
        private String _reportPage;
        public String ReportPage { get { return _reportPage; } set { _reportPage = value; OnPropertyChanged("ReportPage"); } }

        public DynamoWindow()
        {
            InitializeComponent();

            browser.DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(String propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多