代码之家  ›  专栏  ›  技术社区  ›  Ray Hayes

WPF中的ListView绑定刷新建议

  •  0
  • Ray Hayes  · 技术社区  · 15 年前

    我有一个 ObservableCollection 绑定到 ListBox 并设置突出显示机制 DataTrigger S,当我有一组简单的Highlighter(调试、警告等)时,我可以简单地用绑定到公开这些选项的视图模型的几个数据触发器来枚举样式。

    我现在已经升级了系统以支持多个用户定义的荧光笔,这些荧光笔通过 IsHighlighted(xxx) 方法(而不是属性)。

    我怎么做 ListView 是否知道可视状态(样式的DataTrigger)已更改?是否有“刷新”的事件我可以在 数据管理器 ?

    更新: 我有一个 数据管理器 映射到公开属性 Active 它只返回值“真”,但尽管没有更新:

    <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 回复  |  直到 13 年前
        1
  •  1
  •   Noldorin    15 年前

    当一个条件 DataTrigger 更改后,这将自动导致父UI元素刷新。

    需要检查的几个事项: 1。触发器的输入数据实际上如您所期望的那样发生了变化。 2.第2条。触发器的输入数据绑定到依赖属性。否则,您将永远不知道值何时更新。

    如果你向我们展示了你的XAML中适当的部分,那将有很大帮助。

        2
  •  1
  •   Drew Noakes    15 年前

    如果您只是想以某种方式设置项目的颜色,您可以编写一个转换器来满足您的需要:

    <Thing Background="{Binding Converter={StaticResource MyItemColorConverter}}" />
    

    在这种情况下,转换器可以调用 IsHighlighted(xxx) 方法并返回 Thing .

    如果你想设置多个属性,你可以使用多个转换器,但是这个想法在某个时刻开始瓦解。

    或者,您可以在 DataBinding 确定问题项是否属于某个类别,然后应用setter。这取决于你需要什么!

    编辑

    我刚刚重新阅读了你的问题,意识到我偏离了目标。哇哦。

    我相信你可以提高 INotifyPropertyChanged.PropertyChanged 用一个 PropertyChangedEventArgs 使用的 string.Empty ,这将强制WPF绑定基础结构刷新所有绑定。你试过了吗?

        3
  •  0
  •   Ray Hayes    13 年前

    我要用我需要做的事情来回答我自己的问题。

    这是一个很长的答案,因为我似乎一直在打击那些WPF认为它更了解并且会缓存的领域。如果datatrigger有一个无条件的改变,我就不需要这些了!

    首先,让我再次回顾一下这个问题。我有一个列表视图,可以用不同的样式突出显示不同的行。最初,这些样式是内置类型,如调试和错误。在这些情况下,我可以很容易地将它们的viewModel更改作为行样式中的数据触发器,并立即进行每个更新。

    一旦我升级到允许用户定义的Highlighter,我就不再有一个属性可以访问(即使我动态地创建了它们,样式也不会了解它们)。

    为了解决这个问题,我已经实现了 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);
        }
    

    尽管如此,我现在知道每当用户更改了注册的荧光笔,但它并没有修复我无法将触发器与任何内容相关联的事实,因此我可以反映显示样式中的更改。

    我找不到唯一的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. 它也注册到 PropertyChanged 作为ViewModel的HighlighterService的事件。
    3. 检测到更改后,它将替换 强光选择器 项目容器样式选择器 (注意,它首先交换为空值,因为BeaCosta在网络上有评论认为这是必要的)。

    所以,现在我所需要的是一个考虑到 现在的 突出显示选择(我知道如果它们发生变化,它将被重建),所以我不需要担心太多的事情。这个 强光选择器 迭代注册的荧光笔,并(如果启用)注册样式。我把这个缓存在 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);
        }
    }