Reputation: 625
I have a window that contains a Viewbox. In that Viewbox, I would like to have one of several views which I have already created as UserControls. I am using the MVVM (Model View View-Model) design pattern. I have searched for about an hour online, and I cannot find any examples or tutorials that explain how to display a UserControl using a ContentPresenter
.
Upvotes: 1
Views: 9357
Reputation: 27382
I'm hiding a lot of details here in terms of view-viewmodel interactions for OK and Cancel, but as long as you have a DataTemplate set up then this should render your view inside of a window.
I have my viewmodel-view datatemplates defined in my application's resource dictionary:
<DataTemplate DataType="{x:Type SaveDocumentChangesVM}">
<SaveDocumentChangesView />
</DataTemplate>
Window to host the view:
<Window x:Class="CustomDialogWindow"
SizeToContent="WidthAndHeight" ShowInTaskbar="False">
<ContentPresenter Name="DialogContentPresenter" />
</Window>
Constructor for the window:
public CustomDialogWindow(object viewModel, string dialogTitle)
{
InitializeComponent();
// A datatemplate will allow WPF to figure out how to render the window contents
DialogContentPresenter.Content = viewModel;
if (dialogTitle != null)
{
Title = dialogTitle;
}
}
Show the dialog like this:
var vm = new SaveDocumentChangesVM();
var dialog = new CustomDialogWindow(vm, "This is my dialog");
dialog.ShowDialog();
Upvotes: 1
Reputation:
Depends.
I think your main issue is that a ContentPresenter
is only used within a control template. You can't just stick it in a Window and expect it to show the content of the window. I believe what you really need to do is to use a ContentControl to host your UI.
By binding your model to the Content
property of this control, you can set up DataTemplates that contain the expected view for that model. I'll give you an abbreviated example. Note, this may not match your design, but it demonstrates how it all goes together.
First, for each view, create a Model (or ViewModel) which manages the data (and interaction) for that view.
public sealed class Dialog : INotifyPropertyChanged // or DependencyObject
{
public string Input {get;set;} // INPC or DP implementation not shown
public ICommand Cancel {get;set;}
// class definition not shown
}
Next, define your View to be shown in the ContentPresenter
<UserControl x:Class="Herp.DialogView"
HideImplementationDetails="true">
<Border BorderBrush="Red" BorderThickness="5">
<TextBlock Text="{Binding Input}" />
<Button Command="{Binding Cancel}">Cancel</Button>
<!-- etc etc -->
</Border>
</UserControl>
Next, in your window, add the ContentControl and the DataTemplate
<Window HideImplementationDetailsForBrevityOfXaml="true">
<Window.Resources>
<DataTemplate xmlns:t="clr-namespace:Herp"
DataType="{x:Type t:Dialog}">
<t:DialogView />
</DataTempalte>
</Window.Resources>
<ContentControl Content="{Binding}" />
</Window>
And finally set the DataContext of the Window to your Dialog
model.
public MyWindow()
{
InitializeComponent();
DataContext = new Dialog();
}
This is how the logic flows:
ContentControl
searches resources for a DataTemplate
whose DataType
is set to typeof(Dialog)
ContentControl
Any time the Content
of the ContentControl
changes, the same process repeats. So you can have many different models, each with a different DataTemplate containing a different UserControl, and every time you update the binding on the ContentControl you see the expected View.
With MVVM, you would bind a property of your ViewModel to the Content
property (call it Current or something), then switch out the model within the property to the expected value depending on the ViewModel's current state. Note, within a ContentControl, whatever is set to the Content
property becomes the DataContext
of the immediate child of the ContentControl. Similar to how each ItemsSource
within an ItemsControl
is the DataContext
of the visual tree defined within the ItemTemplate
.
Upvotes: 8