gpt4 book ai didi

wpf - 如何修复 AttachedBehavior 上的 DependencyPropertyDescriptor AddValueChanged 内存泄漏?

转载 作者:行者123 更新时间:2023-12-04 02:45:38 27 4
gpt4 key购买 nike

我知道我需要调用RemoveValueChanged ,但我一直无法找到一个可靠的地方来调用它。我正在学习可能没有。
我似乎需要找到一种不同的方法来监控更改,然后使用 AddValueChanged 添加处理程序.我正在寻找实现这一目标的最佳方法的建议。我看到了使用 PropertyChangedCallback 的建议在 PropertyMetadata ,但是当我的 TextBoxAdorner不是静态的。此外,IsFocused属性不是 DependencyProperty在我的类里面创建。

public sealed class WatermarkTextBoxBehavior
{
private readonly TextBox m_TextBox;
private TextBlockAdorner m_TextBlockAdorner;

private WatermarkTextBoxBehavior(TextBox textBox)
{
if (textBox == null)
throw new ArgumentNullException("textBox");

m_TextBox = textBox;
}

#region Behavior Internals

private static WatermarkTextBoxBehavior GetWatermarkTextBoxBehavior(DependencyObject obj)
{
return (WatermarkTextBoxBehavior)obj.GetValue(WatermarkTextBoxBehaviorProperty);
}

private static void SetWatermarkTextBoxBehavior(DependencyObject obj, WatermarkTextBoxBehavior value)
{
obj.SetValue(WatermarkTextBoxBehaviorProperty, value);
}

private static readonly DependencyProperty WatermarkTextBoxBehaviorProperty =
DependencyProperty.RegisterAttached("WatermarkTextBoxBehavior",
typeof(WatermarkTextBoxBehavior), typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(null));

public static bool GetEnableWatermark(TextBox obj)
{
return (bool)obj.GetValue(EnableWatermarkProperty);
}

public static void SetEnableWatermark(TextBox obj, bool value)
{
obj.SetValue(EnableWatermarkProperty, value);
}

public static readonly DependencyProperty EnableWatermarkProperty =
DependencyProperty.RegisterAttached("EnableWatermark", typeof(bool),
typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(false, OnEnableWatermarkChanged));

private static void OnEnableWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
{
var enabled = (bool)e.OldValue;

if (enabled)
{
var textBox = (TextBox)d;
var behavior = GetWatermarkTextBoxBehavior(textBox);
behavior.Detach();

SetWatermarkTextBoxBehavior(textBox, null);
}
}

if (e.NewValue != null)
{
var enabled = (bool)e.NewValue;

if (enabled)
{
var textBox = (TextBox)d;
var behavior = new WatermarkTextBoxBehavior(textBox);
behavior.Attach();

SetWatermarkTextBoxBehavior(textBox, behavior);
}
}
}

private void Attach()
{
m_TextBox.Loaded += TextBoxLoaded;
m_TextBox.TextChanged += TextBoxTextChanged;
m_TextBox.DragEnter += TextBoxDragEnter;
m_TextBox.DragLeave += TextBoxDragLeave;
m_TextBox.IsVisibleChanged += TextBoxIsVisibleChanged;
}

private void Detach()
{
m_TextBox.Loaded -= TextBoxLoaded;
m_TextBox.TextChanged -= TextBoxTextChanged;
m_TextBox.DragEnter -= TextBoxDragEnter;
m_TextBox.DragLeave -= TextBoxDragLeave;
m_TextBox.IsVisibleChanged -= TextBoxIsVisibleChanged;
}

private void TextBoxDragLeave(object sender, DragEventArgs e)
{
UpdateAdorner();
}

private void TextBoxDragEnter(object sender, DragEventArgs e)
{
m_TextBox.TryRemoveAdorners<TextBlockAdorner>();
}

private void TextBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateAdorner();
}

private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var hasText = !string.IsNullOrEmpty(m_TextBox.Text);
SetHasText(m_TextBox, hasText);
}

private void TextBoxLoaded(object sender, RoutedEventArgs e)
{
Init();
}

#endregion

#region Attached Properties

public static string GetLabel(TextBox obj)
{
return (string)obj.GetValue(LabelProperty);
}

public static void SetLabel(TextBox obj, string value)
{
obj.SetValue(LabelProperty, value);
}

public static readonly DependencyProperty LabelProperty =
DependencyProperty.RegisterAttached("Label", typeof(string), typeof(WatermarkTextBoxBehavior));

public static Style GetLabelStyle(TextBox obj)
{
return (Style)obj.GetValue(LabelStyleProperty);
}

public static void SetLabelStyle(TextBox obj, Style value)
{
obj.SetValue(LabelStyleProperty, value);
}

public static readonly DependencyProperty LabelStyleProperty =
DependencyProperty.RegisterAttached("LabelStyle", typeof(Style),
typeof(WatermarkTextBoxBehavior));

