6

私は本当にこれで頭を悩ませています。ダイアログを開くメインウィンドウがあります。ダイアログが閉じた後、ダイアログにバインドされたコマンドの CanExecute メソッドは引き続き実行されます。これにより、アプリケーションで重大な問題が発生しています。

例:

MainWindow には、クリック ハンドラを持つボタンがあります。これはクリック イベント ハンドラです。

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        DialogWindow window = new DialogWindow();
        window.ShowDialog();
    }

ダイアログでは、項目コントロールをダイアログ ウィンドウの静的リソースにバインドします。リスト内の各項目にはコマンドがあります。

<Window.Resources>

    <Collections:ArrayList x:Key="itemsSource">
        <local:ItemViewModel Description="A"></local:ItemViewModel>
        <local:ItemViewModel Description="B"></local:ItemViewModel>
        <local:ItemViewModel Description="C"></local:ItemViewModel>
    </Collections:ArrayList>

    <DataTemplate DataType="{x:Type local:ItemViewModel}">
            <Button Grid.Column="1" Command="{Binding Path=CommandClickMe}" Content="{Binding Path=Description}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
            </Button>
    </DataTemplate>

</Window.Resources>

<Grid>
    <ToolBar ItemsSource="{StaticResource itemsSource}"></ToolBar>
</Grid>

これはビューモデルです:

public class ItemViewModel
{
    private RelayWpfCommand<object> _commandClickMe;

    public RelayWpfCommand<object> CommandClickMe
    {
        get
        {
            if (_commandClickMe == null)
                _commandClickMe = new RelayWpfCommand<object>(obj => System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());

            return _commandClickMe;
        }
    }

    private bool CanClickMe()
    {
        return true;
    }

    public string Description { get; set; }

そして、これは DelegateCommand の実装です:

public class RelayWpfCommand<T> : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    public RelayWpfCommand(Action<T> execute, Predicate<T> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    /// <summary>
    /// Forces a notification that the CanExecute state has changed
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

    public bool CanExecute(T parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(T parameter)
    {
        _execute(parameter);
    }

    bool ICommand.CanExecute(object parameter)
    {
        if (!IsParameterValidType(parameter))
            return false;

        return CanExecute((T)parameter);
    }

    void ICommand.Execute(object parameter)
    {
        if (!IsParameterValidType(parameter))
            throw new ArgumentException(string.Format("Parameter must be of type {0}", typeof(T)));

        Execute((T)parameter);
    }

    private static bool IsParameterValidType(object parameter)
    {
        if (parameter != null && !typeof(T).IsAssignableFrom(parameter.GetType()))
            return false;

        return true;
    }
}

ここで、ダイアログ ウィンドウを閉じて、viewmodel の CanExecute メソッド (弱いイベント サブスクリプションで Prism DelegateCommand を使用しています) にブレークポイントを設定すると、ダイアログが閉じられていてもトリガーされることに気付きます。ダイアログのボタンとViewModelのコマンドの間のバインディングがまだ生きているのはなぜですか?

そして、ウィンドウを閉じて実行されているかどうかを確認し、後でビューモデルの「CanClickMe」メソッドにブレークポイントを設定しています。しばらく実行された後、突然停止します (おそらく GC が原因です)。実際のアプリケーションではビューモデルが既に破棄されている可能性があるため、この非決定論的な動作が問題を引き起こしています。

4

4 に答える 4

1

WeakEvent パターンを使用して、この問題を軽減できます。次の Stackoverflow の質問を参照してください: Josh Smith の RelayCommand の実装に欠陥がありますか?

于 2011-12-05T17:44:53.123 に答える
0

コマンドをプロパティとして持つのではなく、次のことを試してください。

public ICommand CommandClickMe
{
   get
   {
       return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
   }
}
于 2011-11-25T10:11:12.447 に答える
0

ウィンドウを閉じるときに、ウィンドウの CommandBindings コレクションをクリアできます。

于 2011-06-22T11:49:44.080 に答える
0

このキャッチは、さまざまなプロジェクトで何度も見てきました。この不気味なバグがあなたのアプリにも潜んでいるかどうかはわかりませんが、チェックする価値はあります。

WPF 3.5 (SP1 を含む) には既知のメモリ リークの問題DependencyPropertyがありますINotifyPropertyChanged。そして、これがまさにあなたのコードの内容です。

実装INotifyPropertyChangedItemViewModelて様子を見てください。お役に立てれば。

于 2011-01-09T21:46:57.127 に答える