Binding more properties into GridViewColumnHeader - wpf

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>

Related

Can't get Dictionary<> (Or ObservableCollection<KVP>) to bind to ListView

I am trying to get a Dictionary to bind to a ListView. Having not worked, I changed the Datatype to ObservableCollection> but still no joy. I know I'm missing something silly but....
The data is readonly, meaning that the UI will not update it, only the code behind.
The XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<ListView Grid.Column="1" Background="Orange" ItemsSource="{Binding MyItems}">
<ListView.View>
<GridView>
<GridViewColumn Header="Item" DisplayMemberBinding="{Binding Key}"/>
<GridViewColumn Header="Quantity" DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
The DataObject:
public ObservableCollection<KeyValuePair<string, int>> MyItems{ get; set; }
And the assignment:
this.MyItems = new ObservableCollection<KeyValuePair<string, int>>(
PIData.GetNeededItems(itemName));
You should assign the MyItems property before InitializeComponent is called.
public MainWindow()
{
MyItems = new ObservableCollection<KeyValuePair<string, int>>(
PIData.GetNeededItems(itemName));
InitializeComponent();
}
If that is not possible, implement INotifyPropertyChanged:
public partial class MainWindow : Window, INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<KeyValuePair<string, int>> myItems;
public ObservableCollection<KeyValuePair<string, int>> MyItems
{
get { return myItems; }
set
{
myItems = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(MyItems)));
}
}
}

WPF DataGrid ContentControll bindng

I want to make DataGrid with a structure as in image below.
To have two TextBoxes in each cell.
Ive made Class
public class ComplexTable : ViewModelBase
{
public ComplexTable()
{
FirstProperty = new FirstClass();
SecondProperty = new Second();
}
public class FirstClass
{
public FirstClass()
{
First = "FirstString";
Second = "SecondString";
}
public string First { get; set; }
public string Second { get; set; }
}
public class Second
{
public Second()
{
Third = "ThirdString";
Fourth = "FourthString";
}
public string Third { get; set; }
public string Fourth { get; set; }
}
public FirstClass FirstProperty { get; set; }
public Second SecondProperty { get; set; }
}
public ObservableCollection<ComplexTable> _testCollection = new ObservableCollection<ComplexTable>();
private ObservableCollection<ComplexTable> TestCollection
{
get { return _testCollection; }
set
{
_testCollection = value;
RaisePropertyChanged("TestCollection");
}
}
And a TestCollection that should be a ItemsSource for DataGrid.
My DataGrid
<DataGrid CanUserAddRows="True"
ItemsSource="{Binding TestCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="First Column">
<DataGridTemplateColumn.CellEditingTemplate >
<DataTemplate >
<ContentControl>
<StackPanel>
<TextBox Text=" "/>
<TextBox Text=" "/>
</StackPanel>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Second Column">
<DataGridTemplateColumn.CellEditingTemplate >
<DataTemplate >
<ContentControl>
<StackPanel>
<TextBox Text=" "/>
<TextBox Text=" "/>
</StackPanel>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I can't figure out how to bind those textboxes. Or i went in a wrong direction?
The datagrid sets the DataContext of the columns to the elements of the ItemSource. Every element in that ItemSource will be displayed as one Row. In your case the ItemSource is TestCollection. Therefore the DataContext inside your DataGridTemplateColumn is set to the elements of the TestCollection. If TestCollection contains ComplexTable elements. You can bind directly to the properties on ComplexTable.
var TestCollection = new ObservableCollection<DataForOneRow> {DataForFirstRow, DataForSecondRow, DataForThirdRow};
public class DataForOneRow {
public string DataForFirstColumnFirstTextBox {get; set;} //left out raise of PropertyChanged for brevity
public string DataForFirstColumnSecondTextBox {get; set;}
public string DataForSecondColumnFirstTextBox {get; set;}
public string DataForSecondColumnSecondTextBox {get; set;}
}
<DataGridTemplateColumn Header="First Column">
<DataGridTemplateColumn.CellEditingTemplate >
<DataTemplate >
<ContentControl>
<StackPanel>
<TextBox Text="{Bidning DataForFirstColumnFirstTextBox}"/>
<TextBox Text="{Bidning DataForFirstColumnSecondTextBox}"/>
</StackPanel>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
FYI: You have to raise a PropertyChanged event in all ViewModel properties.

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.

