Binding a list with another list with WPF - c#

Straight to the point. I'm writing a program using MVVM and I have made a view like this:
Class structure:
class Company
{
int CompanyID
string Name
List<Material> MaterialList
}
class Material
{
int ID
string Name
string Description
}
Here is the XAML code of my View (deleted most of the irrelevant stuff to make it more readable):
<ListView x:Name="_companies" ItemsSource="{Binding ElementName=_this, Path=ItemsSource}" SelectedItem="{Binding ElementName=_this, Path=SelectedItem}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding CompanyID}" />
<GridViewColumn Header="Company Name" DisplayMemberBinding="{Binding CompanyName}" />
</GridView>
...
<ListView x:Name="_materials" >
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ElementName=_companies, Path=SelectedItem.MaterialID}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding ElementName=_companies, Path=SelectedItem.Name"/>
</GridView>
</ListView.View>
</ListView>
<TextBox x:name="_description" Text="{Binding Description}" IsReadOnly="True" />
</StackPanel>
And a part of my MainView:
<Window.DataContext>
<viewModels:CompanyListViewModel />
</Window.DataContext>
<Grid>
<view:CompanyListView ItemsSource="{Binding Companies}" />
Companies is a list of Company objects containing Name, ID and MaterialList. The Companies list is displayed in the _companies ListView (code above).
Now after selecting a company from the list I want to display the assigned MaterialList in the _materials ListView.
After selecting a material in _materials I want to display its description in the _description TextBox
How do I do that? I found a similar thread that explains the concept but I still can't do it in my case. Can I bind to one of the SelectedItem properties ?

Refer the below code. I have done it using MVVM. You need use SelectedItem property of the listview and bind to the chid data listview.
<StackPanel>
<ListView x:Name="_companies" ItemsSource="{Binding Companies}" >
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn Header="Company Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="_materials" ItemsSource="{Binding ElementName=_companies,Path=SelectedItem.MaterialList}" >
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
</GridView>
</ListView.View>
</ListView>
<TextBox x:Name="_description" Text="{Binding ElementName=_materials, Path=SelectedItem.Description}" IsReadOnly="True" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
class MainViewModel
{
private ObservableCollection<Company> companies;
public ObservableCollection<Company> Companies
{
get { return companies; }
set { companies = value; }
}
public MainViewModel()
{
companies = new ObservableCollection<Company>();
for (int i = 0; i < 10; i++)
{
Company comp = new Company();
comp.ID = i + 1;
comp.Name = "Comp" + i;
ObservableCollection<Material> matlist = new ObservableCollection<Material>();
for (int j = 0; j < 10; j++)
{
Material mat = new Material();
mat.ID = j + 1;
mat.Name = "Mat" + j + i;
mat.Description = "descrp" + j + i;
matlist.Add(mat);
}
comp.MaterialList = matlist;
companies.Add(comp);
}
}
}
class Company
{
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ObservableCollection<Material> materialList;
public ObservableCollection<Material> MaterialList
{
get { return materialList; }
set { materialList = value; }
}
}
class Material
{
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
}

Related

In WPF, displaying only empty fields

