Julien Ferraro
Julien Ferraro

Reputation: 401

Mvvm Light, UWP and code behind

Base on this excellent presentation from Laurent Bugnion at Xamarin Evolve 2014, I'm trying to create my first UWP/MVVM Light application.

I created a very simple Article : ObservableObject class with 2 string properties : Référence and Désignation.

In the view model associated to the article list view, I have an action to create a new article :

    public ArticlesViewModel(IArticleService dataService, INavigationService navigationService)
    {
        ArticleService = dataService;
        NavigationService = navigationService;

        CréeArticleCommand = new RelayCommand(CréeArticle);
    }

    public RelayCommand CréeArticleCommand { get; private set; }

    private void CréeArticle()
    {
        if (!CréeArticleCommand.CanExecute(null))
            return;

        NavigationService.NavigateTo(ViewModelLocator.ArticleDetail_Key,
                                     new ArticleViewModel(new Article(),
                                                          ArticleService,
                                                          NavigationService));
    }

here is the XAML for my Article detail view :

<!-- language: xaml -->
<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UniversalTest1.UWP.Articles"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Editors="using:DevExpress.UI.Xaml.Editors"
    x:Class="UniversalTest1.UWP.Articles.Article_Detail"
    mc:Ignorable="d"
    xmlns:vm="clr-namespace:UniversalTest1.Data.ViewModels.Articles;assembly=UniversalTest1.Data"
    d:DataContext="{d:DesignInstance Type=vm:ArticleViewModel, IsDesignTimeCreatable=True}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Référence :"   HorizontalAlignment="Left" Margin="24,15,0,0" VerticalAlignment="Top"/>
        <TextBlock Text="Désignation :" HorizontalAlignment="Left" Margin="10,52,0,0" VerticalAlignment="Top"/>

        <Editors:TextEdit Text="{Binding Article.Référence, Mode=TwoWay}" HorizontalAlignment="Left" Margin="100,8,0,0" VerticalAlignment="Top" Width="300"/>
        <Editors:TextEdit Text="{Binding Article.Désignation, Mode=TwoWay}" HorizontalAlignment="Left" Margin="100,45,0,0" VerticalAlignment="Top" Width="500"/>

        <Button Content="Sauver" Command="{Binding SauverCommand}" HorizontalAlignment="Left" Margin="102,84,0,0" VerticalAlignment="Top"/>
    </Grid>
</Page>

My problem here is that I have to define the DataContext in the code behind of my page :

public sealed partial class Article_Detail : Page
{
    public Article_Detail()
    {
        this.InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        DataContext = (ArticleViewModel)e.Parameter;
    }
}

Is there a way to keep the design time DataContext as defined in the d:DataContext part of the Xaml's Page, and at runtime, get the DataContext from the Navigation parameter ?

My goal here is to have the less amount possible of code in the code behind. So I would like to define the runtime DataContext in the XAML also.

Upvotes: 3

Views: 2192

Answers (2)

Arnaud Develay
Arnaud Develay

Reputation: 3970

For this, you need to use your own implementation of NavigationService. The concept is to navigate to your page and call your ViewModel at the same time to handle parameters and set the DataContext.

Here are two samples of this pattern:

Upvotes: 0

H77
H77

Reputation: 5967

You can make use of dependency injection to create design or runtime service instances for your viewmodel. Using a view model locator you can do something like this:

public class ViewModelLocator
{
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            if (!SimpleIoc.Default.IsRegistered<IArticleService>())
            {
                SimpleIoc.Default.Register<IArticleService, DesignArticleService>();
            }
        }
        else
        {
            if (!SimpleIoc.Default.IsRegistered<IArticleService>())
            {
                SimpleIoc.Default.Register<IArticleService, ArticleService>();
            }
        }

        SimpleIoc.Default.Register<ArticleViewModel>();
    }

    public ArticleViewModel ArticleViewModel => ServiceLocator.Current.GetInstance<ArticleViewModel>();
}

And in your App.xaml you register the locator

<Application
    x:Class="UniversalTest1.App" // your namespace
    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" 
    mc:Ignorable="d" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModel="using:UniversalTest1.Data.ViewModels"> // your namespace

    <Application.Resources>

        <ResourceDictionary>
            <viewModel:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
        </ResourceDictionary>

    </Application.Resources>

</Application>

And then you can reference it in your xaml like this:

<Page
...
DataContext="{Binding ArticleViewModel, Source={StaticResource Locator}}">

You could also take a look at the sample code here https://mvvmlight.codeplex.com/SourceControl/latest#Samples/Flowers/Flowers.Data/ViewModel/ViewModelLocator.cs

Upvotes: 1

Related Questions