私は WPF アプリケーションで View First MVVM を使用することを余儀なくされており、それをエレガントに動作させる方法を見つけるのに苦労しています。
問題の根本は、ネストされた にありますUserControls
。MVVM アーキテクチャでは、それぞれのUserControl
ビュー モデルをそのDataContext
に割り当てる必要があります。これにより、バインディング式がシンプルに保たれます。さらに、これは、WPF が を介して生成されたビューをインスタンス化する方法でもありますDataTemplate
。
ただしUserControl
、親が独自のビューモデルにバインドする必要がある依存関係プロパティが子にある場合、子が独自のビューモデルに設定されているという事実はUserControl
、DataContext
親 XAML ファイルの「暗黙的なパス」バインディングが代わりに子のビューモデルに解決されることを意味します。親の。
これを回避するにUserControl
は、アプリケーション内のすべてのすべての親は、デフォルトですべてに対して明示的な名前付きバインディングを使用する必要があります (これは冗長で、醜く、エラーが発生しやすい)、または特定のコントロールがDataContext
独自のビューモデルに設定されているかどうかを知る必要があります。適切なバインディング構文を使用します (これは同様にエラーが発生しやすく、基本的なカプセル化の重大な違反です)。
何日にもわたる調査の後、私はこの問題に対する半分まともな解決策に出くわしていません。私が遭遇した解決策に最も近いのは、ビューモデルを(最上位またはその他UserControl's
の) 内部要素に設定することです。それでも、それ自体のプロパティを独自のビューモデルにバインドしようとする問題に直面します! (バインディングは、ビューモデルが割り当てられた名前付き要素の前に宣言されるため、この場合は機能しません)。UserControl
Grid
UserControl
ElementName
DataContext
他の多くの人がこの問題に遭遇しない理由は、この問題のないビューモデルの最初の MVVM を使用しているか、この問題を改善する依存性注入の実装と組み合わせてビューの最初の MVVM を使用しているためだと思います。
誰かがこれに対するきれいな解決策を持っていますか?
アップデート:
リクエストされたサンプルコード。
<!-- MainWindow.xaml -->
<Window x:Class="UiInteraction.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UiInteraction"
Title="MainWindow" Height="350" Width="525"
x:Name="_this">
<Window.DataContext>
<local:MainWindowVm/>
</Window.DataContext>
<StackPanel>
<local:UserControl6 Text="{Binding MainWindowVmString1}"/>
</StackPanel>
</Window>
namespace UiInteraction
{
// MainWindow viewmodel.
class MainWindowVm
{
public string MainWindowVmString1
{
get { return "MainWindowVm.String1"; }
}
}
}
<!-- UserControl6.xaml -->
<UserControl x:Class="UiInteraction.UserControl6" x:Name="_this"
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:local="clr-namespace:UiInteraction">
<UserControl.DataContext>
<local:UserControl6Vm/>
</UserControl.DataContext>
<StackPanel>
<!-- Is bound to this UserControl's own viewmodel. -->
<TextBlock Text="{Binding UserControlVmString1}"/>
<!-- Has its value set by the UserControl's parent via dependency property. -->
<TextBlock Text="{Binding Text, ElementName=_this}"/>
</StackPanel>
</UserControl>
namespace UiInteraction
{
using System.Windows;
using System.Windows.Controls;
// UserControl code behind declares DependencyProperty for parent to bind to.
public partial class UserControl6 : UserControl
{
public UserControl6()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(UserControl6));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
namespace UiInteraction
{
// UserControl's viewmodel.
class UserControl6Vm
{
public string UserControlVmString1
{
get { return "UserControl6Vm.String1"; }
}
}
}
これにより、次の結果が得られます。
System.Windows.Data エラー: 40: BindingExpression パス エラー: 'MainWindowVmString1' プロパティが 'object' ''UserControl6Vm' (HashCode=44204140)' に見つかりません。BindingExpression:Path=MainWindowVmString1; DataItem='UserControl6Vm' (HashCode=44204140); ターゲット要素は 'UserControl6' (Name='_this'); ターゲット プロパティは 'Text' (タイプ 'String') です
MainWindow.xaml
宣言<local:UserControl6 Text="{Binding MainWindowVmString1}"/>
では で解決しようとしているMainWindowVmString1
ためですUserControl6Vm
。
最初の とUserControl6.xaml
の宣言をコメントアウトすると、コードは機能しますが、 には. 暗黙的なパス バインディングの代わりに を使用することもできますが、バインディング構文を使用するには、 がそのビューモデルをその に割り当てることを知る必要があります(カプセル化の失敗) か、どこでもバインディングを使用するポリシーを採用する必要があります。どちらも魅力的ではありません。DataContext
TextBlock
UserControl
DataContext
MainWIndow1
ElementName
ElementName
UserControl
DataContext
ElementName