Display master detail data in nested ListBox - c#

I want to display my data like this
Here is the class definition:
public class Order
{
public string Customer { get; set; }
public decimal Amount { get; set; }
public List<OrderDetail> Details = new List<OrderDetail>();
}
public class OrderDetail
{
public string Product { get; set; }
public int Qty { get; set; }
}
I try to use ListBox but fail to display Details. Below is my try in xaml:
<ListBox x:Name="lstOrder">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Customer}"/>
<TextBlock Text="{Binding Amount}"/>
<ListBox ItemsSource="{Binding Details}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Product}"/>
<TextBlock Text="{Binding Qty}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
in code behind:
public MainWindow()
{
InitializeComponent();
List<Order> OrderList = new List<Order>();
Order order = new Order { Customer = "David", Amount = 2000 };
order.Details.Add(new OrderDetail { Product = "A", Qty = 5 });
OrderList.Add(order);
order = new Order { Customer = "John", Amount = 5000 };
order.Details.Add(new OrderDetail { Product = "A", Qty = 2 });
order.Details.Add(new OrderDetail { Product = "B", Qty = 3 });
OrderList.Add(order);
lstOrder.ItemsSource = OrderList;
}
What is the correct way to bind OrderList.Details? Any suggestions will be appreciated.

In your Order class Details is a field not a property. Binding to fields is not supported.
You can modify the class slightly so it becomes:
public class Order
{
public string Customer { get; set; }
public decimal Amount { get; set; }
List<OrderDetail> details;
public List<OrderDetail> Details { get { return details; } }
public Order()
{
details = new List<OrderDetail>();
}
}
And then it will bind correctly.

Related

ListView ItemSource ObservableRangeCollection<> accessing Model ObservableRangeCollection<>

I'm developing an .NET Xamarin App. I want to list all Products as ItemTemplate in the Grouped-ListView and Invoice.Name as Header. Currently I only get the GroupHeaderTemplate shown in my Grouped-ListView.
Model: Invoice.cs
public class Invoice : ObservableRangeCollection<Invoice>
{
public string Name { get; set; }
public Company Company { get; set; }
public ObservableRangeCollection<Product> Products { get; set; }
}
Model: Products.cs
public class Product : ObservableRangeCollection<Product>
{
public string Name { get; set; }
public DateTime ExpireDate { get; set; }
}
ViewModel: MainViewVM (creating fake data)
public ObservableRangeCollection<Invoice> Invoices { get; set; }
public MainViewVM()
{
Invoices.Add(new Invoice { Name = "Test1", Company = new Company { Image = image }, Products = new ObservableRangeCollection<Product>() { new Product { Name = "ProductTest1", CreatedDate = DateTime.Now }, new Product { Name = "ProductTest2", CreatedDate = DateTime.Now } } });
Invoices.Add(new Invoice { Name = "Test2", Company = new Company { Image = image }, Products = new ObservableRangeCollection<Product>() { new Product { Name = "ProductTest3", CreatedDate = DateTime.Now }, new Product { Name = "ProductTest4", CreatedDate = DateTime.Now } } });
}
View: MainViewVM (ListView)
<ListView
IsGroupingEnabled="True"
ItemsSource="{Binding Invoices}">
<ListView.GroupHeaderTemplate>
<DataTemplate x:DataType="models:Invoice">
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="Center">
<Image Source="{Binding Company.Image}" WidthRequest="66" VerticalOptions="Center"/>
<Label Text="{Binding Name}" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Product">
<ViewCell>
<StackLayout VerticalOptions="Center">
<Label Text="{Binding Name}" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
I added a constructor with : base. That fixed it for me.
https://learn.microsoft.com/de-de/xamarin/xamarin-forms/user-interface/collectionview/grouping
public Invoice(string name, Company company, ObservableRangeCollection<Product> products) : base(products)
{
Name = name;
Company = company;
Products = products;
}

ObservableCollection not showing any items on ListView

