WPF Binding sub class to Listview - wpf

I have this class:
public class Car
{
public struct CarType
{
public string Manufacturer { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
public CarType Type;
public string LicenseNumber { get; set; }
public int Km { get; set; }
}
To bind LicenseNumber or Km to my listview, I use:
<ListView.View>
<GridView>
<GridViewColumn Width="140" DisplayMemberBinding="{Binding LicenseNumber}">
<GridViewColumn.Header>
<GridViewColumnHeader Click="GridViewColumnHeader_Click">License Number</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
The command DisplayMemberBinding="{Binding LicenseNumber}" binds to LicenseNumber property.
But how to bind sub-class. like Type.Manufacturer? Type.Model?

You can bind to properties on instances as well (just use the dot notation that you are already familiar with):
<TextBlock Text="{Binding Path=Type.Manufacturer}" />

You probably never tried.
<GridViewColumn Width="140" DisplayMemberBinding="{Binding Type.Manufacturer}">

You should using following code:
<ListView.View>
<GridView>
<GridViewColumn Width="140" DisplayMemberBinding="{Binding Type.Manufacturer}">
...

Related

WPF ListView: binding a nested property to GridViewColumn.DisplayMemberBinding throws exception "BindingExpression path error"

Within a ListView I have a GridViewColumn which I am trying to bind a nested property to its DisplayMemberBinding attribute:
<ListView x:Name="myListView"
ItemsSource="{Binding myObservableCollection}"
IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn Header="Timestamp"
DisplayMemberBinding="{Binding Path=Timestamp}"/>
<GridViewColumn Header="Product Id"
DisplayMemberBinding="{Binding Path=Product.Id}"/>
<GridViewColumn Header="Product Name"
DisplayMemberBinding="{Binding Path=Product.Name}"/>
<GridViewColumn Header="Product Description"
DisplayMemberBinding="{Binding Path=Product.Description}"/>
</GridView>
</ListView.View>
</ListView>
In the view model I populate the observable collection. The observable collection is defined as below:
public ObservableCollection<myCustomData> myObservableCollection { get; } = new ObservableCollection<myCustomData>();
And the classes are:
internal Class myProduct
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public Class myCustomData
{
internal myProduct Product { set; get; } = new myProduct();
public long Timestamp { get; set; } = DateTime.UtcNow.ToFileTimeUtc();
}
In run time I get below exception, for all items bound Product.*, for example for Product.Name it says:
BindingExpression path error: 'Product' property not found on 'object'
''myCustomData' (HashCode=1858451)'.
BindingExpression:Path=Product.Name; DataItem='myCustomData'
(HashCode=1858451); target element is 'TextBlock' (Name=''); target
property is 'Text' (type 'String')

WPF combine icon and text in Listview with columns

I'm coding simple file explorer in WPF and I have Listview with 5 columns (Name, Extension, Size, Date, Attributes)
XAML looks this:
<ListView Name="ListView" BorderThickness="2,0,2,0" BorderBrush="Gray" Height="Auto" Width="Auto" >
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="450"/>
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}" Width="70"/>
<GridViewColumn Header="Size" DisplayMemberBinding="{Binding Size}" Width="80"/>
<GridViewColumn Header="Date" DisplayMemberBinding="{Binding Date}" Width="80"/>
<GridViewColumn Header="Attributes" DisplayMemberBinding="{Binding Attributes}" Width="60"/>
</GridView>
</ListView.View>
Then, I have class FileInfo with five properties, each binded to column via DisplayMemberBinding
public class FileInfo
{
public string Name { get; set; }
public string Extension { get; set; }
public string Size { get; set; }
public string Date { get; set; }
public string Attributes { get; set; }
}
var files = new List<FileInfo> { new FileInfo { Name = "Foo", Extension = ".sub", Date = "1.1.2015", Size = "3.05", Attributes = "H" } } //just as test
ListView.ItemsSource = files;
That works fine.
Now, in column Name I want to combine file name with its associated icon. And I don't know, how to do that. I don't want to create another column for icon, icon must be included in Name column.
Any ideas? Is there a way to do this programmatically, or do I need to modify XAML?
You need to create CellTemplate for your GridViewColumn like this
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="450">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="<Your Image Source>"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Binding more properties into GridViewColumnHeader

I would like to make GridView with column header, for example like exampe below (example for one column). My DataTemplate for header contains have more controls (TextBlock and TextBox) and I am not able to figure out how to set binding to access individual controls from template (header). In my example, instead of "aaa" to set value of HColumn1Line2 property.
<ListView Name="PeopleList" ItemsSource="{Binding People}">
<ListView.Resources>
<DataTemplate x:Key="MyHeaderTemplate">
<StackPanel>
<TextBlock Text="{Binding}"/>
<TextBox Text="aaa"></TextBox>
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderTemplate="{StaticResource MyHeaderTemplate}">
<GridViewColumn Header="{Binding HColumn1Line1}" DisplayMemberBinding="{Binding PeopleListItem}"/>
</GridView>
</ListView.View>
</ListView>
//Code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PeopleList.DataContext = this;
_people.Add(new Person() { PeopleListItem = "some information" });
HColumn1Line1 = "Header line #1";
HColumn1Line2 = "text instead of aaa";
}
public List<Person> People { get { return _people; } }
List<Person> _people = new List<Person>();
public string HColumn1Line1 { get; set; }
public string HColumn1Line2 { get; set; }
}
public class Person
{
public string PeopleListItem { get; set; }
}
The main problem you have is you are using the MainWindow instance as DataContext for the ListView which is in the MainWindow Content this will run you into exceptions such as "Logical tree depth exceeded while traversing the tree.", so you better make your DataContext a seperate object such as the following:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PeopleList.DataContext = new ViewModel();
}
}
public class ViewModel
{
public List<Person> People { get { return _people; } }
List<Person> _people = new List<Person>();
public string HColumn1Line1 { get; set; } = "Header line #1";
public string HColumn1Line2 { get; set; } = "text instead of aaa";
public ViewModel()
{
_people.Add(new Person() { PeopleListItem = "some information" });
}
}
public class Person
{
public string PeopleListItem { get; set; }
}
Updated XAML:
<ListView Name="PeopleList" ItemsSource="{Binding People}">
<ListView.Resources>
<DataTemplate x:Key="MyHeaderTemplate">
<StackPanel>
<TextBlock Text="{Binding HColumn1Line1}"/>
<TextBox Text="{Binding HColumn1Line2}"></TextBox>
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderTemplate="{StaticResource MyHeaderTemplate}">
<GridViewColumn Header="{Binding}" DisplayMemberBinding="{Binding PeopleListItem}"/>
</GridView>
</ListView.View>
</ListView>

