SenseiHitokiri
SenseiHitokiri

Reputation: 489

Using Unity to resolve user control views inside another view

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

Answers (1)

user1228
user1228

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:

enter image description here

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

Related Questions