Binding Pivot control with Observable Collection MVVM (Windows Phone 8)

I am new to WP8 and MVVM. I created a wp8 application that requests various bits of data after a user logs in. I just can't get the Pivots header for dynamic creation, and I don't know if this is due to the fact that I'm doing something in a binding, INotifyPropertyChanged, both or something else !!

Here is what I have done so far:

I have a global MainViewModel defined in App.cs that will save all the data returned during login.

Once the login is successful and the data is loaded into the MainViewModel, I redirect it to the test page containing the Pivot Control, and I try to create Pivot Items dynamically.

This is xaml for my test page, i.e. MainPivotPage.xaml, and my MainPivotViewModel is initialized because it is defined as a local resource and set as a datacontext for the Pivot element, and I don’t know if I am doing this but I assign the Name property to the PivotItem header. which is an object stored in my observed Pivots collection. The property name is one of two properties that I have in a class called Pivot, which contains PivotId and Name.

<phone:PhoneApplicationPage x:Class="TestApp.Views.MainPivotPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModel="clr-namespace:TestApp.ViewModels" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded"> <!--LayoutRoot is the root grid where all page content is placed--> <phone:PhoneApplicationPage.Resources> <viewModel:MainPivotViewModel x:Key="MainPivotViewModel" /> </phone:PhoneApplicationPage.Resources> <Grid x:Name="LayoutRoot" Background="Transparent"> <!--Pivot Control--> <phone:Pivot Title="My Search Options" x:Name="MainPivots" ItemsSource="{Binding Pivots}" DataContext="{StaticResource MainPivotViewModel}"> <phone:PivotItem Header="{Binding Name}"> <!--<Grid/>--> </phone:PivotItem> </phone:Pivot> </Grid> </phone:PhoneApplicationPage> 

When the MainPivotViewModel is created, I set the Pivots observable collection to the same observable collection that is stored in my MainViewModel, which contains all the data returned at login. As you can see, I'm assigning it to a property, not an internal variable, to make sure it calls INotifyPropertyChanged (well ... I think)

 public class MainPivotViewModel : BaseViewModel { private ObservableCollection<Pivot> _pivots = null; public MainPivotViewModel() { Pivots = App.MainViewModel.Pivots; } public ObservableCollection<Pivot> Pivots { get { return _pivots; } set { if (_pivots != value) this.SetProperty(ref this._pivots, value); } } } 

I use the SetProperty function, which is contained in my base class and is used to generate the INotifyPropertyChanged event and allows me to do this without setting the property name every time I need the INotifyPropertyChanged event.

This is the code for my BaseView:

 public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (object.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; } protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { var eventHandler = this.PropertyChanged; if (eventHandler != null) { eventHandler(this, new PropertyChangedEventArgs(propertyName)); } } } 

The My Pivot class is as follows:

 public class Pivot: BaseModel { private int _pivotId; private string _name = string.Empty; public Pivot() { } public Pivot(int pivotId, string name) { PivotId = pivodId; Name = name; } public int PivotId { get { return _pivotId; } set { if (_pivotId != value) this.SetProperty(ref this._pivotId, value); } } public string Name { get { return _name; } set { if (_name != value) this.SetProperty(ref this._name, value); } } } 

You may notice that this inherits from BaseModel. This is exactly the same code as in BaseViewModel, but I would like to keep these two separate. I'm not sure if this is necessary for my Pivot class, but I used a different script and left it for now.

I don’t know what I'm doing wrong, but no matter what I try, I can’t get the “Name” property, which will display as the title text. I am sure that MainPivotViewModel is initialized when it is assigned as a local resource, since I will correctly name my constructor, which then initializes my observable collection, but as much as possible.

But he shows absolutely nothing!

Another thing I noticed is when I set breakpoints in the OnPropertyChanged method in the BaseViewModel class, eventHandler is always null, whatever I assume should not be, but I can't see what I'm doing wrong .

I have a lot of articles on stackoverflow and others, and I just can't see what I'm doing wrong? Does anyone have any ideas?

Thanks.

+4
source share
3 answers

The problem is solved !!!

My code was right all the time, but there was no XAML!

A steep and painful learning curve, I think! Anyway, I found a solution after searching for an article on stackoverflow, which basically showed me that the way I wrote xaml just didn't work.

I will be honest, I don’t understand why this doesn’t work the way it was defined, but, in short, I have to use HeaderTemplate and ItemTemplate to correctly display the data when binding to ViewModel!

Here is the message: Binding Summary Table Does Not Download First PivotItem on Windows Phone 8

+3
source

No, you are wrong, and your code is wrong.

Error 1:

  set { if (_pivots != value) this.SetProperty(ref this._pivots, value); } 

here it doesn’t matter if you change the property or variable, the binding will be lost.

Error 2: all UIElements retrieved from ItemsControl ignore INotifyPropertyChanged because it does not update the ItemsSource with only the DataContext.

Working example

  public ObservableCollection<string> LstLog { get; set; } private ObservableCollection<string> _lstContent = new ObservableCollection<string>(); public ObservableCollection<string> LstContent { get { LstLog.Add("get"); return _lstContent; } set { LstLog.Add("set"); _lstContent = value; } } public MainWindow() { LstLog = new ObservableCollection<string>(); InitializeComponent(); this.DataContext = this; } private void Add_Click(object sender, RoutedEventArgs e) { LstContent.Add("Value added"); } private void New_Click(object sender, RoutedEventArgs e) { _lstContent = new ObservableCollection<string>(); } private void NewBind_Click(object sender, RoutedEventArgs e) { _lstContent = new ObservableCollection<string>(); listObj.ItemsSource = _lstContent; } private void NewProp_Click(object sender, RoutedEventArgs e) { LstContent = new ObservableCollection<string>(); } private void NewPropBind_Click(object sender, RoutedEventArgs e) { LstContent = new ObservableCollection<string>(); listObj.ItemsSource = LstContent; } 

and ui

 <Grid DataContext="{Binding}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition Height="25" /> </Grid.RowDefinitions> <ItemsControl Grid.Row="0" Name="logObj" ItemsSource="{Binding Path=LstLog}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <ItemsControl Grid.Row="1" Name="listObj" ItemsSource="{Binding Path=LstContent}"> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <StackPanel Grid.Row="2" Orientation="Horizontal"> <Button Name="Add" Content="Add" Click="Add_Click"/> <Button Name="New" Content="New" Click="New_Click"/> <Button Name="NewBind" Content="New Bind" Click="NewBind_Click"/> <Button Name="NewProp" Content="New Prop" Click="NewProp_Click"/> <Button Name="NewPropBind" Content="New Prop Bind" Click="NewPropBind_Click"/> </StackPanel> </Grid> 

LstLog is just to see the list of events, if you click add , it will update the two lists, but if you click New or New Prop , the binding will be lost until you update it, as in New Bind or New Prop Bind

We hope this clarifies the recurring issue of the XAML List event.

PS: this is in WPF, but it works the same in WP8 and the Windows Store app.

+2
source

This all looks good to me, so I think App.MainViewModel.Pivots is null or empty when calling the constructor (therefore the Pivot control is empty), and you end up creating a new Pivots instance in your MainViewModel after creating the MainPivotViewModel.

Have you tried to set a breakpoint in getter for MainPivotViewModel.Pivots to confirm that there are some elements?

+1
source

All Articles