Change WPF mainwindow label from another class and

2019-03-15 22:32发布

问题:

Im working on a WPF application. I have a label called "Status_label" in MainWindow.xaml. and I want to change its content from a different class (signIn.cs). Normally I'm able to do this

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

But my problem is,when I'm trying to access it via different thread in signIn.cs class, it gives an error:

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

Can I solve this by using Dispatcher.Invoke(new Action(() =>{.......... or something else?

EDIT: I'm gonna call this label change action from different class as-well-as separate thread

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 errorThe calling thread cannot access this object because a different thread owns it.

Please guide me,

Thank you

回答1:

I resolved my question, hope somebody will need this. But don't know whether this is the optimized way.

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

from my SignIn.cs class

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

This works fine for me. You can find more details from here, Change WPF window label content from another class and separate thread

cheers!!



回答2:

try below snippet:

status_lable.Dispatcher.Invoke(...)


回答3:

Thanks to the answers, they led me in the right direction. I ended up with this simple solution:

public partial class MainWindow : Window
{
    public static MainWindow main;

    public MainWindow()
    {
        InitializeComponent();                        
        main = this;
    }
}

Then in my eventhandler in another class that runs in a different thred:

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

This to show the minimized window when i message is received via a namedPipeline.



回答4:

Thank you! I wound up with a slightly different solution, but you definitely pointed me in the right direction with your answer.

For my application, I have a lot of controls in main, and most of the method calls on main were occurring from within the scope of main, so it was simpler to use the default { get; set } within MainWindow.xaml.cs (or to just define the controls in XAML).

In my parent window's code-behind, I launch the MainWindow in a separate thread like this (simplified example). The key is to define main globally, even though it is instantiated inside of 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();

    }

Then in my MainWindow code-behind, I just interact with the controls as though it is a simple single-threaded application (there is no control of the parent thread from the child thread in my case). I can, however, control main from the parent thread like this:

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

        }

By doing it this way, I avoid the complexity of defining everything in code-behind and using the dispatcher from within the code-behind of MainWindow.xaml.cs. There are only a few spots in my application where I modify main from the parent window, so this was simpler for me, but your approach seems equally valid. Thanks again!