7

I'm writing an application in WPF, using the MVVm toolkit and have problems with hooking up the viewmodel and view.

The model is created with ado.net entity framework.

The viewmodel:

public class CustomerViewModel
    {
        private Models.Customer customer;
        //constructor
        private ObservableCollection<Models.Customer> _customer = new ObservableCollection<Models.Customer>();
        public ObservableCollection<Models.Customer> AllCustomers
        {
            get { return _customer; }

        }
        private Models.Customer _selectedItem;
        public Models.Customer SelectedItem
        {
            get { return _selectedItem; }

        }
        public void LoadCustomers()
        {
            List<Models.Customer> list = DataAccessLayer.getcustomers();
            foreach (Models.Customer customer in list)
            {
                this._customer.Add(customer);
            }
            }
        }

And the view (no code behind at the moment):

<UserControl x:Class="Customers.Customer"
             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" 
             xmlns:vm ="clr-namespace:Customers.ViewModels"
             d:DesignHeight="300" d:DesignWidth="300"
             xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit" >

    <Grid>
        <toolkit:DataGrid ItemsSource="{Binding AllCustomers}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" AutoGenerateColumns="True">

        </toolkit:DataGrid>

        <toolkit:DataGrid ItemsSource="{Binding SelectedItem.Orders}">

        </toolkit:DataGrid>



    </Grid>
</UserControl>

And dataaccesslayer class:

 class DataAccessLayer
    {
        public List<Customer> customers = new List<Customer>();

        public static List<Customer> getcustomers()
        {
            entities db = new entities();

            var customers = from c in db.Customer.Include("Orders")
                           select c;
            return customers.ToList();
        }


    }

The problem is that no data is displayed simply because the data context is not set. I tried to do it in a code-behind but is did not work. I would prefer to do it in a xaml file anyway. Another problem is with the SelectedItem binding - the code is never used.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
EVA
  • 193
  • 1
  • 6
  • 14
  • In your code you have an AllCustomers property on your View Model but you are binding to Customers. – mak May 17 '10 at 17:58
  • I was cleaning the code slightly and made a typo. The binding in the working code is of course correct. – EVA May 17 '10 at 18:17

3 Answers3

5

Since this is using the MVVM paradigm, I would instance your ViewModel in the constructor for the View. My View/ViewModels typically follow this sequence of events:

  1. View is instanced
  2. View constructor instances ViewModel
  3. ViewModel initializes
  4. ViewModel runs data getting procedures(separate thread)
  5. ViewModel calls OnPropertyChanged("") to alert View that something has changed; check everything

My ViewModel is instanced from the XAML codebehind (sorry this is in VB.NET, have not gotten around to learning C# well enough to trust myself with it):

Public Sub New()
    MyBase.New()
    Me.DataContext = New EditShipmentViewModel(Me) 'pass the view in to set as a View variable
    Me.InitializeComponent()
End Sub

Initially I hoped to have something like

<UserControl>
    <UserControl.DataContext>
        <Local:EditShipmentViewModel>
    </UserControl.DataContext>
</UserControl>

But that did not work out like I wanted it to.

Hope that helps a little.

CodeWarrior
  • 7,388
  • 7
  • 51
  • 78
0

I set my viewmodel datacontext the same way I observed Blend4 to. That is, if my viewmodel is called MainViewModel, I reference it in the view like:

 <UserControl.Resources>
    <local:MainViewModel x:Key="MainViewModelDataSource" />
 </UserControl.Resources>

 <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}">
    ...view xaml stuff

 </Grid>

also, if you're loading data from a database in the constructor of your viewmodel, don't forget to add a helper method around it like:

  if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
        {
             myCollection = new ObservableCollection<Blah>(something);
        }

so that visual studio/Blend4 doesn't crash trying to retrieve the data from the database connection in the Designer. I personally load data in the constructor quite often, just because I need it right away, and for it to be cached in memory from startup.

:)

Rachael
  • 1,965
  • 4
  • 29
  • 55
0

Is this what you're looking for?

Community
  • 1
  • 1
Chris Nicol
  • 10,256
  • 7
  • 39
  • 49
  • yes and no. I need to somehow call the method getcustomers(). Should I do it in a viewmodel constructor? Doesn't seem very good. – EVA May 17 '10 at 17:57
  • Personally I would have the ViewModel call getcustomers() in the constructor. What do you feel is not good about it? When the view renders it will create a new instance of the ViewModel and at that point you want the data to be retrieved, so it makes sense for the constructor to do it. – Chris Nicol May 17 '10 at 18:07
  • What about the xaml construction in Resources? – EVA May 17 '10 at 18:19
  • 2
    The only major issue with declaring the object in the XAML is that any error thrown during the VM construction, will be eaten by a XAML parsing error. We have switched off to using a DI like MEF to have inject the VM into the View's DataContext at Load. OnLoad can fire multiple times so make sure you short circuit it with an _isLoaded field or something of the like. – Agies May 19 '10 at 01:19
  • Yes that's a better solution to use DI for sure. Nice comment! – Chris Nicol May 19 '10 at 23:25