开发者

WPF MultiBinding to part of ObservableCollection

开发者 https://www.devze.com 2023-03-17 09:37 出处:网络
I need to set a TextBlock\'s Text to something like \"Name (number of items with not null property)\". Right now, I\'m doing this using the number of items of the entire collection, using ItemsSource.

I need to set a TextBlock's Text to something like "Name (number of items with not null property)". Right now, I'm doing this using the number of items of the entire collection, using 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> 

F开发者_运维技巧or that I'm using an 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 is a DataGrid defined below (not showing code here), binded to the collection, which has TrackingData objects. TrackingData has a property called Tracking. I need to count only the items in the ObservableCollection that has this property as not null. How can I achieve this?

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

Thanks in advance.


Put this logic (... items in the ObservableCollection that has this property as not null) in your ViewModel and bind to this property.


Personally, I would add this to the ViewModel, since it's so highly customized.

That being said, you could make this work with a couple of minor tweaks. First, change your second binding from ItemsSource.Count to ItemsSource. This will cause values[1] inside of your IMultiValueConverter to be the entire collection.

Once you've done this, your converter could change, so you report:

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() + ")";
}


I would suggest to avoid using MultiBinding unless it's totally necessary. It's hard to draw the line when it's necessary but usually you know when there are no other options to achieve desired behavior or alternative implementation requires significant efforts. My gut tells me MultiBinding not only less efficient in terms of performance but it's also harder to read, test and maintain.

That said you could achieve your desired behavior through a property in your ViewModel (as @polishchuk suggests), or with binding to the whole view model and single converter that returns formatted string based on view model. Among two options I would prefer the first one if you don't need advanced text formatting, and the second one otherwise.


As a couple of people have pointed out I would move this logic into the ViewModel. Here's one easy way to do so that doesn't require you to attach a bunch of event handlers and write custom logic:

One easy way to implement this in the ViewModel is to create an ICollectionView to wrap your source collection and applying a filter to it to include only values whose property is not null

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

Then you could expose this ICollectionView through your ViewModel as a proprerty, say TrackedItems

Then in XAML you can databind to TrackedItems.Count. The ICollectionView will stay in sync as your TrackingData object properties change

0

精彩评论

暂无评论...
验证码 换一张
取 消