11

MVVM パターンを使用して、WPF と c# で記述されたデスクトップ アプリを完成させました。このアプリでは、デリゲート コマンドの実装を使用して、ModelView で公開されている ICommands プロパティをラップしました。問題は、これらの DelegateCommands が、ビューを閉じた後に ModelView と View がガベージ コレクションされるのを妨げていることです。したがって、アプリケーション全体を終了するまで、それは鳴き続けます。モデルビューをメモリに保持するデリゲートコマンドがすべてであることがわかったアプリケーションのプロファイルを作成します。どうすればこの状況を回避できますか?これは mvvm パターンの性質によるものですか、それともパターンの移植に関するものですか? ありがとう。

編集:これは小さいですが、MVVMパターンを実装する方法の完全な部分です

最初: CommandDelegte クラス

class DelegateCommand:ICommand
{
    private Action<object> execute;
    private Predicate<object> canExcute;
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }
        this.execute = execute;
        this.canExcute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        if (this.canExcute != null)
        {
            return canExcute(parameter);
        }
        return true;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }


    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

2番目: ModelView クラス

public class ViewModel:DependencyObject, INotifyPropertyChanged
{
    private DelegateCommand printCommand;

    public ICommand PrintCommand
    {
        get
        {
            if (printCommand == null)
            {
                printCommand = new DelegateCommand(Print, CanExecutePrint);
            }
            return printCommand;
        }
    }
    void Print(object obj)
    {
        Console.WriteLine("Print Command");

    }
    bool CanExecutePrint(object obj)
    {
        return true;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnProeprtyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

3 番目: ウィンドウ コード ビハインド

public MainWindow()
    {
        InitializeComponent();
        base.DataContext = new ViewModel();
    }

4: 私の XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
    <KeyBinding Key="P" Modifiers="Control" Command="{Binding Path=PrintCommand}"/>
</Window.InputBindings>
<StackPanel>
    <Button Content="Print - Ctrl+P" Width="75" Height="75" Command="{Binding Path=PrintCommand}"/>
</StackPanel>

4

3 に答える 3

9

あなたの場合、何が何への参照を含んでいますか?

  1. DelegateCommandへの参照が含まれていますViewModel- そのexecuteおよびプロパティには、インスタンスcanExecuteのメソッドへの参照が含まれています。ViewModel

  2. ViewModelDelegateCommand- そのPrintCommandプロパティへの参照が含まれています。

  3. ビューには、 への参照がいくつでも含まれていViewModelます。

  4. には、そのイベントにCommandManagerへの参照が含まれています。DelegateCommandRequerySuggested

最後の参照は特殊なケースです。 イベントで a をCommandManager使用するためWeakReference、 がそのRequerySuggestedイベントに登録されているにもかかわらず、DelegateCommandガベージ コレクションが実行される可能性があります。

これらすべてを考えると、問題が発生することはありません。ビューが破棄された場合、 にも にViewModelDelegateCommand到達できません。

アプリケーションのプロファイルを作成し、DelegateCommandへの参照を保持しているとしViewModelます。論理的な次の質問は次のように思われます: への参照を保持しているのは何DelegateCommandですか? そうであってはなりませんCommandManager。コマンドを参照しているアプリケーションに何か他のものがありますか?

于 2010-06-15T17:53:57.767 に答える
1

この投稿を読んだ後、関連する情報が記載された Web ページに出くわしました。DelegateCommand.CanExecuteChanged Event によって引き起こされる Memory Leakと呼ばれる CodePlex 上のページです。

報告者:huetter
更新者:dschenkelman

アプリケーションのプロファイリングを行っているときに、多数の EventHandler が DelegateCommand の CanExecuteChanged-Event から登録解除されていないことに気付きました。そのため、これらの EventHandler はガベージ コレクターではなく、重大なメモリ リークが発生していました。

CanExecuteChanged-EventHandles の登録はアプリケーション コードの範囲外で行われるため、それらも自動的に登録解除されると予想していました。この時点で、これはサードパーティの WPF コントロールの問題かもしれないと思いましたが、さらに掘り下げて、「WPF は、ICommand.CanExecuteChanged-Event が EventHandler に WeakReferences を適用することを期待している」というブログ投稿を読みました。RoutedCommand を調べたところ、WeakReferences も使用していることに気付きました。

RoutedCommand の CanExecuteChanged-Event と同様の実装を使用するように DelegateCommand を調整したところ、メモリ リークはなくなりました。CompositeCommand についても同様です。

この問題は Prism-v2.1 リリースで修正されたため、Workitem は現在クローズされています。Prism 2.1 は、http: //www.microsoft.com/downloads/details.aspx? FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang= en からダウンロードできます

于 2013-04-07T15:57:41.150 に答える
1

このコードには、ViewModel がガベージ コレクションされない循環参照があると思います。

これは古い質問であることは承知していますが、DelegateCommand またはRelayCommandの一部の実装がアクションへの WeakReference を保持していることを指摘します。ここでの DelegateCommand の使用は一般的ですが、残念ながら、ViewModel のメソッドが DelegateCommand のコンストラクターに渡されると、そのメソッドを含むクラスへの参照がデリゲートによって自動的にキャプチャされるため、この実装ではメモリ リークが発生します。

ViewModel に IDispose を実装し、Dispose で明示的に DelegateCommands への参照をクリアした場合は、引き続きこの実装を使用できます。ただし、ViewModel を構築しているビューもそれを破棄する必要があります。

于 2014-03-17T07:53:21.723 に答える