In WPF, I have a ListView which is bounded to a Dictionary (InpLangList) and a CheckBox bounded with a boolean(IsShowEmptyFields) property.
Eg. private Dictionary<string, string> _langList = new Dictionary<string, string>();
<ListView ItemsSource="{Binding InpLangList, Mode=TwoWay}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Id" Width="Auto" DisplayMemberBinding="{Binding Key}" />
<GridViewColumn Header="Values" Width="Auto" DisplayMemberBinding="{Binding Value}" />
</GridView>
</ListView.View>
</ListView>
public Dictionary<string, string> InpLangList
{
get { return _langList ; }
set
{
_langList = value;
NotifyPropertyChanged();
}
}
(...)
InpLangList.Add("id1","One");
InpLangList.Add("id2","");
InpLangList.Add("id3","");
InpLangList.Add("id4","Four");
InpLangList.Add("id5","Five");
(...)
private bool _isShowEmptyFields;
public bool IsShowEmptyFields
{
get { return _isShowEmptyFields; }
set
{
_isShowEmptyFields = value;
NotifyPropertyChanged();
}
}
What I need is, if Checkbox is checked, then I want to display only the empty fields,
i.e.
InpLangList.Add("id2","");
InpLangList.Add("id3","");
should be displayed in the ListView
else entire InpLangList should be displayed in the ListView.
Use CollectionView:
ViewModel:
// Actual data source
Dictionary<string, string> inpLangList = new Dictionary<string, string>();
// A presentation of your data that you can group, sort, filter, etc
public ICollectionView InpLangList { get; set; }
private bool _isShowEmptyFields;
public bool IsShowEmptyFields
{
get { return _isShowEmptyFields; }
set
{
_isShowEmptyFields = value;
// if the presentation of your data is assigned - filter it
InpLangList?.Refresh();
}
}
// ViewModel constructor
public VM()
{
inpLangList.Add("id1", "One");
inpLangList.Add("id2", "");
inpLangList.Add("id3", "");
inpLangList.Add("id4", "Four");
inpLangList.Add("id5", "Five");
// note what's going next:
// assigning the data source to it's presentation (view)
InpLangList = CollectionViewSource.GetDefaultView(inpLangList);
// assigning filter that will be applied to your data source
// before the showing it within the UI
InpLangList.Filter = (obj) =>
{
if (!(obj is KeyValuePair<string, string> pair))
return false;
return !IsShowEmptyFields || string.IsNullOrEmpty(pair.Value);
};
}
View:
<CheckBox IsChecked="{Binding IsShowEmptyFields}" Content="Empty only"/>
<ListView ItemsSource="{Binding InpLangList}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Id" Width="Auto" DisplayMemberBinding="{Binding Key}" />
<GridViewColumn Header="Values" Width="Auto" DisplayMemberBinding="{Binding Value}" />
</GridView>
</ListView.View>
</ListView>

WPF ListView doesn't work

I have been experiencing problems with WPF ListView (using elements binding), as I tried to initialize it by injecting ItemsSource the list of Disks, and got no visual feedback from the element. I wrote my code using the example supplied here.
Here are the relevant code parts:
Setting the ListView
private void viewDisk_Click(object sender, RoutedEventArgs e)
{
List<DiskDetails> data = new List<DiskDetails>();
foreach(Disk disk in disks)
data.Add(new DiskDetails(disk.GetVolumeHeader().DiskName, disk.GetVolumeHeader().DiskOwner,disk.GetVolumeHeader().ProdDate));
disksList.ItemsSource = data;
}
DiskDetails Class
public class DiskDetails
{
public string diskName { get; set; }
public string diskOwner { get; set; }
public string cDate { get; set; }
public DiskDetails(string dN, string dO,string cD)
{
diskName = dN;
diskOwner = dO;
cDate = cD;
}
}
WPF ListView
<Grid Grid.Column="0">
<ListView x:Name="disksList" VerticalAlignment="Top" Height="250" SelectionChanged="disksList_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="Disk Name" Width="108" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Disk Owner" Width="108" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Creation Date" Width="108" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
<Button x:Name="viewDisk" Content="View Disk" Width="90" Height="40" VerticalAlignment="Bottom" Margin="0,0,0,15" Click="viewDisk_Click"/>
</Grid>
Thanks.
Looks like your DisplayMemberBinding wasn't changed from the example code. Try changing the bindings to match the properties of DiskDetails. E.g. DisplayMemberBinding="{Binding Name}" should change to DisplayMemberBinding="{Binding diskName}"

wpf Bind Listbox in Listview column subclass Object

