8

I'm trying to put together what should be a very basic MVVM sample, but I'm having trouble getting it to work. Basically, I want to bind an ObservableCollection to a ListBox, and have a search option for the user to search for other items. Upon searching, the ListBox should be refreshed as the collection will change. Here is my code:

Model:

public class Item
   public string Name { get; set; }
}

ViewModel:

public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Item> _items { get; set; }
    public ObservableCollection<Item> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            RaisePropertyChanged("Items");
        }
    }

    public void GetDefaultItems()
    {
        ObservableCollection<Item> temp = new ObservableCollection<Item>();
        temp.Add(new Item() { Name = "abc" + " 1" });
        temp.Add(new Item() { Name = "def" + " 2" });
        temp.Add(new Item() { Name = "ghi" + " 3" });
        Items = temp;
    }


    public void Search(string query)
    {
       ObservableCollection<Item> temp = new ObservableCollection<Item>();
       temp.Add(new Item() { Name = query + " 1" });
       temp.Add(new Item() { Name = query + " 2" });
       temp.Add(new Item() { Name = query + " 3" });
       Items = temp;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

View:

<Grid x:Name="LayoutRoot">
    <ListBox ItemsSource="{Binding}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock x:Name="Name" Text="{Binding Name}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

MainPage.xaml:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <TextBox x:Name="txtSearch"/>
        <TextBlock Text="Items:" />
        <views:ItemView x:Name="ItemsOnPage" />
    </StackPanel>
</Grid>

and finally, MainPage.xaml.cs:

public partial class MainPage : PhoneApplicationPage
{
    private ViewModel vm;

    // Constructor
    public MainPage()
    {
        InitializeComponent();
        txtSearch.KeyUp += txtSearch_KeyUp;
        vm = new ViewModel();
    }

    void txtSearch_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            vm.Search(txtSearch.Text);
        }
    }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        vm.GetDefaultItems();
        ItemsOnPage.DataContext = vm.Items;
    }
}

So what happens is, I can see the default items load the first time but when I search, the list does not get refreshed. It is all hard-coded right now so if the search does work I should see 3 items for whatever they searched for.

What I've noticed though is that if I set up a breakpoint in the ViewModel at RaisePropertyChanged, this.PropertyChanged is always null, so it never makes it inside the if statement. I've seen examples that use INotifyPropertyChanged on the model, but in this case since I need to be notified when the collection changes, it seemed correct to use on the view model. This could be wrong, but I'm not sure how else to set it up.

Can anyone see what I'm doing wrong?

lhan
  • 4,585
  • 11
  • 60
  • 105
  • Try putting `ItemsOnPage.DataContext = vm.Items;` in your MainPage constructor. – siger Jan 24 '14 at 05:27
  • so just move it from `OnNavigatedTo` up to the constructor? I tried that just now, made no difference. – lhan Jan 24 '14 at 05:30
  • maybe you need some other event for `windows-phone-8` i think i have read about it a while ago but i'm not quite sure basically it's because you haven't the full .Net Lib support for phones – WiiMaxx Jan 24 '14 at 08:15
  • Great example for me to implement an Xamarin forms viewmodel – 27k1 Feb 04 '17 at 10:28

2 Answers2

5

try doing this

  1. set ItemSource to Items

  2. Clear and Add Data Items

    String preQuery="";
    public void Search(string query)
    {
       if(preQuery==query)
          return;
    
       Items.Clear();
    
       Items.Add(new Item() { Name = query + " 1" });
       Items.Add(new Item() { Name = query + " 2" });
       Items.Add(new Item() { Name = query + " 3" }); 
    }
    

Hope this helps.

AlexisXu
  • 126
  • 3
  • Wow, that works. I'm noticing though, now, that my breakpoint inside `RaisedPropertyChanged` doesn't get hit, but the values are still updated correctly. Should that happen? – lhan Jan 24 '14 at 13:17
  • What I mean is that do I even need to implement `INotifyPropertyChanged` for this? From the list of items on the page, the user won't be able to edit them so I won't need to re-bind the other way. The only thing I will need to do with each of the list items is give them an event handler so, for example, tapping on one could take you to a different page. – lhan Jan 24 '14 at 14:07
  • @lhan16 technically you don't need to implement `INotifyPropertyChanged` in this case. That's because the `ObservableCollection` implements `INotifyCollectionChanged`, which notifies the view when you Add, Clear, etc. – McGarnagle Jan 24 '14 at 19:01
0

It looks like value of items collection is not being refreshed on UI.

Try this-

Code in MainPage.cs

   private ViewModel _viewModel;

MainPage Constructor

  _viewModel = new ViewModel();
  this.DataContext = _viewModel ;

Binding in MainPage XAML

<views:ItemView x:Name="ItemsOnPage" DataContext="{Binding Path=Items,UpdateSourceTrigger=PropertyChanged}"/>
Ramashankar
  • 1,598
  • 10
  • 14
  • Thanks. I will try this tonight. One question though, if I move the binding to the MainPage.xaml, should my `ListBox` in View.xaml have *any* binding (other than for `Name` in the `TextBlock`)? – lhan Jan 24 '14 at 13:49
  • @lhan16: I think there shouldn't if any issue, you can bind it with any property you want, as long as that property belongs to its datacontext. – Ramashankar Jan 24 '14 at 13:53