3

ビューモデルから発生したイベントで、サービス時にビューで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にコールバックするためのより良い方法はありますか?

4

1 に答える 1

1

WPF のイベント ベースのプログラミング モデルには反対することをお勧めします。このようなことには入り込みやすいからです。また、イベントベースのプログラミングは密結合を強制します。そのため、そのダブルノーノー

View でイベントをスローする代わりに、DelegateCommandを使用して ViewModel レベルで定義されたアクションを実行し、ViewModel でイベントをスローする代わりに、通常のプロパティとINotifyPropertyChangedを使用して物事の状態を表します。

これには確かに、winform やその他の wpf 以外の考え方からの大幅なマインドシフトが必要ですが、それを実現すると、生成されるコードが少なくなり、よりクリーンになります。

于 2012-11-18T04:29:07.543 に答える