Click here to Skip to main content
15,887,337 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi guys.
For example... we have a Label in MainWindow.
Its name is : myLbl
I know that if I want to change the text of it inside the MainWindow class, it is like this:
VB
myLbl.Content = "How are you?"


So I have another wpf window in my project, which name is MySecondWindow
So how can I change the content of myLbl from MySecondWindow class?

(I am using vb.net. Please kindly write the code in vb.net)

What I have tried:

I tried this, but no luck.
VB
Mainwindow.myLbl.Content = "Hi from second window."
Posted
Updated 30-Jun-23 1:30am

One issue that can rear it's ugly head is memory leaks. When you tightly couple objects together, it is easy to forget to release unused objects.

A method to overcome this is to use a proxy class to share data. Typically, this is done with DI (Dependency Injection). When not using DI, an alternative is to use a shared class.
C#
public static class SharedDataProxy
{
    public static void PostMessage(string message)
    {
        ReceiveMessage?.Invoke(message);
    }

    public static Action<string> ReceiveMessage { private get; set; } = null;
}

In this example, I am going to pass a message string from the main window to a side window. I have purposely started each window separately sp neither are linked / tightly coupled.

1. Main Window Xaml (sender):
XML
<Window x:Class="WpfSharingDataAcrossWindows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Message:" Padding="0 0 10 0"/>
            <TextBox x:Name="TextMessage" Width="200"/>
        </StackPanel>
        <Button Grid.Row="1"
                Content="SEND"
                HorizontalAlignment="Left"
                Margin="0 10"
                Padding="20 10"
                Click="ButtonBase_OnClick"/>
    </Grid>
</Window>

And the code-behind:
C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        SharedDataProxy.PostMessage(TextMessage.Text);
    }
}

So when the SEND button is clicked, the PostMessage on the SharedDataProxy is used to pass the data. The MainWindow is totally oblicious to who will get the data.

2. Side Window Xaml:
XML
<Window x:Class="WpfSharingDataAcrossWindows.SideWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SideWindow" Height="450" Width="800">
    <Grid>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Message:" Padding="0 0 10 0"/>
            <TextBlock x:Name="TextMessage"/>
        </StackPanel>
    </Grid>
</Window>

and the code-behind:
C#
public partial class SideWindow : Window
{
    public SideWindow()
    {
        InitializeComponent();
        SharedDataProxy.ReceiveMessage = ShowMessage;
    }

    public void ShowMessage(string message)
    {
        TextMessage.Text = message;
    }
}

Here we register the method to be used to show the message in the constructor to the ReceiveMessage of the SharedDataProxy class. So this is the listener.

3. Now, for this demo, to launch each window separately when the app starts, we need to modify the App Startup in the App.xaml and App.xaml.cs files:

App.xaml:
XML
<Application
    x:Class="WpfSharingDataAcrossWindows.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />

We removed the default startup.

App.xaml.cs:
C#
public partial class App : Application
{
    private Window _sideWindow;

    protected override void OnStartup(StartupEventArgs e)
    {
        _sideWindow = new SideWindow();
        
        _sideWindow.Show();
        App.Current.MainWindow = new MainWindow();
        App.Current.MainWindow .Show();

        base.OnStartup(e);
    }
}


So the MainWindow button calls SharedDataProxy.PostMessage method, and that method invokes the ReceiveMessage delegate method that points to the SideWindow.ShowMessage method and the message is displayed.

Where this is useful is for testing code and also, if you need to change what window receives the message, the MainWindow is totally unaware.

This technique is not isolated to WPF, it can be used with all application types, like Winforms. Also, will work for both .Net Framework and Dot Net.
 
Share this answer
 
Comments
Andre Oosthuizen 30-Jun-23 11:17am    
This is the correct answer and methods to follow!
Sh.H. 30-Jun-23 14:19pm    
@Graeme_Grant Doesn't this make delay between clicking from window2 and appearing in window1?
Also doesn't this need something like Application.DoEvents ?
Graeme_Grant 30-Jun-23 20:32pm    
No, not at all.
Basically, you shouldn't try to do it directly, because you need two things:

