2013-08-29 27 views
6

まず最初に、いくつかのコンテキスト。問題に精通している場合は、BindingExpression部分にスキップしてください。これはWPFでの私の最初の主要なプロジェクトなので、私はまだMVVMのパターンにはまったく新しいです。 Hereは私が見つけた唯一の他の同様の質問であり、その不潔な答えが本当に私をあまり熱狂させない。MVVMアプリケーションでViewModelsを切り替えるときのBindingExpressionパスエラー

私は.NET 3.5 WPFアプリケーションをビルドしていますが、私はMVVM(自分自身、フレームワークは実装されていません)を使用しています。この中で、私はViewsViewModelsの番号を持っています。これらはそれぞれマスターApplicationViewApplicationViewModelの中にあります。

私は、ビューを変更する方法はそうのように、ApplicationViewでXAMLのDataTemplate要素を使用している:

<DataTemplate DataType="{x:Type viewmodels:InitViewModel}"> 
    <views:InitView /> 
</DataTemplate> 

そして本体に私はApplicationViewModel

<ContentControl Content="{Binding CurrentPageViewModel}"/> 
内のプロパティにバインドするContentControlにを持っています

私はアプリケーションを実行すると、これはすべて正常に動作するように見え、まさに意図されているとおりです。しかし、実行後にデバッグ出力を見ると、多くのエラーが発生します。BindingExpression

たとえば、これは1つです。私はInitViewModelに財産、SplashTextを持っています。これは、スプラッシュ画面(InitView)のテキストブロックにバインドされています。

System.Windows.Data Error: 39 : BindingExpression path error: 'SplashText' property not found on 'object' ''MainMenuViewModel' (HashCode=680171)'. BindingExpression:Path=SplashText; DataItem='MainMenuViewModel' (HashCode=680171); target element is 'TextBox' (Name='FeedBackBox'); target property is 'Text' (type 'String')

が、私はバインディングがまだ存在しているため、これがあることを理解し、しかし、のDataContextのCurrentPageViewModelプロパティが変更されました:スプラッシュ画面が終了し、私はのviewmodelを切り替えると、私は次の取得します。それでは、私が知りたいことは次のとおりです。

  • これはつかの間の問題であり、すなわちビューが使用されていないときに処分されるか、または(と悪いバインディング)が無期限にメモリ内にそこに座っていますか?
  • ビューが非アクティブな間にこれらのバインディングをクリーンアップまたは非アクティブ化する方法はありますか?
  • これらを単独で放置すると、アプリケーションにどのようなパフォーマンスノックが発生しますか?
  • この問題を回避するビューを切り替えるより良い方法はありますか?

ご協力いただきありがとうございました。モノリシックな質問に対してお詫び申し上げます。

Edit 03/09/13 Jehof、Francesco De Lisi、およびFaster Solutionsのおかげで、ContentControlがdatacontextを処理するため、サブビューdatacontextを{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}と設定することは意味がないことを指摘してください。

答えて

1

あなたのお財産は別のViewModelに属する間にあなたのDataContextMainMenuViewModelになるようです。

スプラッシュ画面前後の値がBindingに変更され、表示が切り替わります。

問題は、実際にはDataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

にduedされ、CurrentPageViewModel = InitViewModelあなたのアプリケーションが起動しますが、問題は、すべてのViewが持っている同じDataContext(最初はすなわちInitViewModel)ということですが、私はViewModelsがいないと確信しているときビューバインディングを満たすのに必要なプロパティのプール全体。 理解する例

ViewXは、PropertyXへのバインディングを持ち、ViewModelXで管理されています。 ViewYは、ViewModelYで管理されるPropertyYへの結合を有する。 どちらもDataContext = CurrentViewModelです。

スタートアップ時に、CurrentViewModel = ViewModelXとViewXとViewYの両方にDataContext = ViewModelXがあります。しかし、これはが間違っています!おそらくエラーが発生します。

私が通常行うことは、適切なView Modelに合わせてViewクラスにDataContext(必要に応じてcsまたはXAML)を設定することです。次に、必要に応じて、ページを切り替えるたびに値を更新するためのリフレッシュメソッドを呼び出します。プロパティを共有している場合は、モデルを使用して情報(および値)を一元化することを検討してください。

サンプル画像enter image description here

http://wildermuth.com/images/mvvm_layout.pngから明らかビューは、あなたのメインウィンドウで包まれたコントロールがあります。

希望です。

+0

