Change WPF mainwindow label from another class and separate thread

17,014

Solution 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!!

Solution 2

try below snippet:

status_lable.Dispatcher.Invoke(...)

Solution 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.

Solution 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!

Share:
17,014
iJay
Author by

iJay

Software Engineer, enthusiastic C# and Google apps script developer. [email protected]

Updated on August 02, 2022

Comments

  • iJay
    iJay almost 2 years

    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

  • iJay
    iJay about 11 years
    Thanks for the response. but my issue is due to different thread.
  • David
    David about 11 years
    Don't understand what you mean.Do you wish to do so in separate thread? You can use status_lable.Dispatcher.BeginInvoke(...) or Task.Factory.StartNew(()=> {status_lable.Dispatcher.BeginInvoke(...);}); if you want to be more reponsive
  • iJay
    iJay about 11 years
    But my error return from var mainWin line, Can I access mainWindow's status_label by another way? I edited the question can u pls check it?
  • David
    David about 11 years
    These are two questions actually. You mixed them but your title is about threaded access. I think I have answered your question about threaded access. As for the accessing the status_label, you have to provide a function or property so that you can access it in your mainwin class. For instance, in your user control class (where the status_label is located), you can provide a property called StatusCtrl to offer public/internal access.