Ok, Im using UserControl
s instead of Page
s to keep them in a single Window. Since you didn't post any XAML, I have no idea what your real need is, but here is my take:
MultiPageSample.xaml ("Main Window"):
<Window x:Class="MiscSamples.MultiPageMVVM.MultiPageSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples.MultiPageMVVM"
Title="MultiPageSample" Height="300" Width="300">
<UniformGrid Rows="1" Columns="2">
<local:Page1 DataContext="{Binding Page1}"/>
<local:Page2 DataContext="{Binding Page2}"/>
</UniformGrid>
</Window>
Code Behind:
public partial class MultiPageSample : Window
{
public MultiPageSample()
{
InitializeComponent();
DataContext = new MultiPageViewModel();
}
}
ViewModel:
public class MultiPageViewModel
{
public Page1ViewModel Page1 { get; set; }
public Page2ViewModel Page2 { get; set; }
public MultiPageViewModel()
{
Page1 = new Page1ViewModel();
Page2 = new Page2ViewModel();
Page2.AddNewCommand = new Command(Page1.AddCommand);
}
}
Page1:
<UserControl x:Class="MiscSamples.MultiPageMVVM.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ItemsControl ItemsSource="{Binding Commands}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding}" Content="Click Me!"
Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
Code Behind:
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
}
}
Page2:
<UserControl x:Class="MiscSamples.MultiPageMVVM.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Content="Add New Command (I Mean Button)"
VerticalAlignment="Center" HorizontalAlignment="Center"
Command="{Binding AddNewCommand}"/>
</UserControl>
Code Behind:
public partial class Page2 : UserControl
{
public Page2()
{
InitializeComponent();
}
}
ViewModel:
public class Page2ViewModel
{
public Command AddNewCommand { get; set; }
}
Command class (can be found on most MVVM frameworks)
//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
public class Command<T>: ICommand
{
public Action<T> Action { get; set; }
public void Execute(object parameter)
{
if (Action != null && parameter is T)
Action((T)parameter);
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action<T> action)
{
Action = action;
}
}
Result:
Now, the explanation of all this mess:
First of all, you must leave behind the traditional mentality of Manipulating UI elements in code, and embrace MVVM.
WPF has very powerful DataBinding capabilities that are utterly absent in ancient dinosaur frameworks.
Notice how I'm using the reusable Command
class (which is kind of a basic part of most MVVM frameworks) to represent the Buttons in the Page1ViewModel. These instances of Command
are then added to the ObservableCollection
, which in turn notifies WPF when an element is added or removed to it, and thus the UI is automatically updated by the Binding
.
Then, the DataTemplate
defined as the ItemTemplate
for the ItemsControl
in Page1
is used to "render" each item inside the ObservableCollection
.
This is what I refer to when I say WPF needs a really different mindset to work with. This is the default approach to EVERYTHING in WPF. You almost NEVER have the need to reference / create / manipulate UI elements in procedural code. That's what XAML is for.
Also notice that this could be simplified A LOT by using the same ViewModel
for both Page
s, but I kept them separate on purpose just to show you this case where you have different ViewModels communicating with each other directly.
Let me know if you have any doubts.