I am trying to bind a ObservableCollection on a ListView ItemsSource but it doesn't show anything to me, tried binding inside code, on the xaml..
public partial class ReadPage : ContentPage
{
private CarregarClientes _CClientes = new CarregarClientes();
private MySQLCon _db = new MySQLCon();
private MySQLConOverloads _over = new MySQLConOverloads();
private MySQLiteCon _dbSqLiteCon = new MySQLiteCon();
private MySQLiteConOverloads _oversqlite = new MySQLiteConOverloads();
//MY OBSERVABLECOLLECTION DEFINITION -----------------------------
public ObservableCollection<Clientes> _ClientesList { get; set; }
private string _tabela = "Clientes";
public ReadPage()
{
InitializeComponent();
backBtn.Clicked += async (s, o) => await Navigation.PopModalAsync();
//Method used to populate the ObservableCollection (_ClientesList);
PopularObservableCollection();
}
I am defining the ObservableCollection inside my ReadPage class, then populating it with a method called PopularObservableCollection.
public void PopularObservableCollection()
{
_ClientesList = new ObservableCollection<Clientes>();
int quantidadeDados = _CClientes.CNumeroItems() -1;
List<string> id = _CClientes.Cid();
List<string> debito = _CClientes.CDebito();
List<string> endereco = _CClientes.CEndereco();
List<string> nome = _CClientes.CNome();
List<string> observacao = _CClientes.CObservacao();
List<string> saldo = _CClientes.CSaldo();
List<string> telefone = _CClientes.CTelefone();
for (int i = 0; i <= quantidadeDados; i++)
{
_ClientesList.Add(new Clientes
{
id = id[i],
Debito = debito[i],
Endereco = endereco[i],
Nome = nome[i],
Observacao = observacao[i],
Saldo = saldo[i],
Telefone = telefone[i]
});
}
}
Clientes.cs:
public class Clientes : BindableObject
{
public string id { get; set; }
public string Nome { get; set; }
public string Endereco { get; set; }
public string Telefone { get; set; }
public string Debito { get; set; }
public string Saldo { get; set; }
public string Observacao { get; set; }
}
XAML:
<ListView
BindingContext="{Binding Source={x:Reference MyPage}, Path=.}"
x:Name="readListView"
BackgroundColor="PaleVioletRed"
HasUnevenRows="True"
CachingStrategy="RecycleElement"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding _ClientesList}"
VerticalOptions="FillAndExpand"
ItemTapped="ReadListView_OnItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="Id:" Grid.Row="0"/>
<Label Text="{Binding id}" Grid.Row="0" Margin="10,0,0,0"/>
<Label Text="{Binding Nome}" Grid.Row="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

WPF, Caliburn.Micro and Dapper ComboBoxes

