Here is full answer for your question:
Main window xaml code
<Window x:Class="TwoWindowsDialogSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:twoWindowsDialogSoHelpAttempt="clr-namespace:TwoWindowsDialogSoHelpAttempt"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<!--Main window data context declaration-->
<twoWindowsDialogSoHelpAttempt:FirstWindowDataContext/>
</Window.DataContext>
<i:Interaction.Behaviors>
<!--next behavior helps to control loading of the second window-->
<twoWindowsDialogSoHelpAttempt:SecondWindowLoadingBehavior
IsSecondWindowShouldBeShown="{Binding IsSecondWindowShouldBeShown, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
CanProvideDataForSecondWindow="{Binding CanProvideDataForSecondWindow, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Border Width="150" Height="30" BorderBrush="Tomato" BorderThickness="1">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</Border>
<Button Command="{Binding Command, UpdateSourceTrigger=PropertyChanged}">Show second Window</Button>
</StackPanel>
</Grid>
Second window xaml code
<Window x:Class="TwoWindowsDialogSoHelpAttempt.SecondWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecondWindow" Height="300" Width="300">
<Grid Background="Green">
<TextBlock Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="150"></TextBlock>
</Grid>
Second window loading behavior, helps to control the SecondWindow appearance, and injects the ICanProvideDataForSecondWindow into the c'tor og the second window view model
public class SecondWindowLoadingBehavior:Behavior<FrameworkElement>
{
private Window _window;
public static readonly DependencyProperty CanProvideDataForSecondWindowProperty = DependencyProperty.Register(
"CanProvideDataForSecondWindow", typeof (ICanProvideDataForSecondWindow), typeof (SecondWindowLoadingBehavior), new PropertyMetadata(default(ICanProvideDataForSecondWindow)));
/// <summary>
/// helps to control dialog between first and second window
/// </summary>
public ICanProvideDataForSecondWindow CanProvideDataForSecondWindow
{
get { return (ICanProvideDataForSecondWindow) GetValue(CanProvideDataForSecondWindowProperty); }
set { SetValue(CanProvideDataForSecondWindowProperty, value); }
}
public static readonly DependencyProperty IsSecondWindowShouldBeShownProperty = DependencyProperty.Register(
"IsSecondWindowShouldBeShown", typeof (bool), typeof (SecondWindowLoadingBehavior), new PropertyMetadata(default(bool), IsSecondWindowShouldBeShownPropertyChangedCallback));
//when the IsSecondWindowShouldBeShown dependency property will be changed, will trigger the window showing
private static void IsSecondWindowShouldBeShownPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var isShouldBeShown = (bool) args.NewValue;
var behavior = dependencyObject as SecondWindowLoadingBehavior;
if (isShouldBeShown == false || behavior == null || behavior.CanProvideDataForSecondWindow == null)
{
return;
}
behavior.ShowSecondWindow();
}
/// <summary>
/// helps to control the second window loading
/// </summary>
public bool IsSecondWindowShouldBeShown
{
get { return (bool)GetValue(IsSecondWindowShouldBeShownProperty); }
set { SetValue(IsSecondWindowShouldBeShownProperty, value); }
}
//helps to prepare and show the second window
private void ShowSecondWindow()
{
_window = new SecondWindow {DataContext = new SecondWindowDataContext(CanProvideDataForSecondWindow)};
_window.Closing += WindowOnClosing;
_window.Show();
}
//disposes a data context instance when a window was closed
private void WindowOnClosing(object sender, CancelEventArgs e)
{
_window.Closing -= WindowOnClosing;
IsSecondWindowShouldBeShown = false;
var disposableDataContext = _window.DataContext as IDisposable;
if (disposableDataContext == null) return;
disposableDataContext.Dispose();
}
}
View models and helpers
Main window view model, implements the ICanProvideDataForSecondWindow to be able to send data and change the second window content on the fly
/// <summary>
/// a first window data context, provides data to the second window based on a
/// ICanProvideDataForSecondWindow implementation
/// </summary>
public class FirstWindowDataContext:BaseObservableObject, ICanProvideDataForSecondWindow
{
private string _text;
private ICommand _command;
private bool _isSecondWindowShouldBeShown;
private ICanProvideDataForSecondWindow _canProvideDataForSecondWindow;
public FirstWindowDataContext()
{
CanProvideDataForSecondWindow = this;
}
public ICanProvideDataForSecondWindow CanProvideDataForSecondWindow
{
get { return _canProvideDataForSecondWindow; }
set
{
_canProvideDataForSecondWindow = value;
OnPropertyChanged();
}
}
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged();
OnSecondWindowDataEventHandler(new DataForSecondWindowArgs{Data = Text});
}
}
public ICommand Command
{
get { return _command ?? (_command = new RelayCommand(ShowSecondWindow)); }
}
//method to show the second window
private void ShowSecondWindow()
{
//will show the window if it isn't opened yet
if(IsSecondWindowShouldBeShown) return;
IsSecondWindowShouldBeShown = true;
}
public bool IsSecondWindowShouldBeShown
{
get { return _isSecondWindowShouldBeShown; }
set
{
_isSecondWindowShouldBeShown = value;
OnPropertyChanged();
}
}
public event EventHandler<DataForSecondWindowArgs> SecondWindowDataEventHandler;
protected virtual void OnSecondWindowDataEventHandler(DataForSecondWindowArgs e)
{
var handler = SecondWindowDataEventHandler;
if (handler != null) handler(this, e);
}
}
ICanProvideDataForSecondWindow - interface to help to communicate between main and second windows view models
public interface ICanProvideDataForSecondWindow
{
event EventHandler<DataForSecondWindowArgs> SecondWindowDataEventHandler;
string Text { get; set; }
}
DataForSecondWindowArgs - argument delivered during the view model communication
public class DataForSecondWindowArgs:EventArgs
{
public string Data { get; set; }
}
The SecondWindow data context - is the listener, get data by the ICanProvideDataForSecondWindow interface.
/// <summary>
/// second window data context, listening for the changes in first window data context to show them on the second window
/// </summary>
class SecondWindowDataContext:DisposableBaseObservableObject
{
private readonly ICanProvideDataForSecondWindow _canProvideDataForSecondWindow;
private string _text;
public SecondWindowDataContext(ICanProvideDataForSecondWindow canProvideDataForSecondWindow)
{
_canProvideDataForSecondWindow = canProvideDataForSecondWindow;
_canProvideDataForSecondWindow.SecondWindowDataEventHandler += CanProvideDataForSecondWindowOnSecondWindowDataEventHandler;
Text = _canProvideDataForSecondWindow.Text;
}
private void CanProvideDataForSecondWindowOnSecondWindowDataEventHandler(object sender, DataForSecondWindowArgs args)
{
Text = args.Data;
}
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged();
}
}
protected override void DisposeOverride()
{
base.DisposeOverride();
_canProvideDataForSecondWindow.SecondWindowDataEventHandler -= CanProvideDataForSecondWindowOnSecondWindowDataEventHandler;
}
}
INPC implementation and command(you can replace this with your own Commandclass) code
/// <summary>
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class DisposableBaseObservableObject : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
#region Disposable
private bool _isDisposed;
~DisposableBaseObservableObject()
{
Dispose();
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
GC.SuppressFinalize(this);
_isDisposed = true;
DisposeOverride();
}
protected virtual void DisposeOverride()
{
}
#endregion
}
public class RelayCommand : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action _execute;
public RelayCommand(Action execute)
: this(() => true, execute)
{
}
public RelayCommand(Func<bool> canExecute, Action execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter = null)
{
return _canExecute();
}
public void Execute(object parameter = null)
{
_execute();
}
public event EventHandler CanExecuteChanged;
}
Explanations
- MainWindow - like your first window.
- SecondWindow - like your second window.
- SecondWindowLoadingBehavior - behavior(links - what is behavior and how to create behavior, where is behavior dll) use to control the second window appearance.
- FirstWindowDataContext - the main window data context, is a data provider for the second window.
- ICanProvideDataForSecondWindow - interface to provide data for the second window(good for a dependency injection of SOLID).
- SecondWindowDataContext - the second window data context, listens for the changes in the main window data context(here the ICanProvideDataForSecondWindow is injected to it's c'tor).
- In addition you can see some IEventAggregator based solutions(EventAggregator example).
I'll really glad to help with additional explanations if needed.
Just let me know.
Regards.