Reputation: 2925
I have been searching for an answer for this for almost a week now, and I can't seem to find a way to do this. I would think it is a relatively simple thing...
I have a ListView, and one GridViewColumn contains TextBox items. I want to put a Label right underneath the ListView, and fill it with the sum of the items in all the TextBoxes. Whenever someone changes a value in any of the TextBoxes, the sum should change, too.
I know I need to use a Converter to get the sum... but I don't know how to get the binding right...
Any help would be greatly appreciated!
EDIT: I ended up solving this in a slightly different way. I will post my solution below.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="myParties" XPath="Parties" Source="XMLFile1.xml" />
<CollectionViewSource x:Key="myCollectionViewSource" Source="{StaticResource myParties}" />
<converters:SumConverter x:Key="mySumConverter" />
</Window.Resources>
<StackPanel>
<Button x:Name="Breakpoint" Click="bpClick" Content="Breakpoint"/>
<ListView x:Name="myListView"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Source={StaticResource myCollectionViewSource},XPath='Party',Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Width="100" DisplayMemberBinding="{Binding XPath='@Contact'}" Header="Contact"/>
<GridViewColumn DisplayMemberBinding="{Binding XPath='@Qty'}" Header="Q"/>
<GridViewColumn DisplayMemberBinding="{Binding XPath='@Amount'}" Header="Amt"/>
<GridViewColumn x:Name="tbTot" Header="Tot">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBox Width="100" Text="{Binding XPath='@Tot'}" />
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Label Height="22">
<Label.Content>
<MultiBinding Converter="{StaticResource mySumConverter}">
<Binding ElementName="myListView" Path="Items"/>
<Binding ElementName="myListView" Path="Items.Count"/>
</MultiBinding>
</Label.Content>
</Label>
</StackPanel>
</Window>
XML file:
<?xml version="1.0" encoding="utf-8" ?>
<Parties>
<Party Contact="Jim Shmekel"
Qty="1"
Amount="55.00"
Tot="55.00"/>
<Party Contact="Shmi Skywalker"
Qty="1"
Amount="20.00"
Tot="20.00"/>
<Party Contact="Jon Ronson"
Qty="1"
Amount="23.00"
Tot="23.00"/>
</Parties>
SumConverter:
Imports System.Collections.ObjectModel
Imports System.Reflection
<ValueConversion(GetType(Object()), GetType(String))>
Public Class SumConverter : Implements System.Windows.Data.IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Static lvItems As IList
Static lvItem As Xml.XmlElement
Dim nVal As Double
Convert = 0
lvItems = values(0)
If lvItems Is Nothing Then Exit Function
For Each lvItem In lvItems
'Debug.Print(lvItem.GetAttribute("Tot"))
If Double.TryParse(lvItem.GetAttribute("Tot"), nVal) Then
Convert = Convert + nVal
End If
Next
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
ConvertBack = Nothing
End Function
End Class
Upvotes: 1
Views: 977
Reputation: 2925
I'm a bit disappointed, because I really was hoping to approach this the way the sllev had suggested. To me, that seemed like the most logical way of doing it. There may still be a way, but I haven't figured it out.
Here is how I ended up solving this problem. Basically what I did was add the "Tot" column to my DataSource (it was not there in my initial solution although it is in my question above). I bound my TextBox to that column, and then refreshed the ListView on the LostFocus event of the TextBox within the ListView. The Refresh() causes the SumConverter to be called again.
Hopefully this helps someone else out - it seemed like a simple request but I have wasted a ton of time on it.
MainWindow.xaml:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="myParties" XPath="Parties" Source="XMLFile1.xml" />
<CollectionViewSource x:Key="myCollectionViewSource" Source="{StaticResource myParties}" />
<converters:SumConverter x:Key="mySumConverter" />
</Window.Resources>
<StackPanel>
<ListView x:Name="myListView"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Source={StaticResource myCollectionViewSource},XPath='Party',Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Width="100" DisplayMemberBinding="{Binding XPath='@Contact'}" Header="Contact"/>
<GridViewColumn DisplayMemberBinding="{Binding XPath='@Qty'}" Header="Q"/>
<GridViewColumn DisplayMemberBinding="{Binding XPath='@Amount'}" Header="Amt"/>
<GridViewColumn x:Name="tbTot" Header="Tot">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBox LostFocus="TextBox_LostFocus" Width="100" Text="{Binding XPath='@Tot'}" />
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Label VerticalAlignment="Stretch">
<Label.Content>
<MultiBinding Converter="{StaticResource mySumConverter}">
<Binding ElementName="myListView" Path="Items"/>
<Binding ElementName="myListView" Path="Items.Count"/>
</MultiBinding>
</Label.Content>
</Label>
</StackPanel>
</Window>
CodeBehind on MainWindow.xaml:
Class MainWindow
Public Sub New()
' This call is required by the designer.
InitializeComponent()
End Sub
Private Sub TextBox_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
myListView.Items.Refresh()
End Sub
End Class
SumConverter.vb:
<ValueConversion(GetType(Object()), GetType(String))>
Public Class SumConverter : Implements System.Windows.Data.IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Static lvItems As ItemCollection
Static lvItem As Xml.XmlElement
Dim nVal As Double
Convert = 0
lvItems = values(0)
If lvItems Is Nothing Then Exit Function
For Each lvItem In lvItems
'Debug.Print(lvItem.GetAttribute("Tot"))
If Double.TryParse(lvItem.GetAttribute("Tot"), nVal) Then
Convert = Convert + nVal
End If
Next
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
ConvertBack = Nothing
End Function
End Class
Upvotes: 0
Reputation: 62484
Straightforward solution (I'm sure not the best) is to bind a
ElementName = myListView, Path = Items
And in converter traverse items and using VisualTreeHelper.GetChild()
find out underlying TextBlock of an item and then int.TryParse(textBlock.Text, out currentItemValue);
Upvotes: 1