2016-04-10 23 views
0

私は、親ビューであるGeneralViewを持っていて、それが開くと親に続いて子が開きます。私はナビゲーションを実装し、ボタンを横に(UserPageのように)保ちたい。目的の動作に移り、私が現在行っているコードが続きます。MVVMナビゲーションの親ビューと子ビュー

ChildViewをどのように実装したかは変わりませんが、HomeView、つまりFriendsViewにとどまります。

enter image description here

ので説明ログイン> GeneralView(つまり、ホームですぐに開きます)>についてでクリックして、childViewがAboutViewに変わり、HomeViewが再び示されたされているホームをクリックします。私が持っているもの

GeneralView

<UserControl x:Class="WpfWHERE.View.GeneralView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:WpfWHERE.View" 
     xmlns:ViewModel="clr-namespace:WpfWHERE.ViewModel" 
     mc:Ignorable="d" 
     d:DesignHeight="600" d:DesignWidth="800"> 
<UserControl.DataContext> 
    <ViewModel:GeneralViewModel/> 
</UserControl.DataContext> 
<UserControl.Resources> 
    <DataTemplate DataType="{x:Type ViewModel:FriendsViewModel}"> 
     <local:FriendsView /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type ViewModel:AboutViewModel}"> 
     <local:AboutView /> 
    </DataTemplate> 
</UserControl.Resources> 
<DockPanel Margin="0,0,0,0"> 
    <StackPanel Orientation="Horizontal"> 
     <Grid> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="Auto" MaxWidth="200"/> 
      </Grid.ColumnDefinitions> 
      <Image Grid.Column="1" x:Name="userImage" Source="/Resources/Images/profileImage.png" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="161" Width="180" /> 
      <Label Grid.Column="1" x:Name="labelName" Content="NameHere" HorizontalAlignment="Left" Margin="10.4,171,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.536,1.344" Height="26" Width="67"/> 
      <StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200"> 
       <MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/> 
       <MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/> 
       <MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/> 
       <MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="AboutViewModel" x:Name="About"/> 
      </StackPanel> 
     </Grid> 
     <ContentControl Content="{Binding Current_ViewModel}" Height="600" Width="600"/> 
    </StackPanel> 
</DockPanel> 

GeneralViewModel

class GeneralViewModel:AViewModel 
{ 
    public GeneralViewModel() 
    { 
     this.AddViewModel(new FriendsViewModel() { DisplayName = "Friends", InternalName = "FriendsViewModel" }); 
     this.AddViewModel(new AboutViewModel() { DisplayName = "About", InternalName = "AboutViewModel" }); 
     this.Current_ViewModel = this.GetViewModel("FriendsViewModel"); 
    } 
} 

AViewMoデルインタフェース

public abstract class AViewModel : ViewModelBase 
{ 
    public string Name { get; set; } 
    public RelayCommand<string> SelectViewCommand { get; set; } 

    public AViewModel() 
    { 
     SelectViewCommand = new RelayCommand<string>(OnSelectViewCommand); 
    } 

    private static ObservableCollection<ViewModelBase> _ViewModels; 
    public static ObservableCollection<ViewModelBase> ViewModels 
    { 
     get { return _ViewModels; } 
     set { _ViewModels = value; } 
    } 

    public void AddViewModel(ViewModelBase viewmodel) 
    { 
     if (ViewModels == null) 
      ViewModels = new ObservableCollection<ViewModelBase>(); 

     if (!ViewModels.Contains(viewmodel)) 
      ViewModels.Add(viewmodel); 
    } 

    public ViewModelBase GetViewModel(string viewmodel) 
    { 
     return ViewModels.FirstOrDefault(item => item.InternalName == viewmodel); 
    } 

    private void OnSelectViewCommand(string obj) 
    { 
     switch (obj) 
     { 
      case "ExitCommand": 
       Application.Current.Shutdown(); 
       break; 
      default: 
       this.Current_ViewModel = this.GetViewModel(obj); 
       break; 
     } 
    } 

    private ViewModelBase _Current_ViewModel; 
    public ViewModelBase Current_ViewModel 
    { 
     get { return _Current_ViewModel; } 
     set { _Current_ViewModel = value; OnPropertyChanged("Current_ViewModel"); } 
    } 
} 
+0

あなたが直面している問題は何ですか? – sexta13

+0

