What is the advantage of setting DataContext in code instead of XAML?

27,507

Solution 1

You can (maybe in 2009 you couldn't) get the best of both worlds by using the d:DataContext attribute. You don't need any of that ViewModelLocator craziness if you're not ready for that yet :-)

First make sure that you have the following XML namespace defined in your root element:

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

Then you can add the following attribute to an element in your xaml:

d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type=vm:CustomerInsightViewModel}"

In your xaml codebehind :

    public CustomerInsightUserControl()
    {
        InitializeComponent();

        if (!DesignerProperties.IsInDesignTool)
        {
            DataContext = new CustomerInsightViewModel();
        }
    }

Then in your ViewModel:

    public CustomerInsightViewModel()
    {
        if (IsInDesignMode)
        {
            // Create design time data
            Customer = new Customer() {
                FirstName=... 
            }
        }
        else {
            // Create datacontext and load customers
        }
    }

Don't miss the IsDesignTimeCreatable=True or else Blend won't instantiate your class

Solution 2

I don't like the idea of having Expression Blend try to instantiate my data objects.

I set the DataContext through code where I am able to use Dependency Injection to inject the proper objects, services, providers or what else I am using to find my code.

Solution 3

See Rob's article about design time data in Blend: http://www.robfe.com/2009/08/design-time-data-in-expression-blend-3/

Solution 4

It should also be possible to use ObjectDataProvider to establish an object factory using Unity or other IOCs as implied here...

http://social.msdn.microsoft.com/Forums/en/wpf/thread/1ff9e90e-302e-436e-bab3-ca4bad2b85af

in particular...

http://www.codeproject.com/Articles/43806/WPF-Ninject-Dojo-The-Data-Provider.aspx

Solution 5

Having it in codebehind makes it easy to inject the datacontext using unity.

Share:
27,507
Angry Dan
Author by

Angry Dan

web/software developer, .NET, C#, WPF, PHP, software trainer, English teacher, have philosophy degree, love languages, run marathons my tweets: http://www.twitter.com/edward_tanguay my runs: http://www.tanguay.info/run my code: http://www.tanguay.info/web my publications: PHP 5.3 training video (8 hours, video2brain) my projects: http://www.tanguay.info

Updated on April 22, 2020

Comments

  • Angry Dan
    Angry Dan about 4 years

    There seem to be two main ways to define DataContext in WPF:

    • either in code like this:

    App.xaml.cs (taken from the WPF MVVM Toolkit template):

    public partial class App : Application
    {
        private void OnStartup(object sender, StartupEventArgs e)
        {
            // Create the ViewModel and expose it using the View's DataContext
            MainView mainView = new MainView();
            MainViewModel mainViewModel = new MainViewModel();
            mainViewModel.LoadCustomers("c:\\testdata2\\Customers.xml");
            mainView.DataContext = mainViewModel;
            mainView.Show();
        }
    }
    
    • or in XAML like this:

    Window1.xaml:

    <DockPanel>
        <StackPanel
            HorizontalAlignment="Left"
            DockPanel.Dock="Top"
            Orientation="Horizontal">
            <StackPanel.DataContext>
                <local:CustomerViewModel />
            </StackPanel.DataContext>
            <TextBlock Text="{Binding Path=FirstName}" />
            <TextBlock Text=" " />
            <TextBlock Text="{Binding Path=LastName}" />
        </StackPanel>
    
        <StackPanel
            HorizontalAlignment="Left"
            VerticalAlignment="top"
            DockPanel.Dock="Top"
            Orientation="Horizontal">
            <ListBox ItemsSource="{Binding Source={StaticResource FileNames}}" />
        </StackPanel>
    
        <StackPanel
            HorizontalAlignment="Left"
            VerticalAlignment="top"
            DockPanel.Dock="Top"
            Orientation="Horizontal">
            <ComboBox
                ItemsSource="{Binding Source={StaticResource Directories}}"
                SelectedIndex="0" />
        </StackPanel>
    
        <StackPanel
            HorizontalAlignment="Left"
            VerticalAlignment="top"
            DockPanel.Dock="Top"
            Orientation="Horizontal">
            <StackPanel.DataContext>
                <local:SystemInformationViewModel />
            </StackPanel.DataContext>
            <TextBlock Text="{Binding Path=CurrentTime}" />
        </StackPanel>
    </DockPanel>
    

    One advantage that defining the DataContext in XAML has is that your data shows up in Expression Blend design mode and Expression Blend allows you to do quite a lot within the GUI e.g. choose fields from your datasource, etc. as shown here.

    I have read that binding ADO.NET objects cannot be bound in XAML (although I don't see why you could write a minimal wrapper for them to which you could bind from XAML).

    Strange that the WPF Team in making the WPF MVVM templates define the DataContext in code which very quickly makes it impracticable to edit your Views in Expression Blend, since your data doesn't show up in design mode which is often a significant part of the layout.

    So I'm thinking there must be some advantage down the road to setting the DataContext in code instead of XAML, anyone know what it is?

  • Angry Dan
    Angry Dan about 15 years
    Is there a best-of-both-worlds solution to this? Does this mean that if you use Composite Application Library / Unity that you basically can cut and paste from Expression Blend or is there a simple way to e.g. have mock default DataContexts in XAML which designers can use in Blend?
  • Denis Troller
    Denis Troller about 15 years
    see my answer on the DataobjectProvider. It could be used as a facade to delegate the instantiation and use something coming from DI.
  • Brian Hinchey
    Brian Hinchey almost 12 years
    If you get a compile time error starting "The property 'DataContext' must be in the default namespace...", the solution for this is at stackoverflow.com/q/8303803/62278