从另一个类和独立线程更改WPF主窗口标签

8

我正在开发一个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(() =>{..........或其他方法来解决这个问题吗?

编辑: 我还需要从不同的类和单独的线程中调用此标签更改操作。

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 return error因为不同的线程拥有该对象,所以调用线程无法访问此对象。

请指导我,

谢谢。


也许是因为这个问题已经被回答了一百次。通过搜索,你可以找到正确的解决方案。 - DHN
5个回答

25

我解决了我的问题,希望有人会用得到。但不知道这是否是最优化的方法。

在我的 mainWindow.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";

这对我来说很有效。 您可以在此处找到更多细节:从另一个类和单独的线程更改WPF窗口标签内容

干杯!


2
尝试以下代码片段:
status_lable.Dispatcher.Invoke(...)

1
谢谢您的回复。但是我的问题是由于不同的线程引起的。 - iJay
不理解你的意思。你想在单独的线程中执行吗?如果你希望更加响应,请使用 status_lable.Dispatcher.BeginInvoke(...) 或者 Task.Factory.StartNew(()=> {status_lable.Dispatcher.BeginInvoke(...);});。 - David
但是我的错误返回来自于 var mainWin 这一行,我能通过其他方式访问 mainWindow 的 status_label 吗?我编辑了问题,你能帮忙检查一下吗? - iJay
1
实际上这是两个问题。你把它们混在一起了,但你的标题是关于线程访问的。我认为我已经回答了你关于线程访问的问题。至于访问status_label,你必须提供一个函数或属性,以便你可以在mainwin类中访问它。例如,在你的用户控件类(其中包含status_label)中,你可以提供一个名为StatusCtrl的属性来提供公共/内部访问。 - David

2

感谢这些答案,它们指引了我正确的方向。最终,我得到了这个简单的解决方案:

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;
        }));
    }

当通过命名管道接收到消息时,此代码可显示最小化的窗口。


1

谢谢!我最终得到了一个略有不同的解决方案,但你的答案确实指引了我正确的方向。

对于我的应用程序,我在主窗口中有很多控件,大部分方法调用都发生在主窗口的范围内,因此在MainWindow.xaml.cs中使用默认的{ get; set }(或者只是在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代码后台,我只是像简单的单线程应用程序一样与控件交互(在我的情况下,子线程无法控制父线程)。但是我可以通过以下方式从父线程控制主线程:

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

        }

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

0

不使用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.
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接