1

I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:

<ContentControl Content="{Binding Path=TempVM}"/>

and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this

 <DataTemplate DataType="{x:Type vm:TempViewModel}">
        <view:Temp />
    </DataTemplate>

That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do

<ContentControl Content="{Binding Path=Missions}"/>

(Missions is an observable collection of MissionData) and trying to template it like this:

 <DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
        <StackPanel>
            <ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
            <Button Content="Go"/>
        </StackPanel>
    </DataTemplate>

But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?

Nix
  • 321
  • 8
  • 20

2 Answers2

2

From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.

class MissionsSelectionViewModel
{
    public ObservableCollection<Mission> Misssions;
    public MissionData SelectedMission;
    public ICommand MissionSelected;
}

and modify your datatemplate to

<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
    <StackPanel>
        <ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
        <Button Content="Go" Command="{Binding MissionSelected}/>
    </StackPanel>
</DataTemplate>
dowhilefor
  • 10,971
  • 3
  • 28
  • 45
  • I have done it this way in the end. But when the mission is selected I need to go back to the original view model - that TempVM I have is for displaying a new empty mission or a selected mission. So do I need to pass the parent view model into the MissionsSelection View model so that I can set the parent's mission in there? – Nix Apr 18 '12 at 10:13
  • you can do that, it really depends on your application design. You could also let the parent vm just read the property from the MissionsSelectionViewModel, which is propably stored in there. – dowhilefor Apr 18 '12 at 10:28
1

If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.

However, it seems to me that the best solution is the following.

  1. Add a IsMissionsListVisible property to your VM.
  2. Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
  3. Use a keyed DataTemplate resource.
  4. Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.

I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).

Pavel Gatilov
  • 7,579
  • 1
  • 27
  • 42
  • lol I just came from refactoring what you described to using DataTemplates, cause I though this was way smarter ... but I faced the same problem as Nix described. So I will stay on a Visibility property for now also .... :D – Nicolas Dec 06 '18 at 16:23