【问题标题】:Change WPF mainwindow label from another class and separate thread从另一个类更改 WPF 主窗口标签并分离线程
【发布时间】:2013-03-15 05:37:34
【问题描述】:

我正在开发 WPF 应用程序。我在MainWindow.xaml 中有一个名为“Status_label”的标签。我想从不同的类(signIn.cs)更改它的内容。 通常我能够做到这一点

var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.status_lable.Content = "Irantha signed in";

但我的问题是,当我尝试通过 signIn.cs 类中的不同线程访问它时,它会给出错误:

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

我可以使用Dispatcher.Invoke(new Action(() =&gt;{.......... 或其他方式解决这个问题吗?

编辑: 我将从不同的类以及单独的线程中调用此标签更改操作

MainWindow.xaml

<Label HorizontalAlignment="Left" Margin="14,312,0,0" Name="status_lable" Width="361"/>

SignIn.cs

    internal void getStudentAttendence()
    {
        Thread captureFingerPrints = new Thread(startCapturing);
        captureFingerPrints.Start();
    }

void mySeparateThreadMethod()
{
    var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
    mainWin.status_lable.Dispatcher.Invoke(new Action(()=> mainWin.status_lable.Content ="Irantha signed in"));
}

line var mainWin 返回错误The calling thread cannot access this object because a different thread owns it.

请指导我,

谢谢

【问题讨论】:

  • 为什么这被否决了?
  • 也许,因为这个问题已经被回答了一百次了。一些“谷歌搜索”将为您提供适当的解决方案。

标签: wpf dispatcher mainwindow


【解决方案1】:

我解决了我的问题,希望有人需要这个。但不知道这是否是优化的方式。

在我的 ma​​inWindow.xaml.cs 中:

    public  MainWindow()
    {
      main = this;
    }

    internal static MainWindow main;
    internal string Status
    {
        get { return status_lable.Content.ToString(); }
        set { Dispatcher.Invoke(new Action(() => { status_lable.Content = value; })); }
    }

来自我的 SignIn.cs

 MainWindow.main.Status = "Irantha has signed in successfully";

这对我来说很好。 您可以从这里找到更多详细信息,Change WPF window label content from another class and separate thread

干杯!!

【讨论】:

    【解决方案2】:

    试试下面的sn-p:

    status_lable.Dispatcher.Invoke(...)
    

    【讨论】:

    • 感谢您的回复。但我的问题是由于不同的线程。
    • 不明白你的意思。你想在单独的线程中这样做吗?您可以使用 status_lable.Dispatcher.BeginInvoke(...) 或 Task.Factory.StartNew(()=> {status_lable.Dispatcher.BeginInvoke(...);});如果你想反应更快
    • 但是我的错误从 var mainWin 行返回,我可以通过其他方式访问 mainWindow 的 status_label 吗?我编辑了问题,你可以检查一下吗?
    • 这实际上是两个问题。您将它们混合在一起,但您的标题是关于线程访问的。我想我已经回答了你关于线程访问的问题。至于访问 status_label,您必须提供一个函数或属性,以便您可以在 mainwin 类中访问它。例如,在您的用户控件类(status_label 所在的位置)中,您可以提供一个名为 StatusCtrl 的属性来提供公共/内部访问。
    【解决方案3】:

    多亏了答案,他们将我引向了正确的方向。我最终得到了这个简单的解决方案:

    public partial class MainWindow : Window
    {
        public static MainWindow main;
    
        public MainWindow()
        {
            InitializeComponent();                        
            main = this;
        }
    }
    

    然后在我的事件处理程序中运行在另一个线程中的另一个类中:

    internal static void pipeServer_MessageReceived(object sender, MessageReceivedEventArgs e)
        {
            MainWindow.main.Dispatcher.Invoke(new Action(delegate()
            {
                MainWindow.main.WindowState = WindowState.Normal;
            }));
        }
    

    当通过命名管道接收到 i 消息时显示最小化窗口。

    【讨论】:

      【解决方案4】:

      谢谢!我最终得到了一个稍微不同的解决方案,但你的回答肯定为我指明了正确的方向。

      对于我的应用程序,我在 main 中有很多控件,并且 main 上的大多数方法调用都发生在 main 范围内,因此使用默认的 { get; 更简单。在 MainWindow.xaml.cs 中设置 }(或仅在 XAML 中定义控件)。

      在我的父窗口的代码隐藏中,我在这样的单独线程中启动 MainWindow(简化示例)。关键是全局定义 main,即使它是在 Window_Loaded() 内部实例化的:

          public ParentWindow()
          {
              InitializeComponent();
          }
      
          MainWindow main;
      
          private void Window_Loaded(object sender, RoutedEventArgs e)
          {
              Thread otherThread = new Thread(() =>
              {
                  main = new MainWindow();
                  main.Show();
      
                  main.Closed += (sender2, e2) =>
                      main.Dispatcher.InvokeShutdown();
      
                  System.Windows.Threading.Dispatcher.Run();
              });
      
              otherThread.SetApartmentState(ApartmentState.STA);
              otherThread.Start();
      
          }
      

      然后在我的 MainWindow 代码隐藏中,我只是与控件进行交互,就好像它是一个简单的单线程应用程序一样(在我的情况下,子线程没有对父线程的控制)。但是,我可以像这样从父线程控制 main:

      private void button_Click(object sender, RoutedEventArgs e)
              {
                  main.Dispatcher.Invoke(new Action(delegate () 
                      {
                          main.myControl.myMethod(); 
                      }));
      
              }
      

      通过这种方式,我避免了在代码隐藏中定义所有内容以及在 MainWindow.xaml.cs 的代码隐藏中使用调度程序的复杂性。我的应用程序中只有几个地方可以从父窗口修改 main ,所以这对我来说更简单,但你的方法似乎同样有效。再次感谢!

      【讨论】:

        【解决方案5】:

        不使用Dispatcher.Invoke的简单技巧:在你的Window类中输入:

        public partial class MyWindow: Window
        {
            public static MyWindow mywin;
            [...]
        
            public MyWindow()
            {
                InitializeComponent();
                mywin = this;
                [...]
            }
        
            [...]
        }
        

        接下来在您的第二堂课中调用您需要添加窗口名称 + 您分配的标签的属性。这是一个例子:

        internal class MySecondClass
        {
            internal void ChangeAValue()
            {
                MyWindow.mywin.ATextBox.Text = "Some text"; // I'm changing for example the text for a textbox in MyWindow.
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-12-10
          • 1970-01-01
          • 1970-01-01
          • 2012-05-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多