1

I'm very new to MVVM and I am just trying to get my feet wet with a small testing wpf application. I have a Model call ToDoItemModel defined as below: (basically I have a class called ToDoItemModel and ToDoItemListModel which implements IList

public class ToDoItemModel
{
    private DateTime    _TodoDate;
    private string      _TodoDescription;
    private TimeSpan    _TodoTimeSpan;
    private string      _StartTime;

    public ToDoItemModel(DateTime d, string s)
    {
        _TodoDate=d;
        _TodoDescription=s;
    }

    public string StartTime
    {
        get { return      _StartTime; }
        set {      _StartTime = value; }
    }


    public TimeSpan ToDoTimeSpan
    {
        get { return _TodoTimeSpan; }
        set { _TodoTimeSpan = value; }
    }

    public string ToDoDescription
    {
        get { return _TodoDescription; }
        set { _TodoDescription = value; }
    }

    public DateTime ToDoDate
    {
        get { return _TodoDate; }
        set { _TodoDate = value; }
    }

    public override string ToString()
    {
        return string.Format("Date: {0}- Time: {1}- Duration: {2}- Description: {3}",_TodoDate.ToString("d"),_StartTime,_TodoTimeSpan,_TodoDescription);
    }       
}

public class ToDoListModel: IList<ToDoItemModel>
{
    private readonly IList<ToDoItemModel> _todoList = new List<ToDoItemModel>();

    public ToDoListModel()
    {
         //Hard code this one for testing purpose
        _todoList.Add(new ToDoItemModel(DateTime.Now,"Testing"));
    }

    public int IndexOf(ToDoItemModel item)
    {
        return _todoList.IndexOf(item);
    }

    public void Insert(int index, ToDoItemModel item)
    {
        _todoList.Insert(index,item);
    }

    public void RemoveAt(int index)
    {
        _todoList.RemoveAt(index);
    }

    public ToDoItemModel this[int index]
    {
        get
        {
            return _todoList[index];
        }
        set
        {
            _todoList[index]=value;
        }
    }

    public void Add(ToDoItemModel item)
    {
        _todoList.Add(item);
    }

    public void Clear()
    {
        _todoList.Clear();
    }

    public bool Contains(ToDoItemModel item)
    {
        return _todoList.Contains(item);
    }

    public void CopyTo(ToDoItemModel[] array, int arrayIndex)
    {
        _todoList.CopyTo(array,arrayIndex);
    }

    public int Count
    {
        get { return _todoList.Count; }
    }

    public bool IsReadOnly
    {
        get { return _todoList.IsReadOnly; }
    }

    public bool Remove(ToDoItemModel item)
    {
        return _todoList.Remove(item);
    }