public static bool GetHasText(TextBox obj)
{
return (bool)obj.GetValue(HasTextProperty);
}

private static void SetHasText(TextBox obj, bool value)
{
obj.SetValue(HasTextPropertyKey, value);
}

private static readonly DependencyPropertyKey HasTextPropertyKey =
DependencyProperty.RegisterAttachedReadOnly("HasText", typeof(bool),
typeof(WatermarkTextBoxBehavior), new UIPropertyMetadata(false));

public static readonly DependencyProperty HasTextProperty =
HasTextPropertyKey.DependencyProperty;

#endregion

private void Init()
{
m_TextBlockAdorner = new TextBlockAdorner(m_TextBox, GetLabel(m_TextBox), GetLabelStyle(m_TextBox));
UpdateAdorner();

DependencyPropertyDescriptor focusProp = DependencyPropertyDescriptor.FromProperty(UIElement.IsFocusedProperty, typeof(FrameworkElement));
if (focusProp != null)
{
focusProp.AddValueChanged(m_TextBox, (sender, args) => UpdateAdorner());
}

DependencyPropertyDescriptor containsTextProp = DependencyPropertyDescriptor.FromProperty(HasTextProperty, typeof(TextBox));
if (containsTextProp != null)
{
containsTextProp.AddValueChanged(m_TextBox, (sender, args) => UpdateAdorner());
}
}

private void UpdateAdorner()
{
if (GetHasText(m_TextBox) ||
m_TextBox.IsFocused ||
!m_TextBox.IsVisible)
{
// Hide the Watermark Label if the adorner layer is visible
m_TextBox.ToolTip = GetLabel(m_TextBox);
m_TextBox.TryRemoveAdorners<TextBlockAdorner>();
}
else
{
// Show the Watermark Label if the adorner layer is visible
m_TextBox.ToolTip = null;
m_TextBox.TryAddAdorner<TextBlockAdorner>(m_TextBlockAdorner);
}
}
}

最佳答案

AddValueChanged 如您所知,依赖属性描述符会导致内存泄漏。因此,如 here 所述,您可以创建自定义类 PropertyChangeNotifier 监听任何依赖属性的变化。

完整的实现可以在这里找到 - PropertyDescriptor AddValueChanged Alternative .

引用链接:

This class takes advantage of the fact that bindings use weak references to manage associations so the class will not root the object who property changes it is watching. It also uses a WeakReference to maintain a reference to the object whose property it is watching without rooting that object. In this way, you can maintain a collection of these objects so that you can unhook the property change later without worrying about that collection rooting the object whose values you are watching.



同样为了回答的完整性,我在这里发布了完整的代码,以避免将来出现任何腐烂问题。
public sealed class PropertyChangeNotifier : DependencyObject, IDisposable
{
#region Member Variables

private readonly WeakReference _propertySource;

#endregion // Member Variables

#region Constructor
public PropertyChangeNotifier(DependencyObject propertySource, string path)
: this(propertySource, new PropertyPath(path))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, DependencyProperty property)
: this(propertySource, new PropertyPath(property))
{
}
public PropertyChangeNotifier(DependencyObject propertySource, PropertyPath property)
{
if (null == propertySource)
throw new ArgumentNullException("propertySource");
if (null == property)
throw new ArgumentNullException("property");
_propertySource = new WeakReference(propertySource);
Binding binding = new Binding
{
Path = property,
Mode = BindingMode.OneWay,
Source = propertySource
};
BindingOperations.SetBinding(this, ValueProperty, binding);
}
#endregion // Constructor

#region PropertySource
public DependencyObject PropertySource
{
get
{
try
{
// note, it is possible that accessing the target property
// will result in an exception so i’ve wrapped this check
// in a try catch
return _propertySource.IsAlive
? _propertySource.Target as DependencyObject
: null;
}
catch
{
return null;
}
}
}
#endregion // PropertySource

#region Value
/// <summary>
/// Identifies the <see cref="Value"/> dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(object), typeof(PropertyChangeNotifier), new FrameworkPropertyMetadata(null, OnPropertyChanged));

private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PropertyChangeNotifier notifier = (PropertyChangeNotifier)d;
if (null != notifier.ValueChanged)
notifier.ValueChanged(notifier, EventArgs.Empty);
}

/// <summary>
/// Returns/sets the value of the property
/// </summary>
/// <seealso cref="ValueProperty"/>
[Description("Returns/sets the value of the property")]
[Category("Behavior")]
[Bindable(true)]
public object Value
{
get
{
return GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
#endregion //Value

#region Events
public event EventHandler ValueChanged;
#endregion // Events

#region IDisposable Members

public void Dispose()
{
BindingOperations.ClearBinding(this, ValueProperty);
}

#endregion
}

关于wpf - 如何修复 AttachedBehavior 上的 DependencyPropertyDescriptor AddValueChanged 内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23682232/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com