0

I'm trying to bind a List in my ViewModel to a ListView in my Page but I can't get it to work, the ItemsSource is always null allthough the list is populated with values.

This is how I set the whole thing up.

theese attributes are added to the page tag in my xaml

xmlns:viewmodels ="using:TimeMachine3.ViewModels"
d:DataContext="{d:DesignInstance viewmodels:MasterViewModel}"

My LisstView is defined as

<ListView x:Name="lstMainMaster" ItemsSource="{Binding MyList}" SelectionChanged="lstMainMaster_SelectionChanged">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{x:Bind Name}"/>
                    <TextBlock Text="{x:Bind Number}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

In my master.cs (the page code) I have

    private ViewModels.MasterViewModel viewmodel;

    //constructor
    public master()
    {
        this.InitializeComponent();
        this.Loaded += Master_Loaded;
    }

    private void Master_Loaded(object sender, RoutedEventArgs e)
    {
        viewmodel = new ViewModels.MasterViewModel();
        viewmodel.populate();
    }

And in masterViewmodel (the viewmodel of the page) I have this code

    private ObservableCollection<Month> _myList;
    public ObservableCollection<Month> MyList{ get {return _myList; } }

    public void Populate()
    {
        _myList = new ObservableCollection<Month>(DataBase.GetMonths(2017));
    }
narancha
  • 309
  • 2
  • 16
  • Where do you populate your list with values, can you provide some code? – ivica.moke May 12 '17 at 10:04
  • I have editetd my question with some more code to show how it's done. The viewModel is created in the Page.Loaded event handler. After creation Populate() is called on the viewmodel. In populate() I populate List_myList which the bound property List<> MyList returns in it's get statement – narancha May 12 '17 at 11:20

2 Answers2

1

It is null because you change the reference that MyList points to after you have loaded the view and don't notify the view of the change (via INotifyPropertyChanged). Therefore, the view does not know it needs to refresh the binding.

public void Populate()
{
    _myList = new ObservableCollection<Month>(DataBase.GetMonths(2017));
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyList)));
}

Or equivalent.

Scroog1
  • 3,539
  • 21
  • 26
  • Don't worry. We've all been there :) – Scroog1 May 12 '17 at 11:27
  • I implemented INotifyChanged by calling (in the viewModel) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyList")); After changing the List property.. Then I subscribe to PropertyChanged in the view and reset the ItemsSource. Is that really the correct way of doing it? or is there some nice magical trick that can make it more smoth? – narancha May 12 '17 at 11:36
  • The safer way of doing it would be to add a setter to MyList, which does the event raising and then do `MyList = new ...` instead of directly accessing the backing field. – Scroog1 May 12 '17 at 11:41
  • If you are going to be doing that sort of thing a lot (and I assume that you are), then creating a viewmodel base class is probably the way to go, so you can just call `RaisePropertyChanged();` or similar. – Scroog1 May 12 '17 at 11:42
0

I just realized that I dont have to implement the InotifyChanged interface if my DataContext is set up properly.

I changed the way the data context is set up from

xmlns:viewmodels ="using:TimeMachine3.ViewModels"
enter code here`d:DataContext="{d:DesignInstance viewmodels:MasterViewModel}"

To

<Page
...
xmlns:viewmodels ="using:TimeMachine3.ViewModels"
.../>
<Page.DataContext>
   <viewmodels:MasterViewModel>
</Page.DataContext>

And now the itemssource will be set right after the call to InitializeComponent() and if my list is altered then my ListView will update without adding more code (I don't even have to implement INotifyChanged)

narancha
  • 309
  • 2
  • 16