BusyIndi​​catorの添付プロパティを段階的に作成する

この記事は、 非同期操作だけでなく 、記事「 Automatic BusyIndi​​cator 」の続きです。





attached property



を使用する場合、XAMLで記述する必要があるのは次のとおりです。

 <BusyIndicator AsyncIndicator.Attached="true" > <ListBox ItemsSource="{Binding DataList, IsAsync=true}" ...> ... </ListBox> <BusyIndicator>
      
      





私については-あなたはより少ない想像することはできません!



標準プロトタイプのattached property



から始めましょう:



 public static class AsyncIndicator { static AsyncIndicator() { } public static readonly DependencyProperty AttachedProperty = DependencyProperty.RegisterAttached("Attached", typeof (bool), typeof (ContentControl), new FrameworkPropertyMetadata(false, AttachedChanged)); public static Boolean GetAttached(UIElement element) { return (Boolean) element.GetValue(AttachedProperty); } public static void SetAttached(UIElement element, Boolean value) { element.SetValue(AttachedProperty, value); } private static void AttachedChanged(DependencyObject busyIndicator, DependencyPropertyChangedEventArgs e) { } }
      
      





興味深いことに、 SetAttached



SetAttached



理論的には不要であり、スクリプトで呼び出されることはありませんが、これらがないと、 attached property



は使用できません。




AttachedChanged



イベントメソッドに興味があります。 一般的な考え方は次のとおりですBusyIndicator



のコンテンツとItemsSource



プロパティ(より正確には、 dependency property



ItemsSourceProperty



)を探しています。そのようなプロパティが見つかった場合、プロパティが変更された瞬間をインターセプトします。 値がnull



場合-インジケーターをオンにし、そうでない場合-オフにします。



AttachedChanged



を呼び出した時点でBusyIndicator



のコンテンツはまだインストールされていません。これは驚くべきことではありません。

ContentControl



のような標準イベントを見つけられませんでしたが、私はそれを回避する必要がありました。

 private static void AttachedChanged(DependencyObject busyIndicator, DependencyPropertyChangedEventArgs e) { SetPropertyChangedCallback(ContentControl.ContentProperty, busyIndicator, ContentChangedCallback); } private static void SetPropertyChangedCallback(DependencyProperty dp, DependencyObject d, PropertyChangedCallback callback, bool reset = false) { if (dp == null || d == null) return; var typ = d.GetType(); var metadata = dp.GetMetadata(typ); var oldValue = metadata.SetPropValue("Sealed", false); metadata.PropertyChangedCallback -= callback; if (!reset) metadata.PropertyChangedCallback += callback; metadata.SetPropValue("Sealed", oldValue); } private static void ContentChangedCallback(DependencyObject busyIndicator, DependencyPropertyChangedEventArgs e) { if (!(bool) busyIndicator.GetValue(AttachedProperty)) return; SetBusyIndicator(e.OldValue as DependencyObject, null); SetBusyIndicator(e.NewValue as DependencyObject, busyIndicator); }
      
      





このコードについて少しコメントします。

SetPropertyChangedCallback



メソッドは、 dependency property ContentProperty



メタデータを受け取り、このプロパティの値を変更するイベントハンドラーを追加(または削除)します。

小さなハックが1つあります。事実は、初期化後にメタデータを変更することはできないということです。これは例外で明確に述べられています。 ただし、 PropertyMetadata.cs



モジュールのソースコードを分析したところ、この初期化の兆候はinternal



Sealed



プロパティであることがSealed



ました。 サンプルコードをSetPropValu



にしないために、 SetPropValu



メソッドの実装を引用しませんが、 SetPropValu



を介してオブジェクトのプロパティの変更を書き込むことはできないと思います。



WPFのハブの誰かが、この問題をより美しく解決する方法を教えてくれたら、コメント欄に書いてください。



これで、 BusyIndicator



での新しいコンテンツのインストール場所でContentChangedCallback



メソッドが呼び出されます。 この方法の最初の行は非常に重要です-なぜなら このメソッドは、 AsyncIndicator.Attached="true"



プロパティを設定したものだけでなく、すべてのBusyIndicator



に対して呼び出されます。 したがって、このプロパティの値がtrue



等しいことを確認しtrue







このメソッドの2行目は前のコンテンツのItemsSource変更イベントを無効にし、3行目はイベントを新しいコンテンツに追加します。



SetBusyIndicator



メソッドを検討してSetBusyIndicator





 private static readonly DependencyProperty _busyIndicatorProperty = DependencyProperty.RegisterAttached("%BusyIndicatorProperty%", typeof (ContentControl), typeof (DependencyObject)); private static void SetBusyIndicator(DependencyObject contentObject, DependencyObject busyIndicator) { if (contentObject != null) { SetPropertyChangedCallback(GetItemsSourceValue(contentObject), contentObject, ItemsSourceChangedCallback, busyIndicator == null); contentObject.SetValue(_busyIndicatorProperty, busyIndicator); } UpdateBusyIndicator(busyIndicator, contentObject); } private static object GetItemsSourceValue(DependencyObject contentObject) { var itemsSourceProperty = contentObject.GetFieldValue("ItemsSourceProperty"); return contentObject == null ? null : contentObject.GetValue(itemsSourceProperty); } private static void UpdateBusyIndicator(DependencyObject busyIndicator, DependencyObject contentObject) { if (busyIndicator == null) return; if (contentObject == null) busyIndicator.SetPropValue("IsBusy", false); else { var itemsSource = contentObject == null ? null : contentObject.GetValue(GetItemsSourceValue(contentObject)); busyIndicator.SetPropValue("IsBusy", itemsSource == null); } } private static void ItemsSourceChangedCallback(DependencyObject contentObject, DependencyPropertyChangedEventArgs e) { var busyIndicator = contentObject == null ? null : contentObject.GetValue(_busyIndicatorProperty) as DependencyObject; UpdateBusyIndicator(busyIndicator, contentObject); }
      
      





このコードには説明も必要です。

最初に、 SetBusyIndicator



メソッドは、イベントハンドラーを設定して、 SetBusyIndicator



方法でdependency property ItemsSource



を変更しdependency property ItemsSource





第二に、 contentObject



コントロールのbusyIndicator



インスタンスへのリンクを何らかの方法で保存する必要があります。これにより、 contentObject



は、どのBusyIndicator



'yがIsBusy



属性を変更するかをBusyIndicator



ます。 これに対する最も簡単で明白な解決策は、別のプライベートdependency property _busyIndicatorProperty



を使用することであるように思われました。

3 UpdateBusyIndicator



に、 UpdateBusyIndicator



メソッドUpdateBusyIndicator



は、 _busyIndicatorProperty



を介して_busyIndi​​catorPropertyに格納されているBusyIndi​​catorのIsBusyプロパティの値を設定します。



完全なサンプルテキスト



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



All Articles