WPF中的ListView绑定刷新建议

时间:2009-08-25 10:45:57

标签: wpf listview binding

我有一个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>

3 个答案:

答案 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();
    }
}

这有两件事:

  1. 它为HighlightingSelector分配了一个新的ItemContainerStyleSelector(ListView被称为messages)。
  2. 它还将自身注册到HighlighterService的PropertyChanged事件,这是一个ViewModel。
  3. 在检测到更改后,它会替换HighlightingSelectorItemContainerStyleSelector的当前实例(请注意,它首先交换为null,因为归因于Bea Costa的网上有评论这是必要的)。
  4. 所以,现在我需要的是一个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);
        }
    }