Question

I'm really new in WPF and I need your help. I've app which allows user check continent and view containing countries. Country has two propetries: name and area. The problem is that I need to show average area of all continent's countries. My data model is looks like this:

       <XmlDataProvider x:Key="CountryStoreDataSource" XPath="CountryStore">
            <x:XData>
                <CountryStore xmlns="">
                    <Continents Continent="Europe">
                        <Countries Country="Italy" Area="300"/>
                        <Countries Country="Iceland" Area="350"/>
                    </Continents>
                    <Continents Continent="Asia">
                        <Countries Country="China" Area="700"/>
                        <Countries Country="India" Area="650"/>
                    </Continents>
                    <Continents Continent="Africa">
                        <Countries Country="South Africa" Area="550"/>
                        <Countries Country="Egypt" Area="500"/>
                    </Continents>
                </CountryStore>
            </x:XData>
        </XmlDataProvider>

also I have templates to connect listboxes with my data model:

        <Grid.Resources>
            <DataTemplate x:Key="countryItemTemplate">
                <Label Content="{Binding XPath=@Country}"/>
            </DataTemplate>
            <DataTemplate x:Key="areaItemTemplate">
                <Label Content="{Binding XPath=@Area}"/>
            </DataTemplate>
        </Grid.Resources>

finally I have the implementations of my listboxes:

           <ListBox
                Grid.Row="1"
                ItemsSource="{Binding XPath=Countries}"
                ItemTemplate="{StaticResource countryItemTemplate}"
                Margin="0,0,0,0" />
           <ListBox
                Grid.Row="1"
                ItemsSource="{Binding XPath=Countries}"
                ItemTemplate="{StaticResource areaItemTemplate}"
                Margin="0,0,0,0"
                Grid.Column="1" 
                Name="listBoxAreas"
                />

Actually I don't know how to get my values from listboxes in c# code and is there any way to get values and do something with them in xml? Thank you.

Was it helpful?

Solution

interesting question!

First of all, it seems like the XPaths you are using are not completely correct. If I understand you correctly, you want to display a ListBox with Countries, a ListBox with Areas and a TextBlock with Avg(areas).

First, let's simplify the XPath Binding. You should update the XPath on your XmlDataSource so that you just get a list of Countries, independent of Continent:

    <XmlDataProvider x:Key="CountryStoreDataSource" XPath="/CountryStore/Continents/Countries">
        <x:XData>
            <CountryStore xmlns="">
                <Continents Continent="Europe">
                    <Countries Country="Italy" Area="300"/>
                    <Countries Country="Iceland" Area="350"/>
                </Continents>
                <Continents Continent="Asia">
                    <Countries Country="China" Area="700"/>
                    <Countries Country="India" Area="650"/>
                </Continents>
                <Continents Continent="Africa">
                    <Countries Country="South Africa" Area="550"/>
                    <Countries Country="Egypt" Area="500"/>
                </Continents>
            </CountryStore>
        </x:XData>
    </XmlDataProvider>

Then, set the DataContext of the Grid:

<Grid DataContext="{StaticResource CountryStoreDataSource}"  ../>

After that, both bindings of the ListBoxes can be updated to the following: ItemsSource="{Binding}".


Now, back to the question: show the average of the Areas. For this, you need to bind to the same list and apply a converter to do the calculation.
First, let's create the Converter, which simply parses the list and calculates the average with LINQ:

public class AreaConverter : MarkupExtension, IValueConverter
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return 0;

        //you can do this in one line, but I split it for clarity.
        var xml = value as IEnumerable<XmlNode>;
        var areas = xml.Select(x => x.Attributes["Area"].Value);
        var avg = areas.Average(a => int.Parse(a));

        return avg;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportException();
    }
}

After that, you can create the TextBlock to hold this value with this Binding:

 <TextBlock Text="{Binding Converter={local:AreaConverter}}" />

Now the TextBlock should display the Average!

TIP: to test your given XPaths, you can use Notepad++ with the XML Tools plugin, which provides a Evaluate XPath Expression tool

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top