Change ListViewItem text color

I am currently creating ListViewItems programatically like so:
ListView.Items.Add(New With {Key .Name = ItemName, Key .DateCreated = ItemDateCreated, Key .Description = ItemDescription})
I have found no way of changing the color of the item, as I have not found a way of creating a ListViewItem with sub items in any other way than this one.
Is it possible to change the color of the item's text (or background if not possible), while still being able to have subitems?
EDIT - This is my XAML:
<ListView x:Name="ListView" Opacity="0.75">
<ListView.View>
<GridView x:Name="ListViewGridView">
<GridViewColumn Header="Name" Width="155" DisplayMemberBinding="{Binding Path=Name}"/>
<GridViewColumn Header="Date Created" Width="150" DisplayMemberBinding="{Binding Path=DateCreated}"/>
<GridViewColumn Header="Description" Width="250" DisplayMemberBinding="{Binding Path=Description}"/>
</GridView>
</ListView.View>
</ListView>
The following sample will set the first column's text to green. On a side note, you should probably wrap your "Name", "Date Created" and "Description" fields into an actual object and use it to create your ListViewItem with subitems instead of trying to create a new weakly typed object each time (I did this and attached the code if you are interested, obviously you can use any data type for members (not just string) but I was coding quickly).
Information on DataTemplates:
http://msdn.microsoft.com/en-us/library/system.windows.datatemplate(v=vs.110).aspx
XAML:
<ListView x:Name="ListView" Opacity="0.75">
<ListView.View>
<GridView x:Name="ListViewGridView">
<GridViewColumn Header="Name" Width="155">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" Foreground="{Binding Color}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Date Created" Width="150" DisplayMemberBinding="{Binding Path=DateCreated}"/>
<GridViewColumn Header="Description" Width="250" DisplayMemberBinding="{Binding Path=Description}"/>
</GridView>
</ListView.View>
</ListView>
C#:
// In method somewhere, won't work otherwise
ListView.Items.Add(new DataClass("Bob", "12/04/2013", "Person", Color.Green));
public class DataClass
{
public String Name { get; set; }
public String DateCreated { get; set; }
public String Description { get; set; }
public SolidBrushColor { get; set; }
public DataClass (string Name, String Date, String Desc, SolidBrushColor textColor)
{
this.Name = Name;
this.DateCreated = Date;
Description = Desc;
Color = textColor;
}
}

bind List<string> to GridViewColumn

I have a GridView like following.
<Grid>
<ListView Margin="8,44,8,50" TabIndex="57" Name="CustomFieldList">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Semester Name" Width="120" DisplayMemberBinding="{Binding FieldName}"/>
<GridViewColumn Header="Subjects" Width="150" DisplayMemberBinding="{Binding TypeValidations}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
My class is;
public class DigreePrograme
{
public string semester{ get; set; }
public List<string> subjects { get; set; }
}
I have an observer collection and I have bound that to list.
static ObservableCollection<DigreePrograme> digreeList = new ObservableCollection<DigreePrograme>();
public ObservableCollection<DigreePrograme> DigreeList
{
get { return digreeList }
set { digreeList = value; OnPropertyChanged("DigreeList"); }
}
on the FormFoad
CustomFieldList.ItemsSource=DigreeList;
Issue
semester property displays perfectly. But Subject List not. It displys as Collection. How to fix this?
You could add a ItemsControl in a DataTemplate in your GridViewColumn to show all the Subject items
xaml:
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="275" Width="275" x:Name="UI">
<Grid>
<ListView Name="CustomFieldList" ItemsSource="{Binding ElementName=UI, Path=DigreeList}" >
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Semester Name" Width="120" DisplayMemberBinding="{Binding Semester}"/>
<GridViewColumn Header="Subjects" Width="150" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Subjects}" Width="150" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DigreeList.Add(new DigreePrograme { Semester = "Semester 1", Subjects = new List<string>(new string[] {"Math","Art","Engish" }) });
}
private ObservableCollection<DigreePrograme> digreeList = new ObservableCollection<DigreePrograme>();
public ObservableCollection<DigreePrograme> DigreeList
{
get { return digreeList; }
set { digreeList = value;}
}
}
public class DigreePrograme
{
public string Semester { get; set; }
public List<string> Subjects { get; set; }
}
result:
I would suggest you create a new string property on your view model that would just concatenate your collection and return that. It would be the simplest way to go about this..
public class DigreePrograme
{
public string semester { get; set; }
public List<string> subjects { get; set; }
public string SubjectsDisplay
{
get
{
string returnVal = string.Empty;
foreach (string subject in subjects)
{
returnVal += subject + ", ";
}
return returnVal.TrimEnd(',', ' ');
}
}
}
something like that may help.
obviously change your binding also :)
cheers.
ste.

Resources