それでは、マスタービューモデルのcurrentViewModelプロパティにバインドするのではなく、各ビューにデータコンテキストを直接ハードコードすることをお勧めしますか?私にとっては、ちょっと混乱しているようです。しかし、私はこの答えは実際には解決策を提供するためにおそらく3の中で最も近いと思う。 –

+0

1:1バインディングは最も簡単なソリューションです。高度なMVVMパターンについては、http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspxを参照してください。 –

+0

1:1バインディングで、Mode = OneTimeを意味しますか? –

0

が順番にご質問にお答えします:

  1. あなたはおそらくすでにこの答えを知っています。 .Netガベージコレクションは、ヒープからViewオブジェクトを削除します。しかし、この時間まで、あなたのViewオブジェクトはあなたのページの主なDataContextにバインドされ、DataContextの変更されたイベントに反応します。
  2. 明らかに行うべきことは、Views DataContextをnullに設定することです。 DataContextは依存関係プロパティなので、null値のスコープはあなたのViewにすぎません。
  3. 他の/弱い回答が言ったように、それはあなたを少し遅くしますが、それほど多くはありません。私はこれについてあまり心配しません。
  4. はい。表示のナビゲーションオプションに便利なスレッドは次のとおりです。View Navigation Options

私はフレームワークを見ることをお勧めします。 MVVM Lightのような軽量のものは、ごくわずかな統合で多くの問題を解決します。 ViewModelLocatorのパターンは、あなたがやっていることもやっていますが、副作用がなく、クリーンアップオプションがたくさんあります。

+0

ビューがスコープ内になくなったときにdatacontextをnullに設定すると、bindingexpressionのパスエラーが停止しますか?そして、usercontrolの特定のイベントでdatacontextを無効にするときは、どのように決定しますか? –

+0

そうだと思います。 DataContextは依存関係プロパティです。これは依存関係プロパティであるため、いくつかのことを行います。変更のバインディングを通知し、値の階層を保持します。独自の値を持つことで、もはやページ/ユーザコントロール/ウィンドウから継承した値に依存しなくなります。コンテキストを無効にするかどうかは、アプリの構造によって決まります。 1つのオプションは、子ビューでDataContextChangedイベントを監視することです。これは、DataContextが変更されたときに通知し、そのイベントに反応する可能性があります。 –

+0

これはすべてうまくできていて、「InitView」から「MainMenuView」への移行を修正します。なぜなら、スプラッシュ画面に再度アクセスする必要がないからです。しかし、たとえば、メインメニューからワークフロービューモデルに切り替えた後、メインメニューに戻ると問題が発生します。それはdatacontextがnullになるので、私はそれをリセットする方法を見つけ出すか、バインディング式のエラーを受け入れる必要があります。 –

0

はあなたのビューで

DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 

原因をDataContextのの結合を省略することができますViewDataContextContentControlDataContextであり、それはあなたがContent -Propertyの結合によって設定されます。あなたの財産CurrentPageViewModelInitViewModelに設定されている場合

ので、ContentControlDataContextとしてInitViewModelを使用してContentTemplateとしてInitViewを使用して、それはInitViewのDataContextのように彼自身のDataContextを設定しますします。

+0

有用ですが、ありがたいですが、bindingexpressionのパスエラーは解決しません –

1

具体的な例は、.NET 4.5では再現できません。これは、おそらくマイクロソフトが問題を修正したことを意味します。

しかし、ContentとContentTemplateの両方がデータにバインドされている場合も同様の問題があります。私はこの問題に取り組んでいます。これは、誰でもまだ.NET 3.5を使用しているならば、.NET 3.5の問題を解決する可能性があります。例えば:

<ContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}" /> 

またはContentTemplateがDataTriggerによって決定される:

<ContentControl Content="{Binding Content}"> 
    <ContentControl.Style> 
     <Style TargetType="{x:Type ContentControl}"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Choice}" Value="1"> 
        <Setter Property="ContentTemplate" Value="{StaticResource TemplateA}" /> 
       </DataTrigger> 
       <DataTrigger Binding="{Binding Choice}" Value="2"> 
        <Setter Property="ContentTemplate" Value="{StaticResource TemplateB}" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </ContentControl.Style> 
</ContentControl> 

両方の場合において、一方が観察されたものOPに類似の結合誤差を取得します。

ここでのトリックは、バインディングエラーを防ぐためにContentとContentTemplateの変更を確実に正しい順序で実行することです。 DelayedContentControlと書いてあります。これにより、ContentとContentTemplateが同時に正しい順序で変更されます。

<jc:DelayedContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}"> 

DataTriggerの場合も同様です。

私のopensource JungleControls libraryからDelayedContentControlを取得できます。

関連する問題