4

I am currently working on a small project by myself to learn WPF better, and a question I was asking myself is how would navigation between different views work if I want to follow the MVVM pattern.

I have stumbled upon this question here answering my question, but unfortunately it does not seem to work. Instead of having my view display, it is only the namespace of the associated viewmodel that is displayed. So, for exemple, instead of seeing my LoginView, I am seeing a white screen with "JollyFinance.ViewModels.LoginViewModel" written.

Here is my MainWindow.xaml code:

<Window.Resources>
    <!-- Different pages -->
     <DataTemplate x:Key="LoginView" DataType="vm:LoginViewModel">
        <views:LoginView />
    </DataTemplate>
</Window.Resources>

<Window.DataContext>
    <vm:MainViewModel/>
</Window.DataContext>

<Grid>
    <ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>

Here is my MainViewModel.cs:

public class MainViewModel : BindableObject
{
    private ViewModelNavigationBase _currentViewModel;

    public MainViewModel()
    {
        CurrentViewModel = new LoginViewModel();
    }

    public ViewModelNavigationBase CurrentViewModel
    {
        get { return _currentViewModel; }
        set
        {
            if (_currentViewModel != value)
            {
                _currentViewModel = value;
                RaisePropertyChanged();
            }
        }
    }
}

My LoginViewModel.cs file:

    public LoginViewModel()
    {
        LoginCommand = new RelayCommand(Login);
        NavigateCommand = new RelayCommand(Login);
    }

    private void Login(object param)
    {
        Popup codePopup = new Popup();
        TextBlock popupText = new TextBlock();
        popupText.Text = "Test";
        popupText.Background = Brushes.LightBlue;
        popupText.Foreground = Brushes.Blue;
        codePopup.Child = popupText;
    }

    public String Username { get; set; }

    public String Password { get; set; }

    public ICommand LoginCommand { get; set; }
}

My LoginView.xaml file is a UserControl comprised of a few Buttons and TextBlocks. BindableObject is just an abstract class that I use that implements the INotifyProperty interface. And ViewModelNavigationBase is the class that all my viewmodels will inherit from; it doesn't contain anything for the moment.

How would I resolve this issue for it to display my view instead of a string representation of the associated viewmodel?

Community
  • 1
  • 1
Choub890
  • 1,163
  • 1
  • 13
  • 27

2 Answers2

11

WPF does not support implicit DataTemplates with string as the type identifier.

You need to use the x:Type Markup Extension to specify your view model type:

<DataTemplate DataType="{x:Type vm:LoginViewModel}">
    <views:LoginView />
</DataTemplate>
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • This unfortunately doesn't solve my issue. I switched `DataType="vm:LoginViewModel"` with `DataType="{x:Type vm:LoginViewModel}"`, but I still only see the "JollyFinance.ViewModels.LoginViewModel" string instead of my LoginView – Choub890 Dec 20 '14 at 06:26
  • Strange, with the x:Type it should work. It seems WPF is not able to find your DataTemplate. Are the `` and the `` definied in the same file? Can you try to explicitly specify the DataTemplate? So try to change your `` and in the contentcontorl: ` – nemesv Dec 20 '14 at 06:33
  • Yes, they are both in MainWindow.xaml. I have tried explicitly specify the DataTemplate to the ContentControl the way you have described, but still the same issue persists. The only difference it makes is instead of seeing "JollyFinance.ViewModels.LoginViewModel", I see "System.Windows.DataTemplate". – Choub890 Dec 20 '14 at 06:36
  • 1
    You are not correctly applied the contenttemaplte. The full code is like this `` I guess you have tried: `` that is why you've gotten the "System.Windows.DataTemplate". Other that I was not able to repro this issue in an empty project. There the `DataType="{x:Type vm:LoginViewModel}"` fixes the problem. – nemesv Dec 20 '14 at 06:46
  • This does indeed work. If you want to edit your answer or create a new one, I will set it as answered. Also, out of curiosity, this was not mentioned in the post I have linked in my question, nor was it mentioned in most of the examples I have seen online. Is it because there was a change in the .net/WPF framework since those examples were written? – Choub890 Dec 20 '14 at 06:50
  • 1
    No, no this is not the solution this was just a test to figure out what could be the problem. With this `ContentTemplate="{StaticResource login}"` you will loose the ability to dynamically replace your `CurrentViewModel` and use a different view. So this is not yet the solution. Noting changed in WPF so the linked article should also work and also my answer about the `DataType="{x:Type vm:LoginViewModel}"`... – nemesv Dec 20 '14 at 06:53
  • I have found that on the [MSDN documentation](http://msdn.microsoft.com/fr-fr/library/system.windows.controls.contentcontrol.content(v=vs.95).aspx): `The Content property of a ContentControl can be any type of object, such as a string, a UIElement, or a DateTime. When Content is set to a UIElement, the UIElement is displayed in the ContentControl. **When Content is set to another type of object, a string representation of the object is displayed in the ContentControl.**` It is normal then for it to be only a string in my window no? – Choub890 Dec 20 '14 at 07:19
  • 1
    The contentcontrol should use the datatemplate if it finds one. So it is not normal to display a string. Please check this simple example where your code is working with using my fix: https://www.dropbox.com/s/vgzww3vn1ankg4r/WpfApplication1.zip?dl=0 `` – nemesv Dec 20 '14 at 07:25
  • Yes, your project works perfectly. I do not see the same behavior in your project. – Choub890 Dec 20 '14 at 07:29
  • I can't believe I have been using WPF for over a decade and this tiny detail never occurred to me. – Chris Bordeman Nov 30 '20 at 13:43
9

I have finally found what the problem was. For some reason, when you declare x:Key on the DataTemplate, it made the ContentControl Content appear as a string. I have removed the x:Key of the DataTemplate and everything works now. I also had to apply nemesv's answer for it to work.

Before:

<DataTemplate x:Key="LoginView" DataType="vm:LoginViewModel">

After (resolves the problem):

<DataTemplate DataType="{x:Type vm:LoginViewModel}">
Choub890
  • 1,163
  • 1
  • 13
  • 27
  • 3
    Yeah, you cannot really have both `x:Key` and `DataType` because `x:Key` overrides the "implicitness" of the `DataType`. So if you `x:Key` you have to use your template with `ContentTemplate="{StaticResource LoginView}"`. – nemesv Dec 20 '14 at 07:41
  • Yeah, I had noticed this was the only difference between your code and mine and tried to remove it and it worked. Thanks again for your patience and help! – Choub890 Dec 20 '14 at 07:44
  • I'm facing the same issue: – Luke May 11 '18 at 10:05
  • Yeah, if you want to reference it via key AND have it still work as the default DataTemplate for the type, then you can remove Key=..., create a second DataTemplate with BasedOn={x:Type ...} and the same DataType. – Chris Bordeman Nov 30 '20 at 13:39