【问题标题】:WPF MultiBinding to part of ObservableCollectionWPF MultiBinding 到 ObservableCollection 的一部分
【发布时间】:2023-03-27 12:58:01
【问题描述】:

我需要将 TextBlock 的文本设置为“名称(具有非空属性的项目数)”之类的内容。现在,我使用 ItemsSource.Count 使用整个集合的项目数来执行此操作。

<TextBlock x:Name="textBlockHeader" >
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource headerCreator}" x:Name="multiBinder">
            <Binding ElementName="trackingTable" Path="Name" />
            <Binding ElementName="trackingsGrid" Path="ItemsSource.Count" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock> 

为此,我正在使用 IMultiValueConverter:

internal class HeaderCreator : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // Based on this xaml
        ////<Binding ElementName="trackingTable" Path="Name" />                 values[0]
        ////<Binding ElementName="trackingsGrid" Path="ItemsSource.Count" />    values[1]

        return values[0] + " (" + values[1] + ")";
    }

trackingsGrid 是下面定义的 DataGrid(此处未显示代码),绑定到具有 TrackingData 对象的集合。 TrackingData 有一个名为 Tracking 的属性。我只需要计算 ObservableCollection 中具有此属性的项目不为空。我怎样才能做到这一点?

public class TrackingData : INotifyPropertyChanged
{
    public Model.ITracking Tracking { get; set; }
    ...
}

提前致谢。

【问题讨论】:

    标签: c# .net wpf xaml data-binding


    【解决方案1】:

    将这个逻辑(... ObservableCollection 中具有此属性不为空的项)放入您的 ViewModel 并绑定到此属性。

    【讨论】:

    • 逻辑移至 ViewModel 中的 ItemsCount 属性。在其设置器中触发 OnPropertyChanged。并且 ItemsCount 在 ICollectionView.CollectionChanged 的​​委托中设置。所有的答案都很有帮助。非常感谢!
    【解决方案2】:

    就我个人而言,我会将它添加到 ViewModel 中,因为它是高度定制的。

    话虽如此,您可以通过一些小的调整来完成这项工作。首先,将您的第二个绑定从ItemsSource.Count 更改为ItemsSource。这将导致您的IMultiValueConverter 内部的values[1] 成为整个集合。

    完成此操作后,您的转换器可能会发生变化,因此您需要报告:

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // Based on this xaml
        ////<Binding ElementName="trackingTable" Path="Name" />                 values[0]
        ////<Binding ElementName="trackingsGrid" Path="ItemsSource" />    values[1]
        IEnumerable<TrackingData> tracking = values[1] as IEnumerable<TrackingData>;
        if(tracking == null)
           return values[0] + " (0)"; // Put some reasonable value here?
    
        return values[0] + " (" + tracking.Where(t => t.Tracking != null).Count() + ")";
    }
    

    【讨论】:

    • 我尝试了您的快速解决方案。我以为就是这个。 TextBlock 启动正常,但没有更新。
    • @GRF:这是在这里使用 MultiValueCONverter 问题的一部分——它永远不会更新单个元素的更改。这实际上很难做到,因为需要观察每个元素的每个道具的变化。
    • @GRF:我实际上写了一个关于如何处理这种情况的完整示例:reedcopsey.com/2009/10/14/…
    【解决方案3】:

    我建议避免使用MultiBinding,除非它是完全必要的。在必要时很难划清界限,但通常您知道什么时候没有其他选项可以实现所需的行为,或者替代实施需要付出巨大的努力。我的直觉告诉我MultiBinding 不仅在性能方面效率较低,而且也更难阅读、测试和维护。

    也就是说,您可以通过 ViewModel 中的属性(如@polishchuk 建议的那样)或绑定到整个视图模型和单个转换器来实现您想要的行为,该转换器根据视图模型返回格式化字符串。在两个选项中,如果您不需要高级文本格式,我更喜欢第一个,否则,我更喜欢第二个。

    【讨论】:

      【解决方案4】:

      正如一些人指出的那样,我会将这个逻辑移到 ViewModel 中。这是一种简单的方法,不需要您附加一堆事件处理程序并编写自定义逻辑:

      在 ViewModel 中实现这一点的一种简单方法是创建一个 ICollectionView 来包装您的源集合并对其应用过滤器以仅包含属性不为 null 的值

      IList<T> source = ...
      ListCollectionView customView = new ListCollectionView(source);
      customView.Filter = obj => ((TrackingData)obj).Tracking != null;
      

      然后你可以通过你的 ViewModel 公开这个 ICollectionView 作为一个属性,比如TrackedItems

      然后在 XAML 中,您可以将数据绑定到 TrackedItems.CountICollectionView 将在您的 TrackingData 对象属性更改时保持同步

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-22
        • 2011-03-09
        • 1970-01-01
        • 2019-10-01
        • 2014-05-25
        相关资源
        最近更新 更多