1) The actual instance of the main window you need to change,
2) A Public label to change.

And both of those are either difficult to get, or totally against OOPs principles.

Exactly how depends on the "relationship" between the two forms.
Have a look at these, one of them will fit your circumstances.
The form that creates an instance of another:
C#
MyForm mf = new MyForm();
mf.Show();
Is the "parent", the other form is the "child".
(This doesn't imply any formal MDI relationship)

Transferring information between two forms, Part 1: Parent to Child[^]
Transferring information between two forms, Part 2: Child to Parent[^]
Transferring information between two forms, Part 3: Child to Child[^]

The second option is probably the one you want.
 
Share this answer
 
To do this, you need to pass a reference to the 'MainWindow' instance to the 'MySecondWindow' class -

In your 'MainWindow' -
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    // Method to change the label content
    public void ChangeLabelContent(string newText)
    {
        myLbl.Content = newText;
    }

    private void OpenSecondWindowButton_Click(object sender, RoutedEventArgs e)
    {
        // Instance of MySecondWindow, pass a reference to the current instance of MainWindow
        MySecondWindow secondWindow = new MySecondWindow(this);
        secondWindow.Show();
    }
}


In your 'MySecondWindow' -
public partial class MySecondWindow : Window
{
    private MainWindow mainWindow;

    public MySecondWindow(MainWindow mainWin)
    {
        InitializeComponent();

        // Store your reference to the MainWindow instance in a variable
        mainWindow = mainWin;
    }

    private void ChangeLabelContentButton_Click(object sender, RoutedEventArgs e)
    {
        // Call the ChangeLabelContent method of the mainWindow, pass the text over
        mainWindow.ChangeLabelContent("Hi from second window.");
    }
}


As mentioned in the comments, this might lead to memory leaks. An alternative approach is to use an event-driven approach or a messaging system to communicate between the windows. The alternative below decouples the two windows and reduces the risk of memory leaks, communication between the windows is now event-driven, and each window only needs to know about the event itself, not the specific implementation details of the other window/s.

The below alternative includes code for unsubscribing from events when the windows are closed, making sure that there are no hanging references that prevent the garbage collector from reclaiming memory associated with the closed window/s -
public partial class MyMainWindow : Window
{
    public event EventHandler<string> LabelContentChanged;

    public MyMainWindow()
    {
        InitializeComponent();
    }

    private void OpenSecondWindowButton_Click(object sender, RoutedEventArgs e)
    {
        MySecondWindow secondWindow = new MySecondWindow();
        secondWindow.LabelContentChanged += HandleLabelContentChanged;
        secondWindow.Closed += SecondWindowClosed;
        secondWindow.Show();
    }

    private void HandleLabelContentChanged(object sender, string newText)
    {
        myLbl.Content = newText;
    }

    private void SecondWindowClosed(object sender, EventArgs e)
    {
        MySecondWindow secondWindow = (MySecondWindow)sender;
        secondWindow.LabelContentChanged -= HandleLabelContentChanged;
        secondWindow.Closed -= SecondWindowClosed;
    }
}


public partial class MySecondWindow : Window
{
    public event EventHandler<string> LabelContentChanged;

    public MySecondWindow()
    {
        InitializeComponent();
    }

    private void ChangeLabelContentButton_Click(object sender, RoutedEventArgs e)
    {
        LabelContentChanged?.Invoke(this, "Hi from second window.");
    }
}
 
Share this answer
 
v2
Comments
Graeme_Grant 30-Jun-23 6:07am    
Not a good solution. This is tightly coupling the Windows together. Good way to teach memory leaks.
Andre Oosthuizen 30-Jun-23 6:32am    
Yip, you are correct, I have updated the solution with an alternative.
Graeme_Grant 30-Jun-23 6:40am    
You still have tight coupling. I will post an alternative that does not use MVVM or IOC as that may be too advanced for the OP. I have used this technique in the article: LogViewer Control for WinForms, WPF, and Avalonia in C# & VB[^]
Andre Oosthuizen 30-Jun-23 6:55am    
Great article, thanks. I might learn from this as well it seems, will work through it.
Graeme_Grant 30-Jun-23 7:31am    
posted.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900