i have a properties 'AllShobeh'
public struct listViewShobehItem
{
string _code;
string _name;
string _tell;
string _address;
List<string> _atms;
public string Code
{
get
{
return _code;
}
set
{
_code = value;
}
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public string Tell
{
get
{
return _tell;
}
set
{
_tell = value;
}
}
public string Address
{
get
{
return _address;
}
set
{
_address = value;
}
}
public List<string> Atms
{
get
{
return _atms;
}
set
{
_atms = value;
}
}
}
ObservableCollection<listViewShobehItem> _AllShobeh = new ObservableCollection<listViewShobehItem>();
public ObservableCollection<listViewShobehItem> AllShobeh
{
get { return _AllShobeh; }
set
{
_AllShobeh = new ObservableCollection<listViewShobehItem>(value);
OnPropertyChanged("AllShobeh");
}
}
My Code in constructor :
AllShobeh = new ObservableCollection<listViewShobehItem>(from a in STATICS.db.shobehs
select new listViewShobehItem() {Code = a.code,Name=a.name,Tell=a.tell,Address=a.address,Atms=(from at in STATICS.db.atms
where at.shobehCode==a.code
select at.code + " - " + at.name).ToList()});
listviewShobeh.DataContext = this;
this.DataContext = this;
listviewShobeh.ItemsSource = AllShobeh;
i want show AllShobeh in a listview and show Atms in last column of listView in Listbox ...
this is my Xaml Code :
<ListView Name="listviewShobeh" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="5" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn x:Name="colCode" Header="code" DisplayMemberBinding="{Binding Path=Code}"/>
<GridViewColumn Header="name" DisplayMemberBinding="{Binding Path=Name}"/>
<GridViewColumn Header="tell" DisplayMemberBinding="{Binding Path=Tell}"/>
<GridViewColumn Header="address" DisplayMemberBinding="{Binding Path=Address}"/>
<GridViewColumn Header="atms" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Expander>
<ListBox ItemsSource="{Binding ElementName=listviewShobeh,Path=Atms}" DisplayMemberPath="{Binding Path=.}"/>
</Expander>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
i cant bind Allshobeh.Atms to listBox Atms column ...
My Question In Pic
Delete ElementName and DisplayMemberPath from your binding.
<DataTemplate>
<Expander>
<ListBox ItemsSource="{Binding Path=Atms}" />
</Expander>
</DataTemplate>
Also, more importantly, you're confused about DataContext and ViewModel class, let me know if you need specific help about this point.

Binding a text file information to DataGrid using Caliburn.Micro

I am making an AddressBook program in WPF and I want to bind my DataGridColumn from a text file having contacts information.
But, I don't know how to do it
Here's my ViewModel(ContactsViewModel.cs):
using Caliburn.Micro;
using System.Windows;
namespace AddressBook {
public class ContactsViewModel : Screen {
//What to do here ?
}
}
Here's the XAML Code(ContactsView.xaml):
<UserControl x:Class="AddressBook.ContactsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataGrid FontSize="16" CanUserResizeColumns="False" SelectedIndex="1">
<DataGrid.Columns>
<DataGridTextColumn FontSize="14" Header="Name" Width="150"/>
<DataGridTextColumn FontSize="14" Header="Mobile No." Width="170"/>
<DataGridTextColumn FontSize="14" Header="Email" Width="200"/>
<DataGridTextColumn FontSize="14" Header="Address" Width="240"/>
</DataGrid.Columns>
</DataGrid>
</UserControl>
And Here's my text file(Contacts.txt):
Humayun 032121352643 nexgenking#gmail.com A-312 korangi karachi,
Jahangir 03212515332 mawahid02#gmailcom A-584 korangi karachi
pakistan,
You need a property of collection in the ViewModel and Give the same name to the control name property. Refer the below code.
<DataGrid FontSize="16" x:Name="List" AutoGenerateColumns="False" CanUserResizeColumns="False" SelectedIndex="1">
<DataGrid.Columns>
<DataGridTextColumn FontSize="14" Header="Name" Width="150" Binding="{Binding Name}"/>
<DataGridTextColumn FontSize="14" Header="Mobile No." Width="170" Binding="{Binding MobileNo}"/>
<DataGridTextColumn FontSize="14" Header="Email" Width="200" Binding="{Binding Email}"/>
<DataGridTextColumn FontSize="14" Header="Address" Width="240" Binding="{Binding Address}"/>
</DataGrid.Columns>
</DataGrid>
public class ContactsViewModel : Screen
{
public ContactsViewModel()
{
string str = File.ReadAllText("Contacts.txt");
string[] strContracts = str.Split(',');
foreach (var item in strContracts)
{
string[] fields = item.Trim().Split(' ');
_list.Add(new Contact() { Name = fields[0],MobileNo= fields[1],Email=fields[2],Address=fields[3] });
}
}
private IObservableCollection<Contact> _list = new BindableCollection<Contact>();
public IObservableCollection<Contact> List
{
get { return _list; }
set
{
_list = value;
NotifyOfPropertyChange(() => List);
}
}
}
public class Contact
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string address;
public string Address
{
get { return address; }
set { address = value; }
}
private string email;
public string Email
{
get { return email; }
set { email = value; }
}
private string mobileNo;
public string MobileNo
{
get { return mobileNo; }
set { mobileNo = value; }
}
}
In this example List is the Property name of the collection as well as the name of the Datagrid. In the Constructor of the viewmodel has the parsing logic of the file data and make a collection object out of it