    public IEnumerator<ToDoItemModel> GetEnumerator()
    {
        return _todoList.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

I would like to display on my view a datagrid that is going to bind to ToDoListModel through ToDoListModelView which inherits from ObservableObject. ObservableObject implements INotifyPropertyChanged

public class ToDoListModelView:ObservableObject
{
    ToDoListModel _myModel = new ToDoListModel();
    ...
}

My view is defined by xaml code below: Basically, I would like to bind that datagrid to my ToDoListModel.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestWPF" x:Class="TestWPF.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Grid Margin="1,0,-1,0">
        <DataGrid x:Name="ExistingToDoList" 
          HorizontalAlignment="Left" 
          Margin="33,201,0,0" 
          VerticalAlignment="Top" 
          Height="94" Width="464" 
          Background="#FFF1E6E6" 
          GridLinesVisibility="Horizontal" 
          ItemsSource="{Binding ToDoListModelView}" 
          Style="{DynamicResource ToDoEntry}">
            <DataGrid.DataContext>
                <local:ToDoListModel/>
            </DataGrid.DataContext>
        </DataGrid>
    </Grid>
</Window>

However, after hitting F5, the program ran but I do not see content bound to the data although I do have at least 1 item in the list. What have I done incorrectly? I know it is far from being complete but at the very least, I should see the ToDoItem in the list from the datagrid? What have I missed? Please help. Thanks!

---- My Update ----------------------------------------------

ToDoListModel inherits from List with property ToDoList that return the underlying data

public class ToDoListModel: List<ToDoItemModel>
{
    private  List<ToDoItemModel> _todoList = new List<ToDoItemModel>();
    public List<ToDoItemModel> TodoList
    {
        get { return _todoList; }
    }

    public ToDoListModel()
    {
        _todoList.Add(new ToDoItemModel(DateTime.Now,"Testing"));
    }

I have also corrected the datagrid to bind to ToDoList and set autoGeneratecolumn to true for simplicity and illustration sake.

<Grid Margin="1,0,-1,0" d:DataContext="{d:DesignData /SampleData/ToDoListModelSampleData.xaml}">
    <DataGrid x:Name="ExistingToDoList" 
        HorizontalAlignment="Left" 
        Margin="33,201,0,0" 
        VerticalAlignment="Top" 
        Height="94" Width="464" 
        Background="#FFF1E6E6" 
        GridLinesVisibility="Horizontal" 
        ItemsSource="{Binding TodoList}" 
        Style="{DynamicResource ToDoEntry}" 
        AutoGenerateColumns="True"
        DataContext="{Binding Mode=OneWay}">
        <DataGrid.ItemBindingGroup>
            <BindingGroup/>
        </DataGrid.ItemBindingGroup>
    </DataGrid>
Yael
  • 1,566
  • 3
  • 18
  • 25
user1205746
  • 3,110
  • 11
  • 44
  • 73
  • Based upon what you have provided, it appears that you are not binding the grid to an IEnumerable, but rather to a class that contains an IEnumerable. – Gayot Fow May 20 '14 at 20:57

1 Answers1

1

You set your DataContext property to the "Model" class (something you should never do, always set to a ViewModel class) which does not have a property called "ToDoListModelView", so the binding is failing (you should see this as a System.Data exception in the output window).

Writing ItemsSource="{Binding ToDoListModelView}" says "Look for a property called 'ToDoListModelView' on my DataContext and use it as my ItemsSource". What you probably intended to do was:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestWPF" x:Class="TestWPF.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
       <local:ToDoListModelView/>
    </Window.DataContext>
    <Grid Margin="1,0,-1,0">
        <DataGrid x:Name="ExistingToDoList" 
          ItemsSource="{Binding MyModelProperty}" 
        </DataGrid>
    </Grid>
</Window>

Note that I set the DataContext to a ViewModel object (not a Model) and I set it for the window. This is purely stylistic, but you normally don't set specific control's DataContext property (though there are times when it is necessary). I then bind to a (imaginary) property that would probably be an instance of ToDoListModel.

A few other asides:

  1. If you are inheriting IList you are probably doing it wrong! What's wrong with ObservableCollection or one of the other existing implemenations?
  2. Its "ViewModel" not "ModelView", just as a naming convention

Similar, but not a duplicate (the answer could help you: Setting DataContext in XAML in WPF)

Community
  • 1
  • 1
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • thank you for pointing out the problematic area in my code. ILIST is definitely an overkill for this case. I have changed it to LIST and the naming convention too. I am totally green on this. What I am still a bit fuzzy on is ItemsSource binding: should be bound it to a row or to the entire class? Let say if I change my class ToDoListModel: List with a property ToDoList which return the whole List and a property say ToDoListAt(index). When I bind itemsource, should I bind it to ToDoList or ToDoListAt? Do I have to define each field in the datagrid? – user1205746 May 21 '14 at 11:52
  • You bind to "ToDoList" (ItemsSource is always bound to a collection) and yes, you need to define each field (AutoGenerateColumns can work, but you almost always want it custom). Does that make sense? – BradleyDotNET May 21 '14 at 16:22
  • Yes, it should. I totally understood your explanation. However, it is still not showing the data when the application was launched. It was showing the columns at the design time but not the run time. I wonder what have I missed? I have updated the parts I changed for clarity in my question – user1205746 May 21 '14 at 18:19
  • 1
    @user1205746 Is your DataContext set to ToDoListModel? That still is off, and you shouldn't be deriving from List either. Why not have a ViewModel class that just owns a list? This is still set up in a very funny way. – BradleyDotNET May 21 '14 at 18:25
  • Ah! DataContext!!! I missed that...no wonder! Yeah, I am new so I am trying to get my feet wet, so there are many things that is out of lines. Thank you for pointing it out. Very much appreciated! – user1205746 May 21 '14 at 19:16
  • @user1205746 No problem, remembering the golden rule of inheritance can help with knowing when (and when not) to derive from something: "Inheritance = Is a type of, Composition = "Has a". List should almost *never* be derived from. – BradleyDotNET May 21 '14 at 19:30