繁体   English   中英

使用Observable Collection MVVM绑定透视图控件(Windows Phone 8)

[英]Binding Pivot control with Observable Collection MVVM (windows phone 8)

我是WP8和MVVM的新手。 我创建了wp8应用程序,该应用程序在用户登录后会请求各种数据。我只是无法获取动态创建的数据透视表头,而且我不知道是否是因为我在绑定中进行了某些操作, INotifyPropertyChanged,两者或其他!

到目前为止,这是我所做的:

我在App.cs中定义了一个全局MainViewModel,它将存储在登录时返回的所有数据。

登录成功并将数据加载到MainViewModel后,我将其重定向到包含Pivot控件的测试页,并且尝试动态创建Pivot项。

这是我的测试页面的xaml,即MainPivotPage.xaml和MainPivotViewModel初始化,因为它被定义为本地资源,并被设置为Pivot控件的数据上下文,我不知道我是否做对了,但是我我将“名称”属性分配给PivotItem的标头,这些标头是存储在我可观察的集合Pivots中的对象。 属性Name是我在名为Pivot的类中拥有的2个属性之一,该类包含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时,我将Pivots可观察集合设置为存储在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事件时都无需设置属性名。

这是我的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));
        }
    }
}

我的Pivot类如下所示:

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类上是否需要这样做,但是我尝试了不同的方案,暂时将其保留。

我不知道自己在做什么错,但是无论如何尝试,我都无法将“ Name”属性显示为Header的文本。 我很确定将MainPivotViewModel分配为本地资源时会对其进行初始化,因为它正确地调用了我的构造函数,然后初始化了我的可观察性集合,但仅此而已。

但它绝对不显示任何东西!

我注意到的另一件事是,当我在BaseViewModel类的OnPropertyChanged方法的“ Set”中放置断点时,eventHandler始终为null,无论我认为应该是哪种情况,但我看不到做错了。

我有很多关于stackoverflow的文章,还有其他文章,只是看不到我在做什么错? 任何人有任何想法吗?

谢谢。

问题解决了!!!

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

我猜陡峭而痛苦的学习曲线! 无论如何,在找到有关stackoverflow的文章后,我找到了解决方案,该文章基本上向我表明我编写xaml的方式不合适。

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

这是帖子:数据绑定数据透视表未加载Windows Phone 8中的第一个PivotItem

不,您的回答是错误的,您的代码是错误的。

错误1:

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

在这里,无论您更改属性还是变量都没有关系,绑定将丢失。

错误2:所有从ItemsControl派生的UIElement都将忽略INotifyPropertyChanged,因为它不会仅更新DataContext来更新ItemsSource。

工作实例

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

和用户界面

<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将更新两个列表,但是如果单击“ New或“ New Prop则绑定将丢失,直到像“ New Bind或“ New Prop Bind一样对其进行更新

希望这可以澄清XAML List事件重复出现的问题。

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

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

您是否尝试过在MainPivotViewModel.Pivots的getter中设置断点以确认是否存在某些项目?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM