4

WP8とMVVMは初めてです。ユーザーがログインすると、さまざまなデータを要求するwp8アプリを作成しました。ピボットヘッダーを動的に作成することができず、バインディングで何かを行っているためかどうかわかりません。 INotifyPropertyChanged、両方または他の何か!!

これが私がこれまでにしたことです:

App.csで定義されたグローバルMainViewModelがあり、ログイン時に返されたすべてのデータが保存されます。

ログインが成功し、データがMainViewModelにロードされたら、これをピボットコントロールを含むテストページにリダイレクトし、ピボットアイテムを動的に作成しようとしています。

これは私のテストページのxaml、つまりMainPivotPage.xamlであり、MainPivotViewModelはローカルリソースとして定義され、ピボットコントロールのデータコンテキストとして設定されているため初期化されます。これを正しく行っているかどうかはわかりませんが、監視可能なコレクションPivotsに格納されているオブジェクトであるPivotItemのヘッダーに「Name」プロパティを割り当てています。プロパティNameは、Pivo​​tIdとNameを含むPivotというクラスにある2つのプロパティの1つです。

<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が作成されたら、Pivo​​tsの監視可能なコレクションを、ログオン時に返されるすべてのデータを含む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);
        }
    }
}

基本クラスに含まれ、INotifyPropertyChangedイベントを生成するために使用されるSetProperty関数を使用します。これにより、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));
        }
    }
}

私のピボットクラスは次のようになります。

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とまったく同じコードですが、2つを別々に保ちたいと思いました。これが私のピボットクラスで必要かどうかはわかりませんが、別のシナリオを試していて、今のところそのままにしておきました。

何が間違っているのかわかりませんが、何を試しても、「Name」プロパティをヘッダーのテキストとして表示することはできません。MainPivotViewModelは、ローカルリソースとして割り当てられたときに初期化されると確信しています。これは、コンストラクターを正しく呼び出して、監視可能なコレクションを初期化するためですが、それだけです。

しかし、それはまったく何も表示しません!

私が気付いたもう1つのことは、BaseViewModelクラスのOnPropertyChangedメソッドの「Set」にブレークポイントを設定すると、どのような場合でも、eventHandlerは常にnullになりますが、何が起こっているのかわかりません。私は間違っています。

私はstackoverflowやその他に関する記事をたくさん持っていますが、何が間違っているのかわかりませんか?誰かアイデアがありますか?

ありがとう。

4

3 に答える 3

3

問題は解決しました!!!

私のコードはずっと正しかったのですが、XAMLはそうではありませんでした!

急で苦痛な学習曲線だと思います!とにかく、stackoverflowに関する記事を見つけた後、解決策を見つけました。これは、基本的に、xamlの記述方法が適切ではないことを示しています。

正直なところ、これが定義どおりに機能しない理由はわかりませんが、要するに、ViewModelにバインドされたときにデータを正しく表示するには、HeaderTemplateとItemTemplateを使用する必要があります。

投稿は次のとおりです。データバウンドピボットがWindowsPhone8の最初のPivotItemをロードしていません

于 2013-03-28T00:54:01.523 に答える
2

いいえ、あなたの答えは間違っていて、あなたのコードは間違っています。

エラー1:

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

ここでは、プロパティまたは変数を変更しても、バインディングは失われます。

エラー2:ItemsControlから派生したすべてのUIElementは、DataContextだけでItemsSourceを更新しないため、INotifyPropertyChangedを無視します。

実例

    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と2つのリストが更新されますが、またはをクリックすると、NewまたはのようNew Propに更新するまでバインディングが失われます。New BindNew Prop Bind

これにより、XAMLリストイベントの再発の問題が明らかになることを願っています。

PS:これはWPFにありますが、WP8とWindowsストアアプリでも同じように機能します。

于 2013-12-10T12:24:43.050 に答える
1

これはすべて私には問題ないように見えるので、コンストラクターが呼び出されたときにApp.MainViewModel.Pivotsがnullまたは空であり(したがって、Pivo​​tコントロールが空)、MainViewModelにピボットの新しいインスタンスを作成することになります。 MainPivotViewModelがインスタンス化された後。

MainPivotViewModel.Pivotsのゲッターにブレークポイントを設定して、いくつかのアイテムがあることを確認しましたか?

于 2013-03-27T20:51:56.687 に答える