tags:

views:

476

answers:

3

I have a GridView that contains a list of files, created dates, and file sizes. Below the grid I have a textblock that says "X Files Selected. Y MB". I can bind to SelectedItems.Count just fine, but can I easily bind to the sum of the file sizes for those that are selected?

The question marks below should be the sum of the SelectedItems fileSize column values. Any ideas?

<TextBlock HorizontalAlignment="Right">
     <TextBlock.Text>
      <MultiBinding StringFormat=" {0} Files Selected. {1} MB">
       <Binding ElementName="FilesList" Path="SelectedItems.Count"></Binding>
       <Binding ElementName="FilesList" Path="SelectedItems.?????"></Binding>
      </MultiBinding>
     </TextBlock.Text>
</TextBlock>

I know I can get this done in the codebehind - but I'd like to keep my codebehind empty and do it in the XAML. This is the codebehind code:

private void FilesList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    double x = 0;

    foreach (FileInfo fileInfo in FilesList.SelectedItems)
    {
     x += fileInfo.Length;
    }
}
+2  A: 

You're going to have to use a converter for this. An example: Xaml:




<MultiBinding StringFormat=" {0} Files Selected. {1} MB">
                        <Binding ElementName="FilesList" Path="SelectedItems.Count"></Binding>
                        <Binding ElementName="FilesList" Path="SelectedItems" Converter="{StaticResource sumconverter}"></Binding>
                </MultiBinding>

Codebehind:


[ValueConversion(typeof(ListViewItem[]), typeof(string))]
    class SumConverter : IValueConverter {
     public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) {
      int size = 0;
      ListViewItem[] items = (ListViewItem[])value;
      if(items != null){
       foreach(var lvi in items){
        Someclass sc = lvi.content as Someclass;
        if(sc!=null){
         size += sc.Size;
        }
       }
      }
      return (size / 1000) + "MB";
     }

     public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) {
      return null;
     }
    }

apandit
Nice. Well done.
Anderson Imes
Thanks for the example - I assume this will work just fine - but I need the binding to update on FilesList_SelectionChanged. Not sure how to accomplish that.
djschwartz
It should automatically refresh on selectionChanged since you're using SelectedItems as your binding source. If that doesn't work, you could always try to access the binding and refresh it.
apandit
I thought it would automatically refresh but it didn't. I put a break point in the converter and it only fired when the view first loaded. I also tried://BindingExpression bindingExpression = FilesSelectedTextBlock.GetBindingExpression(TextBlock.TextProperty);if bindingExpression != null bindingExpression.UpdateTarget();but bindingExpression was always null.
djschwartz
A: 

Sadly, you will not be able to do this in XAML, alone.

You will need to bind to the SelectedItems themselves and provide a value converter. The value converter needs to iterate over each file path, create a FileInfo object from the path, and sum up the sizes using the FileInfo.Length property.

Charlie
A: 

You have 3 options.

  1. You can create a sum property in whatever entity you are binding to (your FilesList entity). This will require you to change your FilesList collection to a CollectionView so you can get access to the SelectedItems property from your ViewModel (if you aren't doing this already).

  2. I've never tried this, but you might be able to use Kent Boogaart's "Expression Value Converter" that allows you to write small bits of C#-Like code in your binding expressions: http://wpfconverters.codeplex.com/

  3. Provide a simple ValueConverter that converts a collection of whatever your entity is to a decimal or whatever (this is probably the simplest thing to do).

Anderson Imes
Apandit posted a really nice example of option #3.
Anderson Imes