DataTemplate disappears when moving items in ObservableCollection

I have a CellTemplate for a column in a ListView. The CellTemplate contains a ComboBox which has an ItemTemplate. Both ItemsSource and SelectedItem is bound to another ViewModel.
The ListView is bound to an ObservableCollection on a ViewModel. Above the ListView there is a toolbar with the buttons to move the selected item up and down. I buttons a bound to and ICommand which will make a Move on the ObservableCollection.
The view is updated fine, but the selected item in the ComboBox is not using the DataTemplate and is just showing the type name.
I found out that everything is working fine if IsEditable = false, but I need this to be true.
I have created a small project that verifies the problem. Perhaps this is an issue in WPF.
Here is XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication3="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type WpfApplication3:Item}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<DataTemplate x:Key="cellTemplate">
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" Width="100" IsEditable="true" TextSearch.TextPath="Name"/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ToolBar>
<Button Content="Add" Command="{Binding AddItemCommand}"/>
<Button Content="Up" Command="{Binding MoveItemUpCommand}" CommandParameter="{Binding ElementName=listView, Path=SelectedItem}"/>
<Button Content="Down" Command="{Binding MoveItemDownCommand}" CommandParameter="{Binding ElementName=listView, Path=SelectedItem}"/>
</ToolBar>
<ListView x:Name="listView" ItemsSource="{Binding Collection}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" CellTemplate="{StaticResource cellTemplate}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ICommand AddItemCommand { get; private set; }
public ICommand MoveItemUpCommand { get; private set; }
public ICommand MoveItemDownCommand { get; private set; }
public ObservableCollection<Row> Collection { get; set; }
public ViewModel()
{
Collection = new ObservableCollection<Row>();
AddItemCommand = new RelayCommand(AddItem);
MoveItemUpCommand = new RelayCommand<Row>(MoveItemUp, CanMoveItemUp);
MoveItemDownCommand = new RelayCommand<Row>(MoveItemDown, CanMoveItemDown);
}
private bool CanMoveItemDown(Row arg)
{
if (arg == null)
return false;
return Collection.Last() != arg;
}
private void MoveItemDown(Row obj)
{
var index = Collection.IndexOf(obj);
Collection.Move(index, index + 1);
}
private bool CanMoveItemUp(Row arg)
{
if (arg == null)
return false;
return Collection.First() != arg;
}
private void MoveItemUp(Row row)
{
var index = Collection.IndexOf(row);
Collection.Move(index, index - 1);
}
private void AddItem()
{
Collection.Add(new Row());
}
}
public class Row
{
public Row()
{
Items = new List<Item> { new Item { Name = "Test1" }, new Item { Name = "Test2" } };
}
public List<Item> Items { get; set; }
public Item SelectedItem { get; set; }
}
public class Item
{
public string Name { get; set; }
public int Order { get; set; }
}

Silverlight - binding a listbox within a listbox template

In silverlight, I'm trying to build a listbox of reports and I'm trying to show the parameters of the report in a listbox within the datatemplate of the outer reports listbox.
Here are the data classes:
public class Report
{
public string Title { get; set; }
public string Description { get; set; }
public List<ReportParameter> Parameters = new List<ReportParameter>();
}
public class ReportParameter
{
public string Name { get; set; }
public string ParameterType { get; set; }
public bool Required { get; set; }
}
Here's the XAML I'm trying to use to do it:
<ListBox x:Name="lstReports">
<ListBox.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<ListBox ItemsSource="{Binding Parameters}" Height="60" Width="60">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The binding for the Title of the report works, but the inner listbox is empty for each of the reports.
The Parameters list is populated.
What am I doing wrong?
Thanks!
Your "Parameters" list is a public field not a property, silverlight can only bind to properties and not fields. Try changing your class to this:
public class Report
{
public Report()
{
Parameters = new List<ReportParameter>();
}
public string Title { get; set; }
public string Description { get; set; }
public List<ReportParameter> Parameters { get; set; }
}
This should work the way you want it.

Resources