MVVMパターンを使用して最初のWPFを構築しています。このコミュニティの助けを借りて、モデル、最初のViewModelおよびビューを作成しました。次に、基本的なアプリケーションレイアウトインターフェイスを設計するアプリに複雑さを追加します。私のアイデアは、少なくとも2つの子ビューと1つのメインビューを用意し、それらを複数のXAMLで分離することです。
- Main.XAML
- Products.XAML
- Clients.XAML
メインには、子ビュー(製品とクライアント)をロードするためのメニューとスペースがあります。 MVVMパターンに従って、ビュー間のすべてのナビゲーションロジックをViewModelに書き込む必要があります。したがって、4つのViewModelを使用することを考えます。
- MainViewModel
- 製品ビューモデル
- ClientsViewModel
- NavigationViewModel
したがって、NavigationViewModelには子ビューモデルのコレクションを含める必要がありますか?アクティブなビューモデルはそうですか?
だから私の質問は:
1)MVVMパターンを使用してメインビューにさまざまなビュー(製品、クライアント)を読み込むにはどうすればよいですか?
2)ナビゲーションviewModelを実装するにはどうすればよいですか?
3)開いているビューまたはアクティブなビューの最大数を制御するにはどうすればよいですか?
4)開いているビューを切り替えるにはどうすればよいですか?
私は多くの検索と読み取りを行ってきましたが、メインビュー内に複数のビューを読み込むWPFを使用したMVVMナビゲーションの簡単な動作例を見つけることができませんでした。その後の多く:
1)現在使用したくない外部ツールキットを使用します。
2)すべてのビューを作成するためのすべてのコードを単一のXAMLファイルに入れます。これは、80近くのビューを実装する必要があるため、良いアイデアとは思えません!
私は正しい道にいますか?特にいくつかのコードに関する助けをいただければ幸いです。
[〜#〜] update [〜#〜]
そこで、@ LordTakkeraのアドバイスに従ってテストプロジェクトを作成しますが、行き詰まります。これは私のソリューションがどのように見えるかです:
私が作成します:
2つのモデル(クライアントと製品)
1つのMainWindowと2つのwpfユーザーコントロール(クライアントと製品)XAML。
3つのViewModel(クライアント、製品、メインViewModel)
次に、各ビューのdataContextを対応するviewModelに設定します。その後、このようなContentPresenterを使用してMainWindowを作成し、それをビューモデルのプロパティにバインドします。
MainWindow.XAML
<Window x:Class="PruevaMVVMNavNew.MainWindow"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="519" Width="890">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.ColumnSpan="2" Background="AntiqueWhite" ></Border>
<Border Grid.Row="1" Grid.RowSpan="2" Background="AliceBlue"></Border>
<Border Grid.Row="1" Grid.Column="1" Background="CadetBlue"></Border>
<ContentPresenter Grid.Row="1" Grid.Column="1" x:Name="ContentArea" Content="{Binding CurrentView}"/>
<StackPanel Margin="5" Grid.Column="0" Grid.Row="1">
<Button>Clients</Button>
<Button>Products</Button>
</StackPanel>
</Grid>
また、これはMainWindowのビューモデルです。
class Main_ViewModel : BaseViewModel
{
public Main_ViewModel()
{
CurrentView = new Clients();
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
したがって、これはデフォルトでクライアントに表示され、次のように表示されます(これはまさに正しいことです!):
したがって、左側のボタンを特定のviemodelに関連付けて、それらをMain viewModelのCurrentViewプロパティにバインドする方法が必要だと思います。どうやってやるの?
PDATE2
@LordTakkeraのアドバイスによると、メインビューモデルを次のように変更します。
class Main_ViewModel : BaseViewModel
{
public ICommand SwitchViewsCommand { get; private set; }
public Main_ViewModel()
{
//CurrentView = new Clients();
SwitchViewsCommand = new RelayCommand((parameter) => CurrentView = (UserControl)Activator.CreateInstance(parameter as Type));
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
DelegateCommandの代わりにRelayCommandを使用しますが、同じように機能すると思います。ボタンを押して型パラメーター文字列をOKにするとコマンドが実行されますが、このエラーが表示されます:
翻訳:値をnullにすることはできません。パラメータ名:タイプ。提案は新しいキーワードを使用してオブジェクトインスタンスを作成します新しいキーワードを配置する場所がわかりません。 CommandParameterを試してみましたが、うまくいきません。何か案が?ありがとう
更新
ここで受け取ったすべてのアドバイスとヘルプ、および多くの作業の後に、最終的なナビゲーションメニューとアプリケーションインターフェイスのベースを示します。
別の「ナビゲーション」ビューモデルが必要かどうかはわかりませんが、メインに簡単に入れることができます。どちらにしても:
「子」ビューを分離するには、「メイン」ビューで簡単なContentPresenterを使用します。
<ContentPresenter Content="{Binding CurrentView}"/>
バッキングプロパティを実装する最も簡単な方法は、UserControl
にすることです。ただし、これはMVVMに違反すると主張する人もいます(ViewModelは "View"クラスに依存しているため)。オブジェクトにすることはできますが、タイプセーフは失われます。この場合、各ビューはUserControlになります。
それらを切り替えるには、何らかの選択コントロールが必要になります。ラジオボタンを使用してこれを実行したことがあります。次のようにバインドします。
<RadioButton Content="View 1" IsChecked="{Binding Path=CurrentView, Converter={StaticResource InstanceEqualsConverter}, ConverterParameter={x:Type views:View1}"/>
コンバーターは非常に単純です。「Convert」では、現在のコントロールがパラメーターのタイプであるかどうかを確認し、「ConvertBack」ではパラメーターの新しいインスタンスを返します。
public class InstanceEqualsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (parameter as Type).IsInstanceOfType(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing;
}
}
コンボボックスまたは他の選択コントロールへのバインドは、同様のパターンに従います。
もちろん、DataTemplates(セレクター、残念ながら私が前にやったことではない)を使用し、マージされた辞書を使用してリソースにロードすることもできます(個別のXAMLを許可します)。私は個人的にユーザー制御ルートを好むので、あなたに最適なものを選んでください!
このアプローチは「一度に1つのビュー」です。複数のビューに変換するのは比較的簡単です(UserControlはユーザーコントロールのコレクションになり、コンバータで.Containsを使用するなど)。
ボタンでこれを行うには、コマンドを使用し、CommandParameterを利用します。
ボタンXAMLは次のようになります。
<Button ... Command={Binding SwitchViewsCommand} CommandParameter={x:Type local:ClientsView}/>
次に、コンバーターからアクティベーターコードを実行するデリゲートコマンド(チュートリアル ここ )があります。
public ICommand SwitchViewsCommand {get; private set;}
public MainViewModel()
{
SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type));
}
これは私の頭上ではありませんが、かなり近いはずです。それがどうなるか教えてください!
さらに情報を提供するかどうかを教えてください!
更新:
あなたの懸念に答えるには:
はい、ボタンを押すたびに、ビューの新しいインスタンスが作成されます。これを簡単に修正するには、Dictionary<Type, UserControl>
事前に作成されたビューとそのインデックスがあります。その点については、Dictonary<String, UserControl>
およびコンバーターパラメーターとして単純な文字列を使用します。欠点は、ViewModelが表示できる種類のビューと密に結合されることです(前述のDictionaryを設定する必要があるため)。
クラスは、他の誰もそれへの参照を保持していない限り、破棄されます(登録されたイベントハンドラを考えてください)。
指摘したとおり、一度に作成されるビューは1つだけなので、メモリについて心配する必要はありません。もちろん、コンストラクタを呼び出すことはできますが、それはそれほど高価ではありません。特に、十分なCPU時間を確保する傾向がある現代のコンピューターではそうです。いつものように、パフォーマンスの質問に対する答えは「ベンチマーク」です。これは、意図した展開ターゲットとソース全体にアクセスして、実際に最高のパフォーマンスを発揮するものを確認できるからです。
ナビゲーションは既に実装されているため、IMHOで最適な選択はMVVMフレームワーク(PRISM、MVVM Light、Chinchなど)を使用することです。独自のナビゲーションを作成する場合は、DataTemplateを試してください。