ビューモデルから発生したイベントで、サービス時にビューでdatacontextがnullとして表示されることがあります。これは弱いバインディングパターンの問題であり、使用していないか、誤解していると思い始めています(一般的なデザインパターンに慣れておらず、数か月前にWPFを開始しました)。
関連するMainWindow.xaml
<Grid Tree:TreeView.ItemSelected="DisplayRequested"
Tree:TreeView.PoolCategoriesChanged="PoolContentChanged">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="0" VerticalAlignment="Stretch"/>
<GridSplitter Grid.Column="1" VerticalAlignment="Stretch"
Background="Gray" ShowsPreview="True" Width="5"
HorizontalAlignment="Center"/>
<GridSplitter Grid.Column="2" VerticalAlignment="Stretch"/>
<Tree:TreeView Name="poolTree" Grid.Column="0" Focusable="True" />
<ScrollViewer Grid.Column="2" VerticalScrollBarVisibility="Auto">
<Details:DetailsView Name="detailsDisplay" Focusable="True"/>
</ScrollViewer>
</Grid>
背後にある関連コード
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var vm = (CLUViewModel)e.NewValue;
if (vm == null) return;
vm.OwnerCleanupStarted += OnOwnerCleanupStarting;
vm.OwnerCleanupEnded += OnOwnerCleanupEnding;
}
#region Event Handlers
private void OnOwnerCleanupEnding(object sender, EventArgs e)
{
ViewServices.CloseProgressDialog();
}
private void OnOwnerCleanupStarting(object sender, EventArgs e)
{
var vm = DataContext as CLUViewModel;
if (vm == null) return;
var progressDialogViewModel = vm.ProgressDialogVM;
ViewServices.ShowProgressDialog(GetWindow(this), progressDialogViewModel);
}
#endregion
}
RoutedEvents
問題なく機能しているように見えるものがいくつかあります。ただし、このOnOwnerCleanupStarting
イベントはvarで多くのnullを返すようvm = DataContext as CLUViewModel;
です。これは、バインドが強すぎてWPFフレームワークを使用していないためですか?
これをデバッグしてトレースすると、常に機能し、通常の使用中にエラーなしで機能することがよくあります。これは、サブコンポーネントにフォーカスするときに初期化されていないビューのメモリ内のリスナーを使用している、一種の競合状態ですか?
VMからの呼び出しロジック:
public class CLUViewModel : ViewModelBase
{
#region Properties
private RelayCommand _manageOwnersDialogCommand;
public ProgressDialogViewModel ProgressDialogVM;
#endregion
public CLUViewModel()
{
ProgressDialogVM = new ProgressDialogViewModel(string.Empty);
}
#region ManageOwnersDialogCommand
public ICommand ManageOwnersDialogCommand
{
get
{
return _manageOwnersDialogCommand ??
(_manageOwnersDialogCommand = new RelayCommand(param => OnManageOwnersDialogShow()));
}
}
private void OnManageOwnersDialogShow()
{
var dialog = new ManageOwnersDialog();
var vm = new ManageOwnersViewModel();
dialog.DataContext = vm;
if (!dialog.ShowDialog().Value) return;
var ownersRequiringCleanup = GetOwnersRequiringCleanup(vm);
if(ownersRequiringCleanup.Count < 1) return;
ProgressDialogVM.ClearViewModel();
ProgressDialogVM.TokenSource = new CancellationTokenSource();
ProgressDialogVM.ProgressMax = ownersRequiringCleanup.Count*2;
RaiseOwnerCleanupStartedEvent();
var taskOne = Task.Factory.StartNew(() => OwnerCleanupService.DoOwnerCleanup(ownersRequiringCleanup, ProgressDialogVM));
taskOne.ContinueWith(t => RaiseOwnerCleanupEndedEvent(), TaskScheduler.FromCurrentSynchronizationContext());
}
private List<Owner> GetOwnersRequiringCleanup(ManageOwnersViewModel vm)
{
var ownersRequiringCleanup = new List<Owner>();
// using DB to determine cleanup
// Proprietary code removed for this discussion
return ownersRequiringCleanup;
}
#endregion
#region Events
public event EventHandler OwnerCleanupStarted;
public event EventHandler OwnerCleanupEnded;
public void RaiseOwnerCleanupStartedEvent()
{
if (OwnerCleanupStarted == null) return;
OwnerCleanupStarted(this, new EventArgs());
}
public void RaiseOwnerCleanupEndedEvent()
{
if (OwnerCleanupEnded == null) return;
OwnerCleanupEnded(this, new EventArgs());
}
#endregion
}
さまざまなVMが(ツリービュー内で)親を呼び出し、その親がイベントを発生させる他のいくつかのコントロールでも、これと同じ問題が発生します。
私はこれを進めながら学んでいます。VM側で使用したイベントのいくつかは、物事がどのように機能するかについての以前の理解でした。これは、ビューに上がったイベントを呼び出してRountedEvent
、適切なレベルにバブルを開始する必要があった場所ですか?私は強い結合と弱い結合の罠に陥りましたか?
編集:解決された親/子TreeViewの問題が見られます。マスター-詳細パターンは、表示されていないか、ロードされなかった詳細ビューにアクセスしていたことを意味します。現在、最初の質問はまだnullの発生を取得しています。View DataContextで問題が発生しないUI関連の問題について、VMからViewにコールバックするためのより良い方法はありますか?