C# 绑定只工作一次
在构造函数中,当我向绑定到TextBlock的附加属性的ObservaleCollection添加两个项时,会更新附加属性。但是,当我稍后在另一个方法中将项目添加到同一个ObservableCollection时,附加的属性不会更新 XAML:C# 绑定只工作一次,c#,wpf,xaml,binding,observablecollection,C#,Wpf,Xaml,Binding,Observablecollection,在构造函数中,当我向绑定到TextBlock的附加属性的ObservaleCollection添加两个项时,会更新附加属性。但是,当我稍后在另一个方法中将项目添加到同一个ObservableCollection时,附加的属性不会更新 XAML: <TextBlock local:TextBlockExtensions.BindableInlines="{Binding StatusInlines}" /> public class TextBlockExtensions : Bas
<TextBlock local:TextBlockExtensions.BindableInlines="{Binding StatusInlines}" />
public class TextBlockExtensions : BaseViewModel
{
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
{
return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
}
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
{
obj.SetValue(BindableInlinesProperty, value);
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));
private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var Target = d as TextBlock;
if (Target != null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
public ObservableCollection<Inline> StatusInlines { get; set; } = new ObservableCollection<Inline>();
...
// works in the constructor (called twice)
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
...
// does not work later in other methods
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
TextBlockExtensions.cs:
<TextBlock local:TextBlockExtensions.BindableInlines="{Binding StatusInlines}" />
public class TextBlockExtensions : BaseViewModel
{
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
{
return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
}
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
{
obj.SetValue(BindableInlinesProperty, value);
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));
private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var Target = d as TextBlock;
if (Target != null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
public ObservableCollection<Inline> StatusInlines { get; set; } = new ObservableCollection<Inline>();
...
// works in the constructor (called twice)
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
...
// does not work later in other methods
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
公共类TextBlockExtensions:BaseViewModel
{
公共静态IEnumerable GetBindableInlines(DependencyObject obj)
{
返回(IEnumerable)对象GetValue(BindableInlinesProperty);
}
公共静态void SetBindableInlines(DependencyObject对象,IEnumerable值)
{
对象设置值(BindableInlinesProperty,value);
}
公共静态只读从属属性BindableInlinesProperty=
DependencyProperty.RegisterAttached(“BindableInlines”、typeof(IEnumerable)、typeof(TextBlockExtensions)、新PropertyMetadata(null、OnBindableInlinesChanged));
BindableInlineSchanged上的私有静态无效(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var Target=d作为文本块;
如果(目标!=null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
视图模型:
<TextBlock local:TextBlockExtensions.BindableInlines="{Binding StatusInlines}" />
public class TextBlockExtensions : BaseViewModel
{
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
{
return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
}
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
{
obj.SetValue(BindableInlinesProperty, value);
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));
private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var Target = d as TextBlock;
if (Target != null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
public ObservableCollection<Inline> StatusInlines { get; set; } = new ObservableCollection<Inline>();
...
// works in the constructor (called twice)
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
...
// does not work later in other methods
StatusInlines.Add(new Run($"{ text }{ Environment.NewLine }") { ToolTip = "tooltip" });
public observetecollection StatusInlines{get;set;}=new observetecollection();
...
//在构造函数中工作(调用两次)
添加(新运行($“{text}{Environment.NewLine}”){ToolTip=“ToolTip”});
...
//以后在其他方法中不起作用
添加(新运行($“{text}{Environment.NewLine}”){ToolTip=“ToolTip”});
BaseViewModel.cs(由angelsix提供):
公共类BaseViewModel:INotifyPropertyChanged
{
#区域保护成员
///
///用于属性检查的全局锁,以防止锁定表达式的不同实例。
///考虑到该检查的速度,全局锁定所有呼叫者并不是问题。
///
受保护对象mPropertyValueCheckLock=新对象();
#端区
///
///当任何子属性更改其值时激发的事件
///
公共事件PropertyChangedEventHandler PropertyChanged=(发送方,e)=>{};
///
///将此称为触发事件
///
///
公共void OnPropertyChanged(字符串名称)
{
PropertyChanged(此,新PropertyChangedEventArgs(名称));
}
#区域指挥助手
///
///如果未设置更新标志,则运行命令。
///如果该标志为true(表示函数已在运行),则该操作不会运行。
///如果该标志为false(表示没有运行的函数),则运行该操作。
///一旦操作完成(如果已运行),则该标志将重置为false
///
///定义命令是否已在运行的布尔属性标志
///如果命令尚未运行,则要运行的操作
///
受保护的异步任务RunCommandAsync(表达式更新标志,Func操作)
{
//锁定以确保单次进入检查
锁定(mPropertyValueCheckLock)
{
//检查flag属性是否为true(表示函数已在运行)
if(updateFlag.GetPropertyValue())
回来
//将属性标志设置为true以指示我们正在运行
updateingFlag.SetPropertyValue(true);
}
尝试
{
//运行已传递的操作
等待行动();
}
最后
{
//完成后,将属性标志设置回false
updateingFlag.SetPropertyValue(false);
}
}
///
///如果未设置更新标志,则运行命令。
///如果该标志为true(表示函数已在运行),则该操作不会运行。
///如果该标志为false(表示没有运行的函数),则运行该操作。
///一旦操作完成(如果已运行),则该标志将重置为false
///
///定义命令是否已在运行的布尔属性标志
///如果命令尚未运行,则要运行的操作
///操作返回的类型
///
受保护的异步任务RunCommandAsync(表达式更新标志,Func操作,T defaultValue=default(T))
{
//锁定以确保单次进入检查
锁定(mPropertyValueCheckLock)
{
//检查flag属性是否为true(表示函数已在运行)
if(updateFlag.GetPropertyValue())
返回默认值;
//将属性标志设置为true以指示我们正在运行
updateingFlag.SetPropertyValue(true);
}
尝试
{
//运行已传递的操作
返回等待操作();
}
最后
{
//完成后,将属性标志设置回false
updateingFlag.SetPropertyValue(false);
}
}
#端区
}
现在您只需设置一次TextBlock
内联线。您忘记订阅
INotifyCollectionChanged.CollectionChanged
事件并处理更改的项目
如果绑定源实现了INotifyCollectionChanged
,则以下重构版本的TextBlockExtensions
将侦听INotifyCollectionChanged.CollectionChanged
,并相应地处理更改:
TextBlockExtensions.cs
public class TextBlockExtensions : BaseViewModel
{
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached(
"BindableInlines",
typeof(IEnumerable<Inline>),
typeof(TextBlockExtensions),
new PropertyMetadata(default(IEnumerable<Inline>), OnBindableInlinesChanged));
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj) =>
(IEnumerable<Inline>) obj.GetValue(BindableInlinesProperty);
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value) =>
obj.SetValue(BindableInlinesProperty, value);
private static Dictionary<INotifyCollectionChanged, IList<WeakReference<TextBlock>>> CollectionToTextBlockMap { get; set; }
static TextBlockExtensions()
{
TextBlockExtensions.CollectionToTextBlockMap =
new Dictionary<INotifyCollectionChanged, IList<WeakReference<TextBlock>>>();
}
private static void OnBindableInlinesChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
{
if (!(attachingElement is TextBlock textBlock))
{
throw new ArgumentException("Attaching element must be of type 'TextBlock'.");
}
TextBlockExtensions.Cleanup(textBlock, e.OldValue);
if (!(e.NewValue is IEnumerable<Inline> inlineElements))
{
return;
}
textBlock.Inlines.AddRange(inlineElements);
if (inlineElements is INotifyCollectionChanged observableCollection)
{
ObserveCollectionChanges(observableCollection, textBlock);
}
}
private static void Cleanup(TextBlock textBlock, object oldCollection)
{
textBlock.Inlines.Clear();
if (oldCollection is INotifyCollectionChanged oldObservableCollection)
{
oldObservableCollection.CollectionChanged -= TextBlockExtensions.UpdateTextBlockOnCollectionChanged;
TextBlockExtensions.CollectionToTextBlockMap.Remove(oldObservableCollection);
}
}
private static void ObserveCollectionChanges(INotifyCollectionChanged observableCollection, TextBlock textBlock)
{
if (TextBlockExtensions.CollectionToTextBlockMap.TryGetValue(
observableCollection,
out IList<WeakReference<TextBlock>> boundTextBoxes))
{
boundTextBoxes.Add(new WeakReference<TextBlock>(textBlock));
}
else
{
observableCollection.CollectionChanged += TextBlockExtensions.UpdateTextBlockOnCollectionChanged;
TextBlockExtensions.CollectionToTextBlockMap.Add(
observableCollection,
new List<WeakReference<TextBlock>>() {new WeakReference<TextBlock>(textBlock)});
}
}
private static void UpdateTextBlockOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (TextBlockExtensions.CollectionToTextBlockMap.TryGetValue(
sender as INotifyCollectionChanged,
out IList<WeakReference<TextBlock>> boundTextBlocks))
{
var textBlockReferences = boundTextBlocks.ToList();
foreach (WeakReference<TextBlock> boundTextBlockReference in textBlockReferences)
{
if (boundTextBlockReference.TryGetTarget(out TextBlock textBlock))
{
UpdateCollection(textBlock, e);
}
else
{
// TextBlock already collected by the GC. Cleanup.
boundTextBlocks.Remove(boundTextBlockReference);
}
}
}
}
private static void UpdateCollection(TextBlock textBlock, NotifyCollectionChangedEventArgs eventArgs)
{
switch (eventArgs.Action)
{
case NotifyCollectionChangedAction.Add:
AddNewInlines(eventArgs.NewItems.OfType<Inline>(), textBlock);
break;
case NotifyCollectionChangedAction.Remove:
RemoveInlines(eventArgs.OldItems.OfType<Inline>(), textBlock);
break;
case NotifyCollectionChangedAction.Replace:
ReplaceInlines(eventArgs, textBlock);
break;
case NotifyCollectionChangedAction.Move:
MoveInlines(eventArgs, textBlock);
break;
case NotifyCollectionChangedAction.Reset:
textBlock.Inlines.Clear();
break;
}
}
private static void AddNewInlines(IEnumerable<Inline> newItems, TextBlock textBlock)
{
foreach (Inline newItem in newItems)
{
textBlock.Inlines.Add(newItem);
}
}
private static void RemoveInlines(IEnumerable<Inline> removedItems, TextBlock textBlock)
{
foreach (Inline removedItem in removedItems)
{
textBlock.Inlines.Remove(removedItem);
}
}
private static void ReplaceInlines(NotifyCollectionChangedEventArgs eventArgs, TextBlock textBlock)
{
int currentReplaceIndex = eventArgs.NewStartingIndex;
List<Inline> replacedItems = eventArgs.OldItems.OfType<Inline>().ToList();
List<Inline> replacementItems = eventArgs.NewItems.OfType<Inline>().ToList();
for (int changedItemsIndex = 0; changedItemsIndex < replacementItems.Count; changedItemsIndex++)
{
Inline replacedItem = textBlock.Inlines.ElementAt(currentReplaceIndex++);
Inline replacementItem = replacementItems.ElementAt(changedItemsIndex);
textBlock.Inlines.InsertAfter(replacedItem, replacementItem);
textBlock.Inlines.Remove(replacedItem);
}
}
private static void MoveInlines(NotifyCollectionChangedEventArgs eventArgs, TextBlock textBlock)
{
foreach (Inline movedItem in eventArgs.OldItems.OfType<Inline>())
{
Inline currentItemAtNewPosition = textBlock.Inlines.ElementAt(eventArgs.NewStartingIndex);
textBlock.Inlines.Remove(movedItem);
textBlock.Inlines.InsertAfter(currentItemAtNewPosition, movedItem);
}
}
}
公共类文本