Adding items to a listview row with columns

So I am trying to add a row of information to my listview but when I do it displays it weirdly. Like so:
I am using an for each loop like so:
foreach (Client c in clients)
{
ListViewItem i = new ListViewItem();
i.Content = new String[] { c.info.cid.ToString(), c.info.pc.ToString(),c.info.ip.ToString(), c.info.status.ToString() };
list.Items.Add(i);
}
My Client class is using a struct to store the info
public struct Info
{
public int cid;
public string pc;
public string ip;
public string status;
}
I am also adding values to it:
info = new Info();
info.ip = "192.168.1.100";
info.pc = "Duncan";
info.status = "idle";
info.cid = 1;
Why is it displaying it weirdly? Could anyone help?
My ListView XAML:
<ListView Height="247" HorizontalAlignment="Left" Margin="4,6,0,0" Name="list" VerticalAlignment="Top" Width="319" Background="#FF454545" ItemsSource="{Binding}" SelectionMode="Multiple" Grid.Column="0">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="ID" Width="30" />
<GridViewColumn Header="Computer" Width="100" />
<GridViewColumn Header="IP" Width="100" />
<GridViewColumn Header="Status" Width="100" />
</GridView>
</ListView.View>
</ListView>
There are some wrong things in this code. If you want to push data in a ListView using bindings, you have to have a valid ViewModel with properties to bind on. You have to define the bindings on you GridViewColumns.
Moreover, WPF doesnt know how to bind on fields, so you will need .NET properties for each data you want to display. Here is a very raw example for your case, it's not a realistic scenario but should help you get started :
Window.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ListView Height="247" HorizontalAlignment="Left" Margin="4,6,0,0" Name="list" VerticalAlignment="Top" Width="319" Background="#FF454545" ItemsSource="{Binding Clients}" SelectionMode="Multiple" Grid.Column="0">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="ID" Width="30" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="Computer" Width="100" DisplayMemberBinding="{Binding Computer}" />
<GridViewColumn Header="IP" Width="100" DisplayMemberBinding="{Binding Ip}" />
<GridViewColumn Header="Status" Width="100" DisplayMemberBinding="{Binding Status}" />
</GridView>
</ListView.View>
</ListView>
</Window>
MainWindow.xaml.cs
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<DummyClient> Clients
{
get
{
for (int i = 0; i < 10; i++)
{
var info = new Info();
info.ip = "192.168.1.100";
info.pc = "Duncan";
info.status = "idle";
info.cid = 1;
yield return new DummyClient(info);
}
}
}
}
public class DummyClient
{
public DummyClient(Info info)
{
Info = info;
}
public string Ip { get { return Info.ip; } }
public string Computer { get { return Info.pc; } }
public string Status { get { return Info.status; } }
public int Id { get { return Info.cid; } }
public Info Info
{
get;
private set;
}
}
public struct Info
{
public int cid;
public string pc;
public string ip;
public string status;
}
Once again it's not really the way it should be done but this is a start. For exampe, DummyClient should implement INotifyPropertyChanged if you want two ways bindings to works.

Categories