@ sexta13 AboutButtonをクリックしても、FriendsViewに表示が変わらないちょうど私は2つのchildviews(友人と約)があることを明確にする。友人は一般の時と同じ時に読み込まれ、私は同様に表示されるホームをクリックする場合は、それが欲しい。約クリックすると表示されるはずです。 – Antoine

+0

あなたはINotifyPropertyを実装する必要があります。ビューは何かが変更されたことを知るようにします。 – sexta13

答えて

1

これを試してみてください....

変更この...これに

 <StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200"> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="AboutViewModel" x:Name="About"/> 
     </StackPanel> 

...私は「削除した

 <StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200"> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding SelectViewCommand}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/> 
      <MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding SelectViewCommand}" CommandParameter="AboutViewModel" x:Name="About"/> 
     </StackPanel> 

注意DataContext 'と' ElementName 'をMenuItemsから取得します

INotifyPropertyはすでに問題はその名前の要素が存在しないのElementName = GeneralView ...とあったViewModelBase

UPDATE 1

に実装されています。 base_View XAMLの上部にx:Name = "GeneralView"を追加することができましたが、ContentControlがBase_ViewModelのBase_ViewModelにバインドされている必要はありません....

ボタンを押してビュー 'ContentControlがバインドされているプロパティの値を実際に変更しているため、ContentControlがバインドされているクラスの同じインスタンスで正しいSelectViewCommand関数を呼び出す必要があります。

あなたが「LogOn_View」で、私はここで私は私がBase_V年代に表示されるビューを変更したいので、それはだ、Base_VでSelectViewCommandを呼び出しています

Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}", CommandParameter="Main_ViewModel" 

を呼び出すことがわかりますデモで

ContentControlに

私はここで私は私が見るDISを変更したいので、それはだ、Main_ViewModelでSelectViewCommandを呼び出しています

Command="{Binding SelectViewCommand}", CommandParameter="MainV1_ViewModel" 

を呼び出すMain_Viewで

私は上記の話をしていますデモコードをしたい人のためにManiViewのContentControlに

で遊んだ、あなたはここにも...

http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar

、コードを少し更新し、それを見つけることができます。.. 。.....これでAviewModelでAddViewModel機能を置き換える

public void AddViewModel(ViewModelBase viewmodel) 
    { 
     if (ViewModels == null) 
      ViewModels = new ObservableCollection<ViewModelBase>(); 

     var currentVNs = (from vms in ViewModels where vms.InternalName == viewmodel.InternalName select vms).FirstOrDefault(); 
     if (currentVNs == null) 
      ViewModels.Add(viewmodel); 
    } 
+0

これは実装されていますが、彼はRaisePropertyChangedを呼び出すことはありません。そのため、更新する必要があるものは表示されません。 – sexta13

+0

あなたは男です。出来た。あなたは私が理解し、同じエラーを繰り返しないように説明することができますか?あなたがRaisePropertyChangedと呼ぶ方法@ sexta13? – Antoine

+0

@monty再びお悔やみして申し訳ありません。しかし、私は別のビューモデルからビューモデルを開くことができる方法を知っていますか? OKButton(LoginViewModelで処理されるコマンドを起動します)コマンドがデータをWebサービスに送信し、そこから答えが得られます。ここからviewModelを開き、クラスを送信します。可能だ? – Antoine

1

OKそう...いくつかのより多くの思考の後....インターネット上のチュートリアルのコードの多くの問題は、それがかなりあるということです基本的。 データ(モデル) < =>ビューの間の関係の実装は、味の問題のようです。しかし、私はそれがひどくやり遂げられたことを今までにしか見ていませんでした。個人的に言えば、私が取り組んできたWPFアプリケーションのほとんどは、絶対的なごみ、管理不能な、管理不能なスパゲッティコードでした...あなたは明らかに、オリジナルの開発者が「学習した」ことを彼らが見て、すべてがどこにでもあり、一貫性...私は通常、プロジェクトの途中でやっているので、私はその方法を続ける以外に選択肢がないので、これまで考えていたことはありませんでした。

「モデル」はデータ構造を定義し、「表示」はそのデータをユーザーに表示し、ViewModelはデータ(モデル)を表示可能なものに変換する中間のビットですユーザに提供する。

