MagB
MagB

Reputation: 2221

WPF Caliburn.Micro: Binding DisplayMemberPath on ChildViewModel

I use VS2013, WPF 4.5, Caliburn Micro 2.0.2. This is a sample project of mine. My ShellViewModel has base class Conductor and has collection of model (a property) called Vehicles. I created also ChildViewModel that has base class Screen and a property called ParentVm, which contains its parent. In my real project I have surely more than one child viewmodel (thus, > 1 screens)

The model (class Vehicle) contains properties: Manufacturer, Model and Type.

How can I bind DisplayMemberPath of ListBox in ChildView which has ItemsSource="ParentVm.Vehicles", so the ListBox can show the Manufacturer of class Vehicle?

Following is my sample code. Please feel free to modify it to show me the solution. Thank you in advance.

public class Vehicle : PropertyChangedBase
{
    public String Manufacturer { get; set; }
    public String Model { get; set; }
    public String Type { get; set; }
}

ShellViewModel

public class ShellViewModel : Conductor<Screen>, IHaveActiveItem
{
    public ChildViewModel ChildVm { get; private set; }
    public ObservableCollection<Vehicle> Vehicles { get; set; }

    public ShellViewModel()
    {
        DisplayName = "Shell Window";
        ChildVm = new ChildViewModel(this);
        Vehicles = new ObservableCollection<Vehicle>();
        SetData();
    }

    public void DisplayChild()
    {
        ActivateItem(ChildVm);
    }

    private void SetData()
    { // fill collection with some sample data
        vh = new Vehicle();
        vh.Manufacturer = "Chevrolet";
        vh.Model = "Spark";
        vh.Type = "LS";
        Vehicles.Add(vh);
    }
}

ShellView

<UserControl x:Class="CMWpfConductorParentChild.Views.ShellView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         d:DesignHeight="300"
         d:DesignWidth="300"
         mc:Ignorable="d">
    <Grid Width="300" Height="300" HorizontalAlignment="Center"
          ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="10*" />
        </Grid.RowDefinitions>
        <Button x:Name="DisplayChild"
                Grid.Row="0" Width="120" Height="40"
                HorizontalContentAlignment="Center"
                Content="Display Child View" />
        <ContentControl x:Name="ActiveItem" Grid.Row="1" />
    </Grid>
</UserControl>

ChildViewModel

public class ChildViewModel : Screen
{
    public ShellViewModel ParentVm { get; private set; }
    public ChildViewModel(ShellViewModel parent)
    {
        ParentVm = parent;
    }
}

ChildView (the binding of DisplayMemberPath below doesn't work)

<UserControl x:Class="CMWpfConductorParentChild.Views.ChildView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         d:DesignHeight="300"
         d:DesignWidth="300"
         mc:Ignorable="d">
    <Grid Width="300" Height="300">
        <ListBox DisplayMemberPath="Manufacturer" ItemsSource="ParentVm.Vehicles" />
    </Grid>
</UserControl>

Upvotes: 0

Views: 730

Answers (1)

Mark Polsen
Mark Polsen

Reputation: 149

You need to add the 'Binding' keyword to the 'ItemsSource' property:

<ListBox DisplayMemberPath="Manufacturer" ItemsSource="{Binding ParentVm.Vehicles}" />

I've modified your example to show how you can bind properties to the child view based on convention.

ShellViewModel

public class ShellViewModel : Screen, IShell
{
    private readonly ChildViewModel _ChildView;
    private readonly IEventAggregator _Aggregator;
    public IList<Vehicle> vehicles = new List<Vehicle>();

    public ShellViewModel(IEventAggregator aggregator, ChildViewModel childView)
    {
        if (aggregator == null)
            throw new ArgumentNullException("aggregator");
        _Aggregator = aggregator;

        if (childView == null)
            throw new ArgumentNullException("childView");
        _ChildView = childView;
        DisplayName = "Shell Window";
    }

    public ChildViewModel ChildView 
    { 
        get { return _ChildView; } 
    }

    public void DisplayChild()
    {
        var vh = new Vehicle() { Manufacturer = "Chevrolet", Model = "Spark", Type = "LS" };
        vehicles.Add(vh);
        _Aggregator.PublishOnUIThreadAsync(new ShowChildViewEvent(vehicles));
    }
}

ShellView

<UserControl x:Class="CaliburnDemo.ShellView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:cal="http://www.caliburnproject.org">
<Grid Width="300" Height="300" HorizontalAlignment="Center"
      ShowGridLines="True">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="10*" />
    </Grid.RowDefinitions>
    <Button x:Name="DisplayChild"
            Grid.Row="0" Width="120" Height="40"
            HorizontalContentAlignment="Center"
            Content="Display Child View" />
    <ContentControl cal:View.Model="{Binding ChildView}" Grid.Row="1" />
</Grid>

ChildViewModel

public class ChildViewModel : Screen, IHandle<ShowChildViewEvent>
{
    private BindableCollection<Vehicle> _Vehicles;
    private readonly IEventAggregator _Aggregator;

    public ChildViewModel(IEventAggregator aggregator)
    {
        if (aggregator == null)
            throw new ArgumentNullException("aggregator");
        _Aggregator = aggregator;
        _Aggregator.Subscribe(this);
    }

    public BindableCollection<Vehicle> Vehicles
    {
        get { return _Vehicles; }
        set
        {
            _Vehicles = value;
            NotifyOfPropertyChange(() => Vehicles);
        }
    }

    public void Handle(ShowChildViewEvent message)
    {
        Vehicles = new BindableCollection<Vehicle>(message.Vehicles);
    }
}

ChildView

<UserControl x:Class="CaliburnDemo.ChildView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <ListBox x:Name="Vehicles" DisplayMemberPath="Manufacturer" />
</Grid>

ShowChildViewEvent

public class ShowChildViewEvent
{
    public ShowChildViewEvent(IList<Vehicle> vehicles)
    {
        Vehicles = vehicles;
    }
    public IList<Vehicle> Vehicles { get; private set; }
}

IShell is an empty interface used for resolving the root view from the container. You can find more on conventions in Caliburn here: Caliburn Conventions

Upvotes: 1

Related Questions