Reputation: 489
I am currently using the MVVM pattern with PRISM in my wpf application and have the Shell, Views, and ViewModels all injecting and tying together. (As far as I know... this is my first wpf app). My module definition wires up everything properly.
I have to display a inconsistently sized collection of objects on one of my views and rather than create it all by hand on the backend, I would like to create a new user control and have it bind to an individual data entity so that I can keep it self-cognizant and governing, but those controls need to be added to a grid or panel on my view. Following the previous examples I decided to walk down the interface + concrete path so as to keep even the smaller pieces flexible.
Firstly, am I going down the right path with this? In ASP.Net I would create a template for the display and then attach the model.
Second, is there an acceptable way to have my container resolve this "template" user control while I am constructing my main view?
Originally my code looked this this so I want to completely replace it with another xaml:
foreach (var node in nodes) {
var p = new StackPanel();
p.DataContext = node;
p.Children.Add(new Label() { Content = node.Name });
p.Children.Add(new TextBox() { Text = "" });
NodesControl.Children.Add(p);
}
Upvotes: 0
Views: 894
Reputation:
Let DataTemplates do the work for you.
Given a simple window, with the following ViewModel:
public sealed class ViewModel : INotifyPropertyChanged
{
private object _derper;
public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };
public object Derper
{
get { return this._derper; }
set
{
this._derper = value;
PropertyChanged(this, new PropertyChangedEventArgs("Derper"));
}
}
public ICommand OnDerp { get; set; }
public ViewModel()
{
OnDerp = new DerpCommand(this);
}
}
We want to show different UI for different values of the property Derper. This property is set by the DerpCommand:
public sealed class DerpCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly ViewModel _viewModel;
private int _count = 0;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_viewModel.Derper = _count % 2 == 0 ?
(object)new Derp()
{ Herp = "Derped " + (++_count / 2 + 1) + " times." } :
new Herp()
{ Derp = "Herped " + (_count++ / 2 + 1) + " times." };
}
public DerpCommand(ViewModel viewModel)
{
this._viewModel = viewModel;
}
}
It simply switches the value of ViewModel.Derper
between instances of Herp
and Derp
types based on how many times it runs. I'll omit the implementation of these models as they are POCOs with a single property. Dull stuff.
Now, when the ViewModel.Derper
property is of type Derp
, we want a nice cornflower blue background with black text foreground displaying the value of the model. When it is of type Herp
, we want a black background with white text. So we create DataTemplates
for each:
<DataTemplate
DataType="{x:Type t:Derp}">
<TextBlock
Padding="50"
Background="CornflowerBlue"
Text="{Binding Herp}" />
</DataTemplate>
<DataTemplate
DataType="{x:Type t:Herp}">
<TextBlock
Padding="50"
Foreground="White"
Background="Black"
Text="{Binding Derp}" />
</DataTemplate>
Notice that in each DataTemplate
the DataContext
is the instance of the model of the specified DataType
.
How do we wire these up? We don't. We just specify the area within the UI where we want the appropriate DataTemplate
to be displayed by adding a ContentControl
and binding its Content
property to ViewModel.Derper
. Here is the entire UI:
<Window
x:Class="DataTemplateExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:DataTemplateExample"
Title="DataTemplate example"
Height="350"
Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate
DataType="{x:Type t:Derp}">
<TextBlock
Padding="50"
Background="CornflowerBlue"
Text="{Binding Herp}" />
</DataTemplate>
<DataTemplate
DataType="{x:Type t:Herp}">
<TextBlock
Padding="50"
Foreground="White"
Background="Black"
Text="{Binding Derp}" />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition
Height="auto" />
</Grid.RowDefinitions>
<ContentControl
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding Derper}" />
<Button
Grid.Row="1"
Command="{Binding OnDerp}">Click me to derp</Button>
</Grid>
</Window>
And what it looks like:
After all that, if you still want to use Unity to hook up UI, you can create your own DataTemplateSelector
that uses Unity to look for templates. Waste of time IMHO. Go with the flow.
Upvotes: 2