IT

종속성 속성 변경 듣기

itgroup 2023. 4. 13. 20:46
반응형

종속성 속성 변경 듣기

의 변화를 들을 수 있는 방법이 있나요?DependencyProperty값이 변경되면 알림을 받고 몇 가지 작업을 수행하려고 하는데 바인딩을 사용할 수 없습니다.그것은 이다.DependencyProperty다른 반의

이 방법은 여기서 찾을 수 없습니다.

DependencyPropertyDescriptor
    .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton))
    .AddValueChanged(radioButton, (s,e) => { /* ... */ });

주의:왜냐면DependencyPropertyDescriptor는 어플리케이션 내의 모든 핸들러의 스태틱리스트를 가지고 있습니다.해당 핸들러가 최종적으로 삭제되지 않으면 이들 핸들러에서 참조되는 모든 오브젝트가 누수됩니다.(인스턴스 오브젝트의 일반적인 이벤트와 같이 동작하지 않습니다).

항상 다음을 사용하여 핸들러를 다시 제거합니다.descriptor.RemoveValueChanged(...).

의 경우DependencyProperty가장 쉬운 방법은 값을 바인딩하고 해당 값의 변경을 듣는 것입니다.

DP가 자신의 클래스로 실장되어 있는 경우 PropertyChangedCallback을 등록하여DependencyProperty. 이를 사용하여 속성 변경을 청취할 수 있습니다.

하위 클래스를 사용하는 경우 OverrideMetadata를 사용하여 자체 클래스를 추가할 수 있습니다.PropertyChangedCallback원래 DP가 아닌 DP가 호출될 것입니다.

이 유틸리티 클래스를 작성했습니다.

  • Dependency Property Changed Event Args에 오래된 값과 새로운 값을 지정합니다.
  • 소스가 바인딩의 약한 참조에 저장됩니다.
  • Binding & Binding Expression을 공개하는 것이 좋은 생각인지는 잘 모르겠습니다.
  • 누수가 없다.
using System;
using System.Collections.Concurrent;
using System.Windows;
using System.Windows.Data;

public sealed class DependencyPropertyListener : DependencyObject, IDisposable
{
    private static readonly ConcurrentDictionary<DependencyProperty, PropertyPath> Cache = new ConcurrentDictionary<DependencyProperty, PropertyPath>();

    private static readonly DependencyProperty ProxyProperty = DependencyProperty.Register(
        "Proxy",
        typeof(object),
        typeof(DependencyPropertyListener),
        new PropertyMetadata(null, OnSourceChanged));

    private readonly Action<DependencyPropertyChangedEventArgs> onChanged;
    private bool disposed;

    public DependencyPropertyListener(
        DependencyObject source, 
        DependencyProperty property, 
        Action<DependencyPropertyChangedEventArgs> onChanged = null)
        : this(source, Cache.GetOrAdd(property, x => new PropertyPath(x)), onChanged)
    {
    }

    public DependencyPropertyListener(
        DependencyObject source, 
        PropertyPath property,
        Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        this.Binding = new Binding
        {
            Source = source,
            Path = property,
            Mode = BindingMode.OneWay,
        };
        this.BindingExpression = (BindingExpression)BindingOperations.SetBinding(this, ProxyProperty, this.Binding);
        this.onChanged = onChanged;
    }

    public event EventHandler<DependencyPropertyChangedEventArgs> Changed;

    public BindingExpression BindingExpression { get; }

    public Binding Binding { get; }

    public DependencyObject Source => (DependencyObject)this.Binding.Source;

    public void Dispose()
    {
        if (this.disposed)
        {
            return;
        }

        this.disposed = true;
        BindingOperations.ClearBinding(this, ProxyProperty);
    }

    private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var listener = (DependencyPropertyListener)d;
        if (listener.disposed)
        {
            return;
        }

        listener.onChanged?.Invoke(e);
        listener.OnChanged(e);
    }

    private void OnChanged(DependencyPropertyChangedEventArgs e)
    {
        this.Changed?.Invoke(this, e);
    }
}

using System;
using System.Windows;

public static class Observe
{
    public static IDisposable PropertyChanged(
        this DependencyObject source,
        DependencyProperty property,
        Action<DependencyPropertyChangedEventArgs> onChanged = null)
    {
        return new DependencyPropertyListener(source, property, onChanged);
    }
}

수신하려는 컨트롤을 상속한 후 다음 항목에 직접 액세스할 수 있습니다.

protected void OnPropertyChanged(string name)

메모리 누수의 위험은 없다.

표준 OO 기술을 두려워하지 마세요.

이를 실현하는 방법은 여러 가지가 있습니다.다음은 종속 속성을 관찰 가능한 속성으로 변환하여 시스템을 사용할 수 있도록 하는 방법입니다.반응:

public static class DependencyObjectExtensions
{
    public static IObservable<EventArgs> Observe<T>(this T component, DependencyProperty dependencyProperty)
        where T:DependencyObject
    {
        return Observable.Create<EventArgs>(observer =>
        {
            EventHandler update = (sender, args) => observer.OnNext(args);
            var property = DependencyPropertyDescriptor.FromProperty(dependencyProperty, typeof(T));
            property.AddValueChanged(component, update);
            return Disposable.Create(() => property.RemoveValueChanged(component, update));
        });
    }
}

사용.

메모리 누수를 방지하기 위해 서브스크립션을 폐기하는 것을 잊지 마십시오.

public partial sealed class MyControl : UserControl, IDisposable 
{
    public MyControl()
    {
        InitializeComponent();

        // this is the interesting part 
        var subscription = this.Observe(MyProperty)
                               .Subscribe(args => { /* ... */}));

        // the rest of the class is infrastructure for proper disposing
        Subscriptions.Add(subscription);
        Dispatcher.ShutdownStarted += DispatcherOnShutdownStarted; 
    }

    private IList<IDisposable> Subscriptions { get; } = new List<IDisposable>();

    private void DispatcherOnShutdownStarted(object sender, EventArgs eventArgs)
    {
        Dispose();
    }

    Dispose(){
        Dispose(true);
    }

    ~MyClass(){
        Dispose(false);
    }

    bool _isDisposed;
    void Dispose(bool isDisposing)
    {
        if(_disposed) return;

        foreach(var subscription in Subscriptions)
        {
            subscription?.Dispose();
        }

        _isDisposed = true;
        if(isDisposing) GC.SupressFinalize(this);
    }
}

만약 그렇다면, 한 방이면 됩니다.Static 클래스를 도입할 수 있습니다.DependencyProperty송신원클래스도 그 dp에 바인드 되어 행선지 클래스도 DP에 바인드 됩니다.

언급URL : https://stackoverflow.com/questions/4764916/listen-to-changes-of-dependency-property

반응형