2013-03-27 105 views
4

我是WP8的新手& MVVM。我创建了wp8应用程序,它在用户登录后请求各种数据位。我只是无法让我的pivot标题动态创建,我不知道是否因为我在绑定中做了某些事情, INotifyPropertyChanged,两者或别的东西!使用Observable Collection MVVM绑定枢轴控件(windows phone 8)

这是我迄今所做的:

我得在App.cs定义的全局MainViewModel将存储在用户登录时都返回的数据。

一旦登录成功并且数据已被加载到MainViewModel中,我将其重定向到包含数据透视控件的测试页面,并试图动态创建数据透视项目。

这是我的测试页的xaml,即MainPivotPage.xaml和我的MainPivotViewModel被初始化,因为它被定义为本地资源,并且设置为数据透视控件的数据上下文,我不知道我是否在做这个权利,但我将“名称”属性分配给PivotItem的Header,这是存储在我的可观察集合Pivots中的对象。属性Name是我在一个名为Pivot的类中包含PivotId和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> 

在创建MainPivotViewModel,我的支点观察集合设置为存储在我的MainViewModel同样观察到的集合包含在登录所有返回的数据。正如你可以看到我把它分配给属性,而不是内部变量,以确保它会触发INotifyPropertyChanged的(嗯...,我认为)

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); 
     } 
    } 
} 

我使用的是包含在我的基地的SetProperty功能类并用于生成INotifyPropertyChanged事件,并允许我在不需要设置属性名称的情况下每次需要INotifyPropertyChanged事件时都这样做。

这是我的基本视点代码:

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)); 
     } 
    } 
} 

我的透视类看起来是这样的:

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); } 
    } 
} 

您可能注意到,这个人是从BaseModel继承。这与BaseViewModel中的代码完全相同,但我希望将两者分开。我不是事件,肯定这是我的Pivot类需要的,但我尝试了不同的场景,现在就把它留下。

我不知道我在做什么错,但无论我尝试什么,我都无法使“名称”属性显示为标题的文本。我很确定MainPivotViewModel被初始化时,它被分配为本地资源,因为它正确地调用我的构造函数,然后初始化我的可观察集合,但这就像它一样。

但它绝对没有显示!

我注意到的另一件事是,当我在BaseViewModel类的OnPropertyChanged方法的“Set”中放置断点时,eventHandler始终为空,无论我认为应该如何,但我可以'看看我做错了什么。

我有很多关于stackoverflow和其他人的文章,我只是不明白我做错了什么?任何人有任何想法?

谢谢。

+0

你是否在任何地方设置了你的页面的'DataContext'? – 2013-03-27 07:12:26

+0

嗨,是的,它设置在上面的xaml中。这只是不可见,但如果你滚动,你会看到它被设置。 – Thierry 2013-03-27 13:00:22

+0

你有一个'Pivot'的ObservableCollection。这些是你自己的班级吗?如果是这样,他们看起来像什么? – 2013-03-27 15:00:18

回答

3

问题已解决!!!

我的代码一直都是正确的,但XAML不是!

陡峭而痛苦的学习曲线我猜!无论如何,我找到了一个关于stackoverflow的文章后找到了一个解决方案,它基本上向我展示了我编写xaml的方式并不合适。

我会老实说,我不明白为什么这不符合它的定义方式,但总之,我必须使用HeaderTemplate和ItemTemplate才能在绑定到ViewModel时正确显示数据!

下面是帖子:Databound pivot is not loading the first PivotItem in Windows Phone 8

1

这一切对我来说都很好,所以我在考虑在构造函数被调用时(因此Pivot控件是空的)App.MainViewModel.Pivots为null或空,并且最终创建一个新的实例在MainPivotViewModel实例化后,您的MainViewModel中的枢轴。

您是否尝试过在MainPivotViewModel.Pivots的getter中放置断点以确认是否有一些项目?

+0

我已经在我的代码中放入了各种断点,并且我可以清楚地看到

a)MainPivotViewModel被初始化并且仅在我离开本地资源xaml定义时才被初始化。

b)可观察集合Pivots被设置为App.MainViewModel.Pivots

c)App.MainViewModel.Pivots包含数据。这个特定的集合包含3个枢轴返回。

如上所述,在单步执行代码时,我发现var eventHandler = this.PropertyChanged;总是返回null。会不会是这样,但为什么? – Thierry 2013-03-27 22:48:25

+0

我注意到的另一件事是当我遍历代码时,我的DataContext是通过xaml设置的,但是当我在后面的代码中查看DataContext时,它是空的?这是为什么?此外,必须触发某些东西,因为它不会显示标题的名称,它会在数据透视的查看部分和标题中显示MyTestApp.Models.Pivot。奇怪!这就像它正在做一个object.ToString(),而不是实际上试图显示该属性......基于上面的xaml,你认为它是正确的吗? – Thierry 2013-03-27 23:29:31

2

没有你的答案是错的,你的代码是错误的。

错误1:

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

在这里如果你更改属性或变量的结合将会丢失它的DOS没有关系。

错误2:从ItemsControl派生的所有UIElements忽略INotifyPropertyChanged,因为它不会更新ItemsSource只是DataContext。

工作实例

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; 
    } 

和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是刚才看到的事件列表,如果你点击add将更新两个列表,但如果点击NewNew Prop绑定丢失,直到您更新它,如在New BindNew Prop Bind

希望这将澄清XAML List事件经常发生的问题。

PS:这是在WPF中,但在WP8和Windows Store应用程序中工作相同。