1

現在、WPF (Surface 2.0) で開発を行っており、アプリケーションのほとんどの部分で MVVM パターンを使用しています。残念ながら、私は現在かなり複雑な問題に直面しています。皆さんが私を助けてくれることを願っています:

View とそれに属する ViewModel があります。View には、ViewModel 内のプロパティへの双方向バインディングが含まれています。

<pb:PivotBar ItemsSource="{Binding PivotBarEntries}" 
SelectedItemIndex="{Binding SelectedPivotItemIndex, Mode=TwoWay}" />

(...)

<local:SomeOtherView />

ビューが最初に読み込まれるときに、SelectedPivotItemIndex のセッターが呼び出されます。ビューの残りの部分が読み込まれる前にセッターが呼び出されることを除いて、これは問題ありません。セッターは (MVVMLight の Messenger を介して) メッセージをビュー内で後で作成される他のビューモデルに送信するため、これは問題です。これらのメッセージは、これまで受信者が登録されていないため、宛先に到達しません。

public int SelectedPivotItemIndex
{
    get
    {
        return this.selectedPivotItemIndex;
    }
    set
    {
        if (value != this.selectedPivotItemIndex)
        {
            this.selectedPivotItemIndex = value;
            this.ReportPropertyChanged("SelectedPivotItemIndex");

            (...)

            ChangeSomeOtherViewModelProperty msg = new ChangeSomeOtherViewModelProperty { Property = newValueCalculatedBefore };
            Messenger.Default.Send<ChangeSomeOtherViewModelProperty>(msg);
        }
    }
}

私が今考えることができる唯一の解決策は、ViewModel で LoadedEventHandler を作成し、SelectedPivotItemIndex セッターを再度呼び出すことです。しかし、私はそれがあまり好きではありません:

  • 一度、setter が再度実行されます (これにより、メッセージに渡されるかなり大きなコレクションが作成されます)。それが本当にパフォーマンスに影響を与えるかどうかはわかりませんが、それでも不要なようです。
  • 第二に、ロードされたイベントですべてのプロパティを手動で初期化する必要があるため、ハック的でエラーが発生しやすいようです。

セッターを手動で呼び出すよりも、この問題の解決策はありますか?

4

1 に答える 1

2

最初にビューモデルのチュートリアルはありませんが、そこには多くの例があると確信しています。最初にviewmodelインスタンスを取得してから、wpfに(データテンプレートを介して)ビューを作成させます。

メインビューに PivotBarEntries を含むビューを表示する必要があるとしましょう。したがって、ここで行うことは、メインビューモデル (DI、MEF、new() など) にピボットバービューモデルを作成することです。mainviewmodel は pivotvw をプロパティとして公開し、mainview の ContentPresenter.Content にバインドします。少なくとも、pivotvw DataType の DataTemplate を作成する必要があります。

<DataTemplate DataType="{x:Type local:PivotViewModel>
 <view:MyPivotView/>
</DataTemplate>

それは最初にviewmodelについてです。vmが最初に作成されるため、ビューのロードイベントに依存する必要はもうありません。

もちろん、特定の問題については、メッセンジャーをリッスンするすべてのコンポーネント(VM)を作成する必要があることを確認する必要があります

あなたのxaml

<ContentPresenter Content="{Binding MyPivotDataVM}" />
<ContentPresenter Content="{Binding MySomeOtherStuffVM}" />

最初に表示する代わりに

<pb:PivotBar ItemsSource="{Binding PivotBarEntries}" 
SelectedItemIndex="{Binding SelectedPivotItemIndex, Mode=TwoWay}" />

(...)

<local:SomeOtherView />

編集:最初にビューモデルの非常に単純な例。ps: MEF で DI を使用して、オブジェクト パスを作成します。

app.xaml

<Application x:Class="WpfViewModelFirst.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfViewModelFirst="clr-namespace:WpfViewModelFirst">
<!--StartUp Uri is removed-->
<Application.Resources>
    <!--comment these datatemplates and see what happens-->
    <DataTemplate DataType="{x:Type WpfViewModelFirst:PivotViewModel}">
        <WpfViewModelFirst:PivotView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type WpfViewModelFirst:OtherViewModel}">
        <WpfViewModelFirst:OtherView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type WpfViewModelFirst:OtherChildViewModel}">
        <WpfViewModelFirst:OtherChildView/>
    </DataTemplate>
</Application.Resources>
</Application>

app.xaml.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        //to be fair, sometimes i create the ApplicationRoot(JUST MainWindow with view first, and just the rest with viewmodel first.)
        var mainvm = new MainViewModel();
        var mainview = new MainWindow {DataContext = mainvm};
        this.MainWindow = mainview;
        this.MainWindow.Show();
    }
}

mainview.xaml

<Window x:Class="WpfViewModelFirst.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>        
    <TextBlock Text="{Binding MyProp}" Grid.ColumnSpan="2" Grid.Row="0"/>        
    <ContentPresenter Content="{Binding MyPivot}" Grid.Row="1" Grid.Column="0" />
    <ContentPresenter Content="{Binding MyOther}" Grid.Row="1" Grid.Column="1" />        
</Grid>
</Window>

mainviewmodel.cs

public class MainViewModel
{
    public string MyProp { get; set; }
    public PivotViewModel MyPivot { get; set; }
    public OtherViewModel MyOther { get; set; }

    public MainViewModel()
    {
        this.MyProp = "Main VM";
        this.MyPivot = new PivotViewModel();
        this.MyOther = new OtherViewModel();
    }
}

ピボットビューモデル

public class PivotViewModel
{
    public string MyProp { get; set; }
    public ObservableCollection<string> MyList { get; set; }

    public PivotViewModel()//Dependency here with constructor injection
    {
        this.MyProp = "Test";
        this.MyList = new ObservableCollection<string>(){"Test1", "Test2"};
    }
}

その他のViewmodel

public class OtherViewModel
{
    public string MyProp { get; set; }
    public OtherChildViewModel MyChild { get; set; }

    public OtherViewModel()
    {
        this.MyProp = "Other Viewmodel here";
        this.MyChild = new OtherChildViewModel();
    }
}

その他の子Viewmodel

public class OtherChildViewModel
{
    public string MyProp { get; set; }

    public OtherChildViewModel()//Dependency here with constructor injection
    {
        this.MyProp = "Other Child Viewmodel";
    }
}

ピボットビュー

<UserControl x:Class="WpfViewModelFirst.PivotView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding MyProp}" Grid.Row="0"/>
    <ListBox ItemsSource="{Binding MyList}" Grid.Row="1"/>
</Grid>
</UserControl>

その他見る

<UserControl x:Class="WpfViewModelFirst.OtherView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition  />
        <RowDefinition  />
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding MyProp}" Grid.Row="0" />
    <ContentPresenter Content="{Binding MyChild}" Grid.Row="1"/>
</Grid>
</UserControl>

その他子表示

<UserControl x:Class="WpfViewModelFirst.OtherChildView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
    <TextBlock Text="{Binding MyProp}" />
</Grid>
</UserControl>
于 2012-06-06T09:50:38.813 に答える