2016-09-13 10 views
0

私はWPFとMVVMパターンを学習しています。私は物事を正しく実装していない可能性があります。コンポーネントとしてのユーザーコントロール。親ウィンドウへのアクセスがありません

Windows上でコンポーネントを使用できるユーザーコントロールオブジェクトを使用して、アプリケーションのウィンドウのコンポーネントを構築したいと考えています。たとえば、私は、ダイアログウィンドウの唯一のコンポーネントとして、またMainWindowのコンポーネントとして使う予定のNewUnitUserControlを持っています。

NewUnitUserControl.xaml:

<UserControl x:Class="Sample.Views.UserControls.NewUnitUserControl" 
    ...     
    xmlns:local="clr-namespace:Sample.Views.UserControls"> 

    <Grid> 
     <StackPanel Orientation="Vertical"> 
      <Border> 
       <StackPanel Orientation="Horizontal"> 
        <Label>Name:</Label> 
        <TextBox Text="{Binding Unit.Name}" Width="136"/> 
       </StackPanel> 
      </Border> 
      <Border HorizontalAlignment="Center"> 
       <StackPanel Orientation="Horizontal"> 
        <Border> 
         <Button Command="{Binding CreateUnitCommand}">Create</Button> 
        </Border> 
        <Border> 
         <Button Command="{Binding CancelCommand}">Cancel</Button> 
        </Border> 
       </StackPanel> 
      </Border> 
     </StackPanel> 
    </Grid> 
</UserControl> 

NewUnitUserControl.xaml.cs:

namespace Sample.Views.UserControls 
{ 
    public partial class NewUnitUserControl : UserControl 
    { 
     public NewUnitUserControl() 
     { 
      InitializeComponent(); 
      NewUnitViewModel nuvm = new NewUnitViewModel(); 
      DataContext = nuvm; 
      if (nuvm.CloseAction == null) 
      { 
       var window = Window.GetWindow(this); // window evaluates to null 
                // after this line. 
       nuvm.CloseAction = new Action(window.Close); 
      } 
     } 
    } 
} 

NewUnitViewModel.cs

namespace Sample.ViewModels 
{ 
    internal class NewUnitViewModel : INotifyPropertyChanged 
    { 
     //Properties 
     private Unit _unit; 
     public Unit Unit 
     { 
      get { return _unit; } 
      set { _unit = value; OnPropertyChanged("Unit"); } 
     } 

     public Action CloseAction { get; set; } 

     private ICommand _createUnitCommand; 
     public ICommand CreateUnitCommand 
     { 
      get 
      { 
       if (_unitUpdateCommand == null) 
        _unitUpdateCommand = new RelayCommand(param => CreateUnit(), param => true); 
       return _unitUpdateCommand; 
      } 
     } 

     private ICommand _cancelCommand; 
     public ICommand CancelCommand 
     { 
      get 
      { 
       if(_cancelCommand == null) 
        _cancelCommand = new RelayCommand(param => Cancel(), param => true); 
       return _cancelCommand; 
      } 
     } 

     //Constructor 
     public CreateUnitViewModel() 
     { 
      _unit = new Models.Unit(); 
     } 

     //Methods 
     public void CreateUnit() 
     { 
      Debug.Assert(false, String.Format("{0} was created.", Unit.Name)); 
     } 

     public void Cancel() 
     { 
      this.CloseAction(); 
     } 

     #region INotifyProperty Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void OnPropertyChanged(String propertyName) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 

      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     #endregion 
    } 
} 

NewUnitDialogWindow.xaml:

<Window x:Class="Sample.Views.NewUnitWindow" 
     ... 
     xmlns:local="clr-namespace:Sample.Views" 
     xmlns:controls="clr-namespace:Sample.Views.UserControls" 
     > 
    <controls:NewUnitUserControl /> 
</Window> 

NewUnitDialogWindow.xaml.cs

namespace Sample.Views 
{ 
    public partial class NewUnitWindow : Window 
    { 
     public NewUnitWindow() 
     { 
      InitializeComponent(); 
     }  
    } 
} 

完全なソース:私はすぐに直面していますGitHub