ViewModelではObservableCollections(データ(モデル)のリスト)とデータの単一のインスタンス(モデルの単一のインスタンス)があります。 「ビュー」はObservableCollections(および単一のModelインスタンス)にバインドして、そのデータを表示します(XAMLとTemplatingなどのマジックを使用して)。

オブジェクト(クラス)をViewModelに渡す場合、私はあなたが実際にそれをする必要があるとは思わない。 ViewModelに、表示するデータを表すプロパティを作成し、必要に応じて 'Source'からデータを取得します(通常はViewが表示されているが、タイマー\スレッドなどの周期性もあります) )....

私のデモコード(ダウンロード)の主な問題は、ViewModelのプロパティをリフレッシュする方法がなかったため、アプリケーションの開始後にViewModelsのコンストラクタが呼び出されていないことでした。データソース....

は非常によくこれを行うには良い方法があるかもしれませんが、私は、その後のコンストラクタにに加入することができます「ViewModelBase」

public delegate void MyEventHadler(); 
    public event MyEventHadler Initialize; 

    public void InitializeFunction() 
    { 
     if (Initialize != null) 
      Initialize.Invoke(); 
    } 

た場合に初期化と呼ばれるイベントを導入することでその問題を修正しましたそれぞれのViewModel

public MainV2_ViewModel() 
    { 
     this.Initialize += MainV2_ViewModel_Initialize; // our new Event 
    } 

そして、私たちはどこかからこのViewModelに\ビューにナビゲートするときに呼び出されるイベントスタブ....

private void MainV2_ViewModel_Initialize() 
    { 
     // So here we are retrieving a List of All users from the WCF Service 
     this.AllUsers = new ObservableCollection<ServiceReference1.User>(DataAccessLevel.sr1.GetAllUsers()); 
     // Now our AllUsers property has been updated and the View will display the new data 
    } 
AviewModel \ Current_ViewModelプロパティセッターでは、あなたが別のViewModelに\ビューから切り替えたときに今、初期化イベントが発生\と呼ばれる

private ViewModelBase _Current_ViewModel; 
    public ViewModelBase Current_ViewModel 
    { 
     get { return _Current_ViewModel; } 
     set { 
      _Current_ViewModel = value; 
      // the Constructor of the ViewModel never gets called more that once on App Start 
      // so we have to implement/raise our own event when changing from one View to another. 
      if (Current_ViewModel != null) 
       Current_ViewModel.InitializeFunction(); // InitializeFunction will fire the event in this ViewModel, we can now initialise the properties. 
      OnPropertyChanged("Current_ViewModel"); } 
    } 

これはその後されるのViewModelの「初期化」イベントを発生させますデータを更新する機会を与えてくれました.....

デモコードは完全に機能するアプリケーションになりました(もちろん、まだ作業が必要です)。それは次のような機能...

を提供する新しいユーザーを登録:: UserNameとパスワード

で新しいユーザーを作成すると、ユーザー名とパスワードを使用して、既存のユーザーにオン::ログオンユーザーをログイン

回復ユーザー(忘れたパスワード)::登録されたユーザーのパスワードをリセットする(既存のUserNameと新しいパスワードを使用)

さらに、エラーメッセージがWCFサービスから返され、エラービューで表示されます(LogOnError_ViewModelおよびLogOnError_View )何かが間違っているとき(間違ったパスワードなど)

、それが唯一のVisual Studioから実行することができますWCFサービスを使用しているため、デモコードは、WCFサービスとWPFアプリケーションが付属しています....ここ

http://www.mediafire.com/download/881yo6reo55tm8l/Navigation1_WCF_EF_05052016.rar

をデモコードを見つけますIDE(IISにWCFサービスを展開しない限り)。 WCFサービスはEntity Frameworkを使用して、新しいユーザーを初めて登録するときにユーザーデータを保存するデータベースを作成する必要があります。

多分コードを改善したり、アイディアを得ることができます.....

+0

:私はすべての希望を失い始めていた。 Webサービスリクエストを作成するには、LogOn_ViewModel> public LogOnCommandParameter {//ここでリクエストします}の中に実装する必要がありますか?私が他のviewmodelに渡したいクラスは、私がここに持っているユーザ(ユーザ名+パスワード)ではなく、ウェブサービスによって回答されたユーザです(私はOnSelectViewCommandの代わりにすべきですか?) – Antoine

+0

@Antoine ...今週少し忙しかった私の変更されたデモコード\答えを見てください.... – Monty

関連する問題