I am just new to WPF, Caliburn.Micro and Dapper. I have three combo boxes: the first one is for the region, the second one is for the provinces in the particular selected region and the third one are the cities in the particular selected province. What I want to achieve is that when I selected a particular region it will display all the provinces in that region, the same with the province combo box, when selected it will display all the cities associated with that province. Can this be done in a single method? Here is my code so far.
DataAccess
public List<RegionModel> GetRegion_All()
{
List<RegionModel> output;
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(GlobalConfig.CnnString(db)))
{
output = connection.Query<RegionModel>("dbo.spRegion_GetAll").ToList();
var p = new DynamicParameters();
foreach (RegionModel region in output)
{
p = new DynamicParameters();
p.Add("#RegionId", region.Id);
region.Provinces = connection.Query<ProvinceModel>("dbo.spProvince_ByRegion", p, commandType: CommandType.StoredProcedure).ToList();
foreach (ProvinceModel province in region.Provinces)
{
p = new DynamicParameters();
p.Add("#ProvinceId", province.Id);
region.Cities = connection.Query<CityModel>("dbo.spCity_ByProvince", p, commandType: CommandType.StoredProcedure).ToList();
}
}
}
return output;
}
Models
public class RegionModel
{
public int Id { get; set; }
public string Region { get; set; }
public string RegionName { get; set; }
public List<ProvinceModel> Provinces { get; set; } = new List<ProvinceModel>();
public List<CityModel> Cities { get; set; } = new List<CityModel>();
public List<BarangayModel> Barangays { get; set; } = new List<BarangayModel>();
}
public class ProvinceModel
{
public int Id { get; set; }
public string Province { get; set; }
public int RegionId { get; set; }
}
public class CityModel
{
public int Id { get; set; }
public string City { get; set; }
public int ProvinceId { get; set; }
public int ZipCode { get; set; }
}
ViewModel
public class ShellViewModel : Screen
{
private BindableCollection<RegionModel> _region;
private RegionModel _selectedRegion;
private ProvinceModel _selectedProvince;
public ShellViewModel()
{
GlobalConfig.InitializeConnections(DatabaseType.Sql);
Region = new BindableCollection<RegionModel>(GlobalConfig.Connection.GetRegion_All());
}
public BindableCollection<RegionModel> Region
{
get { return _region; }
set
{
_region = value;
}
}
public RegionModel SelectedRegion
{
get { return _selectedRegion; }
set
{
_selectedRegion = value;
NotifyOfPropertyChange(() => SelectedRegion);
}
}
public ProvinceModel SelectedProvince
{
get { return _selectedProvince; }
set
{
_selectedProvince = value;
NotifyOfPropertyChange(() => SelectedRegion);
}
}
View
<Window x:Class="WPFUI.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFUI.Views"
mc:Ignorable="d" WindowStartupLocation="CenterScreen"
Title="ShellView" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" x:Name="Region" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RegionName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox Grid.Row="1" x:Name="SelectedRegion_Provinces" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Province}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox Grid.Row="2" x:Name="SelectedRegion_Cities" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding City}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Most of my codes ideas are from the tutorials I found in youtube, since references and materials for WPF, Caliburn.Micro and Dapper are very hard to find. Please be patient with my code :)
You have lot of mistake and you dont use the power of Caliburn
The wpf definition:
with Caliburn if you give name Region for combobox, it waits a bindableCollection with same name (Region) and SelectedItem is named SelectedRegion
(see name convention with Caliburn). So i choose Region, Province & City
In the different models i have renamed all strings RegionName, ProvinceName and CityName.
<ComboBox Grid.Row="0" x:Name="Region" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RegionName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox Grid.Row="1" x:Name="Province" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ProvinceName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox Grid.Row="2" x:Name="City" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CityName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
then, i have modified your class definition:
public class RegionModel:PropertyChangedBase
{
public int Id { get; set; }
public string Region { get; set; }
public string RegionName { get; set; }
// each Region has its Provinces
public List<ProvinceModel> Provinces { get; set; } = new List<ProvinceModel>();
}
public class ProvinceModel:PropertyChangedBase
{
public int Id { get; set; }
public string ProvinceName { get; set; }
public int RegionId { get; set; }
// each Province has its Cities
public List<CityModel> Cities { get; set; } = new List<CityModel>();
}
public class CityModel:PropertyChangedBase
{
public int Id { get; set; }
public string CityName { get; set; }
public int ProvinceId { get; set; }
public int ZipCode { get; set; }
}
Then in ViewModel i add the different BindableCollection Region, Province & City dont forget to add
using Caliburn.Micro;
in the using definion. Then Add the Selected Definition
public class ShellViewModel : Screen
{
private RegionModel selectedRegion;
private ProvinceModel selectedProvince;
private CityModel selectedCity;
private BindableCollection<RegionModel> _region;
private BindableCollection<ProvinceModel> _province;
private BindableCollection<CityModel> _city;
public BindableCollection<RegionModel> Region
{
get { return _region; }
set
{
_region = value;
NotifyOfPropertyChange(() => Region);
}
}
public BindableCollection<ProvinceModel> Province
{
get { return _province; }
set
{
_province = value;
NotifyOfPropertyChange(() => Province);
}
}
public BindableCollection<CityModel> City
{
get { return _city; }
set
{
_city = value;
NotifyOfPropertyChange(() => City);
}
}
public RegionModel SelectedRegion
{
get { return selectedRegion; }
set
{
selectedRegion = value;
NotifyOfPropertyChange(() => SelectedRegion);
Province.Clear();
Province.AddRange(selectedRegion.Provinces);
NotifyOfPropertyChange(() => Province);
}
}
public ProvinceModel SelectedProvince
{
get { return selectedProvince; }
set
{
selectedProvince = value;
NotifyOfPropertyChange(() => SelectedProvince);
City.Clear();
City.AddRange(selectedProvince.Cities);
NotifyOfPropertyChange(() => City);
}
}
public CityModel SelectedCity
{
get { return selectedCity; }
set
{
selectedCity = value;
NotifyOfPropertyChange(() => SelectedCity);
}
}
public ShellViewModel()
{
// to DO INITIALIZE Regions
//
Province = new BindableCollection<ProvinceModel>();
City = new BindableCollection<CityModel>();
Region = new BindableCollection<RegionModel>(Regions);
}
and you have a functional sample
Each time you select a region, the associated provinces are loaded and same thing if you select a province for the associated Cities

Storing Radiobutton selection from different groups to a list

I need to get all selected radio button tags and group name after user clicks on Appbar button submit and store it in a List.
So i can compare the user submitted answer list with the list from the server..
If i use Checked="Answer_Checked" , List is over written when i click another radio button in Question 2
public class RootObject
{
public RootObject(int id, string question, int qno, int qcount)
{
this.id = id;
this.question = question;
this.qcount = qcount;
this.qno = qno;
}
public int id { get; set; }
public string question { get; set; }
public int qcount { get; set; }
public int qno { get; set; }
public int time { get; set; }
}
public class AnswerObject
{
public AnswerObject(int question_id, int answer_id, string answer, int is_right_option)
{
this.question_id = question_id;
this.answer_id = answer_id;
this.answer = answer;
this.is_right_option = is_right_option;
}
public int question_id { get; set; }
public int answer_id { get; set; }
public string answer { get; set; }
public int is_right_option { get; set; }
}
public class Question
{
public string QuestionName { get; set; }
public int qcount { get; set; }
public int qno { get; set; }
public ObservableCollection<Option> options { get; set; }
}
public class Option
{
public string QuestionAnswer { get; set; }
public string groupname { get; set; }
public int IsCorrect { get; set; }
}
C# Coding
var result1 = await response1.Content.ReadAsStringAsync();
var objResponse1 = JsonConvert.DeserializeObject<List<RootObject>>(result1);
var result2 = await response2.Content.ReadAsStringAsync();
var objResponse2 = JsonConvert.DeserializeObject<List<AnswerObject>>(result2);
for (int i = 0; i < objResponse1.LongCount(); i++)
{
ObservableCollection<Option> options1 = new ObservableCollection<Option>();
for (int j = 0; j < objResponse2.LongCount(); j++)
{
if (objResponse1[i].id == objResponse2[j].question_id)
{
options1.Add(new Option() { QuestionAnswer = objResponse2[j].answer, IsCorrect = objResponse2[j].is_right_option, groupname = objResponse2[j].question_id.ToString() });
}
}
questions.Add(new Question() { QuestionName = objResponse1[i].question, qno=i + 1, qcount =objResponse1.Count, options = options1 });
}
flipView.ItemsSource = questions;
XAML Coding
<FlipView x:Name="flipView" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding}" Margin="0,35,0,0">
<FlipView.ItemTemplate>
<DataTemplate>
<ListView Name="ItemData" SelectionMode="None" ItemsSource="{Binding}" >
<Grid x:Name="ContentPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="Testing" Margin="10,20" FontSize="22.333">
<Run Text="{Binding qno}"/>
<Run Text="of"/>
<Run Text="{Binding qcount}"/>
</TextBlock>
<TextBlock x:Name="Question" Text="{Binding QuestionName}" Margin="10,60" VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="22.333" TextWrapping="Wrap"/>
<ListBox Grid.Row="1" Padding="0" Margin="10,-10" ItemsSource="{Binding options}" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton x:Name="Answer" GroupName="{Binding groupname}" Checked="Answer_Checked" Tag="{Binding IsCorrect}" Margin="10,2">
<RadioButton.Content>
<TextBlock Text="{Binding QuestionAnswer}" Foreground="White"/>
</RadioButton.Content>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ListView>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
Answer in JSON Format
[{"question_id":3,"answer_id":1,"answer":"10%","is_right_option":0},{"question_id":3,"answer_id":2,"answer":"10.25%","is_right_option":1},{"question_id":3,"answer_id":3,"answer":"10.5%","is_right_option":0},{"question_id":3,"answer_id":4,"answer":"None of these","is_right_option":0},{"question_id":4,"answer_id":5,"answer":"Rs. 2.04","is_right_option":1},{"question_id":4,"answer_id":6,"answer":"Rs. 3.06","is_right_option":0},{"question_id":4,"answer_id":7,"answer":"Rs. 4.80","is_right_option":0},{"question_id":4,"answer_id":8,"answer":"Rs. 8.30","is_right_option":0}]
Question in JSON Format
[{"id":3,"question":"An automobile financier claims to be lending money at simple interest, but he includes the interest every six months for calculating the principal. If he is charging an interest of 10%, the effective rate of interest becomes: ","time":1},{"id":4,"question":"What is the difference between the compound interests on Rs. 5000 for 1 years at 4% per annum compounded yearly and half-yearly? ","time":1}]
I solved it using Dictionary instead of using List
private void Answer_Checked(object sender, RoutedEventArgs e) // Radio button click
{
var radio = sender as RadioButton;
bool check = Convert.ToBoolean(radio.IsChecked);
if(check)
{
Answer[Convert.ToInt16(radio.GroupName)] = Convert.ToInt16(radio.Tag);
}
}
public async void Check_Result() // Evaluate result
{
foreach (KeyValuePair<int, int> count in Answer)
{
if (count.Value == 1)
{
result++;
}
}
MessageDialog showresult = new MessageDialog(result.ToString());
await showresult.ShowAsync();
Frame.Navigate(typeof(MainPage), null);
}
public void TestSubmit_Click(object sender, RoutedEventArgs e) // AppBar button click
{
Check_Result();
}

Change binding at runtime to a particular element of a list based off property of the list item

Say I have a list of Categories (A, B, C, D...) as well as a list of Items which have as a member a list of Details about themselves.
public class Item
{
public string Name { get; set; }
public List<Details> ItemDetails { get; set; }
}
public class Details
{
public string Category { get; set; }
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Value3 { get; set; }
}
What I would like to do is to be able to display information about the Item based on which Category is selected in the listview, so if the user selects B then the listview on the right will have in its datatemplate all of the information about that category. I know how to hardcode it to display a particular item in the list.
<ListView x:Name="CategoriesListView" ItemsSource="{Binding Categories}"/>
<ListView x:Name="ItemsListView" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding ItemDetails[0].Value1}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
but what I want is something more like
<TextBlock Text="{Binding ItemDetails.Find(i => i == **SelectedItem of CategoriesListView**).Value1}"/>
The data context to my page looks like.
public class MyDataContext
{
public List<string> Categories { get; set; }
public List<Item> Items { get; set; }
}
So I want to be able to select a category on the left, and then have the Items on the right ONLY display information from their child Details members where those Details match the category selected.
Not sure if understand your question exactly, but it seems like you want one listview's itemssource to be fed off of another listview's selection. Is that correct?
If so, this should do the trick (give your classes above):
<ListView x:Name="ItemsListView" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView x:Name="CategoriesListView" ItemsSource="{Binding ElementName=ItemsListView, Path=SelectedItem.ItemDetails}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel d:DataContext="{d:DesignInstance Type=local:Details, IsDesignTimeCreatable=True}">
<TextBlock Text="{Binding Category}"/>
<TextBlock Text="{Binding Value1}"/>
<TextBlock Text="{Binding Value2}"/>
<TextBlock Text="{Binding Value3}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
EDIT:
To filter one collection from another, I would use the ICollectionView, as demonstrated in the below vm. The key to making it work is setting 'IsSynchronizedWithCurrentItem' to true in the xaml.
Hopefully something similar to this will get you rolling.
Here's the updated vm:
internal class MainWindowViewModel
{
private ICollectionView _categories;
private ICollectionView _itemsWrapper;
List<Item> _items => new List<Item>()
{
new Item()
{
Name = "First", ItemDetails = new List<Details>()
{
new Details() {Category = "A", Value1 = 1, Value2 = 2, Value3 = 3},
new Details() {Category = "b", Value1 = 10, Value2 = 203, Value3 = 30},
new Details() {Category = "c", Value1 = 100, Value2 = 200, Value3 = 300},
}
},
new Item()
{
Name = "Second", ItemDetails = new List<Details>()
{
new Details() {Category = "d", Value1 = 1, Value2 = 2, Value3 = 3},
new Details() {Category = "e", Value1 = 10, Value2 = 203, Value3 = 30},
new Details() {Category = "f", Value1 = 100, Value2 = 200, Value3 = 300},
}
}
};
public ICollectionView Categories
{
get
{
if (_categories == null)
{
_categories = CollectionViewSource.GetDefaultView(new ObservableCollection<string>() {"A", "b", "c", "d", "e", "f"});
_categories.CurrentChanged += (s, e) => Items.Refresh();
}
return _categories;
}
}
public ICollectionView Items
{
get
{
if (_itemsWrapper == null)
{
_itemsWrapper = CollectionViewSource.GetDefaultView(_items);
_itemsWrapper.Filter = sel =>
{
var item = sel as Item;
var toMatch = Categories.CurrentItem.ToString();
return item.ItemDetails.Any(i => i.Category == toMatch);
};
}
return _itemsWrapper;
}
}
}
public class Item
{
public string Name { get; set; }
public List<Details> ItemDetails { get; set; }
}
public class Details
{
public string Category { get; set; }
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Value3 { get; set; }
}
...and the updated xaml:
<StackPanel Orientation="Horizontal">
<ListView ItemsSource="{Binding Categories}" IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView x:Name="ItemsListView" ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel d:DataContext="{d:DesignInstance Type=local:Item, IsDesignTimeCreatable=True}" Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<ListView x:Name="CategoriesListView" ItemsSource="{Binding ItemDetails}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel d:DataContext="{d:DesignInstance Type=local:Details, IsDesignTimeCreatable=True}">
<TextBlock Text="{Binding Category}"/>
<TextBlock Text="{Binding Value1}"/>
<TextBlock Text="{Binding Value2}"/>
<TextBlock Text="{Binding Value3}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>

Categories