2017-12-30 89 views
0

私はビューモデルでSelectedItemsを使用できるDataGridで使用するための接続プロパティを持っています。コードはこれです:添付されたプロパティのイベントを購読解除する場所を教えてください。

public class DataGridSelectedItemsAttachedProperty 
    { 
     #region SelectedItems 
     /// 
     /// SelectedItems Attached Dependency Property 
     /// 
     public static readonly DependencyProperty SelectedItemsProperty = 
     DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), 
     typeof(DataGridSelectedItemsAttachedProperty), 
     new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
     new PropertyChangedCallback(OnSelectedItemsChanged))); 

     public static IList GetSelectedItems(DependencyObject d) 
     { 
      return (IList)d.GetValue(SelectedItemsProperty); 
     } 

     public static void SetSelectedItems(DependencyObject d, IList value) 
     { 
      d.SetValue(SelectedItemsProperty, value); 
     } 

     private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      DataGrid miDg = (DataGrid)d; 
      miDg.SelectionChanged += dataGrid_SelectionChanged; 
      miDg.Unloaded += dataGrid_Unloaded; 
     } 

     private static void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) 
     { 
      DataGrid miDg = (DataGrid)sender; 
      //Get list box's selected items. 
      IEnumerable miDgSelectedItems = miDg.SelectedItems; 
      //Get list from model 
      IList ModelSelectedItems = GetSelectedItems(miDg); 

      //Update the model 
      ModelSelectedItems.Clear(); 

      if (miDg.SelectedItems != null) 
      { 
       foreach (var item in miDg.SelectedItems) 
        ModelSelectedItems.Add(item); 
      } 
      SetSelectedItems(miDg, ModelSelectedItems); 
     } 


     private static void dataGrid_Unloaded(object sender, RoutedEventArgs e) 
     { 
      DataGrid miDg = sender as DataGrid; 
      miDg.SelectionChanged -= dataGrid_SelectionChanged; 
      miDg.Unloaded -= dataGrid_Unloaded; 
     } 
     #endregion 
    } 

問題は、このデータグリッドは、イベントのアンロードが発射されたタブコントロールであるということですので、イベントはunsubcribe、その後selectedItemsのはもうビューモデルに通知されていないです。

私はこの問題を解決する方法を知りたいと思います。アンロードイベントではなく、別の場所でイベントの登録を解除するのはどうですか?

ありがとうございました。

+2

この場合、どうしてなぜ購読を停止するのですか? – Evk

+0

右。本当にそれは私が見つけた例だったし、良い解決策だと思う。しかし、このケースでは、添付されたプロパティがユーザーコントロールと同じ時間でなければならないため、サブスクライブを解除する必要はないと考えています。ユーザーコントロールを閉じると、オブジェクトが参照していないため、 。だから、この場合、私はそれも無事だと思う。 –

答えて

1

私がユーザーコントロールを閉じると、参照されているオブジェクトがないために添付プロパティが再選択されるときです。

これは誤りです。イベントの登録を解除するコードを削除すると、接続されたプロパティを使用するコントロールは永遠に存続します。どうして?登録するイベントハンドラは静的であるためです。つまり、コントロールに静的なものへの参照が含まれるため、ガベージコレクタはこれを収集できません。

この問題の最初の潜在的な解決策は、イベントを登録するときにウィークイベントパターンを使用することです。私自身の添付プロパティのイベントを登録するときは、弱いイベントパターンを常に使用するという上記の理由によるものです。

このソリューションの厄介なことは、かなり大量の定型文を必要とすることです。新しい種類のイベントごとに新しいWeakEventManager実装を作成する必要があります。その後、弱いイベントを受信するには、インターフェイス(EDIT:.NET 4.5以上を使用していない限り)を実装する必要があります。つまり、静的ハンドラを持つことはできません。したがって、IWeakEventListnerインターフェイスを実装するクラスが必要であり、そのクラスのインスタンスを、関連付けられたプロパティイベントで作成および管理します。

したがって、実際にはDataGridクラスをサブクラス化し、この機能を通常の依存関係プロパティとして追加することをお勧めします。このようにすれば、イベントを登録する必要はありません(上書きできる保護された方法があります)。潜在的なメモリリークの心配もありません。このソリューションをお勧めするのは、私の経験上、DataGridクラスを他の多くの理由でオーバーライドする必要があったため、それらの多くは添付プロパティで実現できますが、そのうちのいくつかはできません。

実際の問題は、WPF DataGridの実装がかなりハーフベークされていることです(私の個人的意見)。バグ、私が好きではないデフォルトの振る舞い、不完全な実装されていない機能(コピーのサポート(貼り付けはできません)、解決しようとしている特定の問題:バインド可能なSelectedItemなど)があります。 DataGridをサブクラス化するだけで、これらの問題を最も簡単に修正することができます。

+1

.net 4.5で追加された[AddHandler](https://msdn.microsoft.com/en-us/library/hh199430(v = vs.110).aspx)メソッドを使用すると、すべての定型文とターンが削除されます弱いイベントを追加または削除するには、1行のステートメントに挿入します。あなたはまだIWeakEventListenerを実装する必要はありません –

+0

良い点ですが、各イベントに対してWeakEventManagerの実装を作成する必要があり、全体的なポイントがまだ残っています。DataGridのサブクラス化はずっと簡単です。 –

+0

あなたはしません。 'WeakEventManager .AddHandler(miDg、" SelectionChanged "、dataGrid_SelectionChanged);は弱いイベントを使って購読するために.net 4.5以降で必要とされる唯一のコード行です。 4.5以降の実装を作成する必要はありません。 –

関連する問題