問題は私の実装で、私は解決策を使用してユーザーコントロールの親ウィンドウにアクセスできませんよということですhere(NewUnitUserControl.xaml.csのコメントを参照)。私は私の問題の根源はMVVMのパターンを正しく理解していないことです。

+0

'NewUnitViewModel cuvm = new NewUnitViewModel(); DataContext = nuvm; 'これはコードの匂いです。ユーザーコントロールは、コントロールのように動作する必要があります。コントロールはDataContextを設定しません。 TextBoxはTextBoxViewModelのインスタンスを作成し、それをDataContextとして設定しますか?それはあなたがそれに縛られないようにするでしょう。 – Will

+0

@私はあなたのポイントを見ます。私はどのようにユーザーコントロールのDataContextを設定せずに、ユーザーコントロールを介して、自己完結型の再利用可能な "コンポーネント"を作成するか分かりません。ユーザーコントロールを構成するものが私の考え方に間違っていますか?これを行う別の方法はありますか? – ZackDeRose

+0

最も良い方法は、ビューモデルとモデルのグラフを使ってアプリケーションの状態を表現することです。このオブジェクトグラフのルートは、メインウィンドウのDataContextです。コントロールはこのルートのプロパティにバインドされています。例えば、それは、観察可能な子ビューモデルの集合を有することができる。コレクションは、ListViewのItemsSourceにバインドされています。子タイプごとにItemTypeを持つDataTemplatesが定義され、その中にその子のために設計されたUserControlが配置されます。 ListViewがNewUnitViewModel型のDataTemplateを探すと、テンプレートとpoofが見つかります。 – Will

答えて

0

私の問題は、ユーザーコントロールが親ウィンドウが誰かを知らないということです。 Loadedイベントに移動してみてください:

private Window _parentWindow = null; 
public NewUnitUserControl() 
{ 
    InitializeComponent(); 
    Loaded += (s, e) => 
    { 
     _parentWindow = Window.GetWindow(this); 
     /// whatever you are going to do with parent window 
    } 
} 

またMvvmToolkitから(私はそれはそれが呼ばれているものだと思う)プリズムツールキットやIMessengerからIEventAggregatorのようなものを使用して、ボタンのコマンド・アクションからメッセージを送信することができます。

+0

これにより、キャンセルボタンのクリックでダイアログウィンドウが閉じます。ありがとう!これをMainWindowのコンポーネントとして実装するには、MainChindowからNewCharacterUserControlをアンロードするようにCancelボタンを設定します。ボタンのクリックで異なる動作をサポートするには、キャンセルボタンの機能を親のビュー(またはビューモデル)に移動することを検討する必要がありますか? – ZackDeRose

+0

これはあいまいですが、私はusercontrolに独自のアクションを管理させ、メインウィンドウはそれ自身を管理します。ユーザーコントロールはデータを処理し、終了またはキャンセルを通知します。メインウィンドウは信号を受信し、必要な処理を行い、メインウィンドウを更新し、データを保持し、ダイアログを閉じます。 –

0

親ウィンドウにアクセスしようとしている方法がわかりません。

var window = Window.GetWindow(this);

次のコードを試してください。私はあなたのものを編集しましたので、ちょうどそれを置き換えてください:)

+0

このコードを入力すると、コンパイラエラーが発生します。 'NewUnitUserControl'に 'ParentForm'の定義がなく、 'NewUnitUserControl'タイプの最初の引数を受け入れる拡張メソッド 'ParentForm'がありませんでした。 usingディレクティブまたはアセンブリ参照がありません) 私に何か不足していますか? – ZackDeRose

+0

インポートが見つからないため、エラーにはなりません。適切なリファレンスDLLを使用していますか?少なくともSystem.Windows.Formsを参照として持つ必要があります。また、次のように使用する必要があります:using System.Windows.Forms;あなたのUserControlはこの名前空間の中にありますか、それともあなたが作成したカスタムクラスですか?また、NewUnitUserControlはフォームに配置する必要があります。別の問題が.NET Frameworkのバージョンになる可能性があります。私はバージョン4以上を使用しています。以前のバージョンでは、実装されていないものもありました。私はあなたにいくつかのヒントをお願いします。 – bitQUAKE

関連する問題