我有一个ObservableCollection
绑定到ListBox
并且有一个高亮显示机制设置DataTrigger
s,当我有一组简单的荧光笔(调试,警告等)时我可以简单地使用绑定到视图模型的多个数据触发器枚举样式,从而公开这些选项。
我现在已经升级了系统以支持多个用户定义的荧光笔,这些荧光笔使用IsHighlighted(xxx)
方法(不是属性)暴露自己。
如何让ListView
知道视觉状态(样式的数据触发器)已经改变?是否有一个“刷新”的事件,我可以在DataTrigger
中发射并捕捉到它?
更新
我有DataTrigger
映射到公开的属性Active
,它只返回值true,但尽管没有更新:
<DataTrigger Binding="{Binding Highlight.Active}"
Value="true">
<Setter Property="Background"
Value="{Binding Type, Converter={StaticResource typeToBackgroundConverter}}" />
<Setter Property="Foreground"
Value="{Binding Type, Converter={StaticResource typeToForegroundConverter}}" />
</DataTrigger>
答案 0 :(得分:1)
当DataTrigger
的条件发生变化时,这会自动导致父UI元素刷新。
要检查的几件事: 1.触发器的输入数据实际上正在按预期变化。 2.触发器的输入数据绑定到依赖项属性。否则,您永远不会知道值何时更新。
如果您向我们展示了XAML的适当部分,那将会有很大帮助。
答案 1 :(得分:1)
如果您只是想以某种方式设置项目的颜色,您可以编写一个能够满足您需求的转换器:
<Thing Background="{Binding Converter={StaticResource MyItemColorConverter}}" />
在这种情况下,转换器可以调用IsHighlighted(xxx)
方法并返回Thing
的相应颜色。
如果你想设置多个属性,你可以使用多个转换器,但这个想法在某些时候开始崩溃。
或者,您可以在DataBinding
上使用转换器来确定相关项目是否属于某个类别,然后应用setter。这取决于你需要什么!
修改强>
我刚刚重新阅读了你的问题并意识到我已经脱离了标记。糟糕。
我相信您只能使用INotifyPropertyChanged.PropertyChanged
使用PropertyChangedEventArgs
来提升string.Empty
,这会强制WPF绑定基础结构刷新所有绑定。你试过了吗?
答案 2 :(得分:0)
我将回答我自己的问题并解释我需要做什么。
这是一个很长的答案,因为我似乎一直在打击WPF认为它更清楚并且会缓存的区域。如果DataTrigger有一个无条件的改变,我就不需要这个了!
首先,让我再次回顾一些问题。我有一个ListView,可以突出显示不同样式的不同行。最初,这些样式是内置类型,例如Debug和Error。在这些情况下,我可以轻松地将它们的ViewModel更改为行样式的DataTriggers,并立即进行每次更新。
一旦我升级到允许用户定义的荧光笔,我就不再有一个属性可以锁定(即使我动态创建它们,样式也不会知道它们)。
为了解决这个问题,我已经实现了HighlightingService
(可以通过使用我的ServiceLocator
并要求IHightlightingServce
支持实例来随时发现这一点。该服务实现了许多重要的属性和方法:
public ObservableCollection<IHighlighter> Highlighters { get; private set; }
public IHighlighterStyle IsHighlighted(ILogEntry logEntry)
{
foreach (IHighlighter highlighter in Highlighters)
{
if ( highlighter.IsMatch(logEntry) )
{
return highlighter.Style;
}
}
return null;
}
由于Highlighters集合可公开访问,因此我决定允许该集合的用户添加/删除条目,从而无需实施添加/删除方法。但是,因为我需要知道内部IHighlighter
记录是否已更改,所以在服务的构造函数中,我将一个观察者注册到其CollectionChanged
属性,并通过注册另一个回调来对添加/删除项做出反应,这允许我发起特定于服务的INotifyCollectionChanged
事件。
[...]
// Register self as an observer of the collection.
Highlighters.CollectionChanged += HighlightersCollectionChanged;
}
private void HighlightersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var newItem in e.NewItems)
{
System.Diagnostics.Debug.Assert(newItem != null);
System.Diagnostics.Debug.Assert(newItem is IHighlighter);
if (e.NewItems != null
&& newItem is IHighlighter
&& newItem is INotifyPropertyChanged)
{
// Register on OnPropertyChanged.
IHighlighter highlighter = newItem as IHighlighter;
Trace.WriteLine(string.Format(
"FilterService detected {0} added to collection and binding to its PropertyChanged event",
highlighter.Name));
(newItem as INotifyPropertyChanged).PropertyChanged += CustomHighlighterPropertyChanged;
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var oldItem in e.OldItems)
{
System.Diagnostics.Debug.Assert(oldItem != null);
System.Diagnostics.Debug.Assert(oldItem is IHighlighter);
if (e.NewItems != null
&& oldItem is IHighlighter
&& oldItem is INotifyPropertyChanged)
{
// Unregister on OnPropertyChanged.
IHighlighter highlighter = oldItem as IHighlighter;
Trace.WriteLine(string.Format(
"HighlightingService detected {0} removed from collection and unbinding from its PropertyChanged event",
highlighter.Name));
(oldItem as INotifyPropertyChanged).PropertyChanged -= CustomHighlighterPropertyChanged;
}
}
}
}
private void CustomHighlighterPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ( sender is IHighlighter )
{
IHighlighter filter = (sender as IHighlighter);
Trace.WriteLine(string.Format("FilterServer saw some activity on {0} (IsEnabled = {1})",
filter.Name, filter.Enabled));
}
OnPropertyChanged(string.Empty);
}
尽管如此,我现在知道用户何时更改了已注册的荧光笔,但它没有修复我无法将触发器关联到任何事物的事实,因此我可以反映显示的样式中的更改。 / p>
我找不到Xaml排序的唯一方法,所以我创建了一个包含ListView的自定义控件:
public partial class LogMessagesControl : UserControl
{
private IHighlightingService highlight { get; set; }
public LogMessagesControl()
{
InitializeComponent();
highlight = ServiceLocator.Instance.Get<IHighlightingService>();
if (highlight != null && highlight is INotifyPropertyChanged)
{
(highlight as INotifyPropertyChanged).PropertyChanged += (s, e) => UpdateStyles();
}
messages.ItemContainerStyleSelector = new HighlightingSelector();
}
private void UpdateStyles()
{
messages.ItemContainerStyleSelector = null;
messages.ItemContainerStyleSelector = new HighlightingSelector();
}
}
这有两件事:
HighlightingSelector
分配了一个新的ItemContainerStyleSelector
(ListView被称为messages
)。PropertyChanged
事件,这是一个ViewModel。HighlightingSelector
上ItemContainerStyleSelector
的当前实例(请注意,它首先交换为null,因为归因于Bea Costa的网上有评论这是必要的)。所以,现在我需要的是一个HighlightingSelector,它考虑了当前突出显示选项(我知道如果它们改变了,它将被重建),所以我不需要担心事情太多了)。 HighlightingSelector
遍历已注册的荧光笔并且(如果已启用)注册样式。我将其缓存在Dictionary
中,因为重建这些可能很昂贵,并且因为它们只是在用户进行手动交互时构建,所以预先做到这一点的成本增加并不明显。
运行时将调用HighlightingSelector.SelectStyle
传递我关心的记录,我所做的只是返回相应的样式(基于用户原始突出显示首选项)。
public class HighlightingSelector : StyleSelector
{
private readonly Dictionary<IHighlighter, Style> styles = new Dictionary<IHighlighter, Style>();
public HighlightingSelector()
{
IHighlightingService highlightingService = ServiceLocator.Instance.Get<IHighlightingService>();
if (highlightingService == null) return;
foreach (IHighlighter highlighter in highlightingService.Highlighters)
{
if (highlighter is TypeHighlighter)
{
// No need to create a style if not enabled, should the status of a highlighter
// change, then this collection will be rebuilt.
if (highlighter.Enabled)
{
Style style = new Style(typeof (ListViewItem));
DataTrigger trigger = new DataTrigger();
trigger.Binding = new Binding("Type");
trigger.Value = (highlighter as TypeHighlighter).TypeMatch;
if (highlighter.Style != null)
{
if (highlighter.Style.Background != null)
{
trigger.Setters.Add(new Setter(Control.BackgroundProperty,
new SolidColorBrush((Color) highlighter.Style.Background)));
}
if (highlighter.Style.Foreground != null)
{
trigger.Setters.Add(new Setter(Control.ForegroundProperty,
new SolidColorBrush((Color) highlighter.Style.Foreground)));
}
}
style.Triggers.Add(trigger);
styles[highlighter] = style;
}
}
}
}
public override Style SelectStyle(object item, DependencyObject container)
{
ILogEntry entry = item as ILogEntry;
if (entry != null)
{
foreach (KeyValuePair<IHighlighter, Style> pair in styles)
{
if (pair.Key.IsMatch(entry) && pair.Key.Enabled)
{
return pair.Value;
}
}
}
return base.SelectStyle(item, container);
}
}