WPF Dynamically Setting CellTemplate and Binding - wpf

I have a GridView control and I am adding GridViewColumns. I was using DisplayMemberBinding property of GridViewColumn but now I want to use the CellTemplate. I am binding to a dictionary.
The following code worked with DisplayMemberBinding:
var column = new GridViewColumn
{
Header = current.Key,
DisplayMemberBinding = new Binding("[" + current.Key + ]")
};
Now, I need to do the same with CellTemplate but for some reason I am not sure why it is not displaying the items.
var column = new GridViewColumn
{
Header = current.Key,
CellTemplate = (DataTemplate)FindResource("GridViewTextBlockDataTemplate"),
};
And here is the DataTemplate defined in Window.Resources:
<DataTemplate x:Key="GridViewTextBlockDataTemplate" x:Name="GridViewTextBlockDataTemplate">
<TextBlock Text="{Binding Path=[Key]}"></TextBlock>
</DataTemplate>
Thanks,
Azam

Your solution is correct except that to get the value in the textblock you just need to have Keyword Binding and no need to provide path as you are binding the datatemplate, it automatically assigns the value.
If that does not work try to use datatemplate selector for the datagrid and For a detailed explanation on datatemplate selector please have a look a these link:
WPF DataTemplate Selector Tutorial

Related

WPF GridViewColumn.CellTemplate DataTemplate ContentPresenter

I have a ListView which uses DataTemplates. If i use this in a ListView defined the columns over XAML it works how it schould. My DataTemplates are used in my view. But if i want to use the same DataTemplates in a second ListView, where i add new columns to the ListView it does not use my DataTemplate. What should i Do?
The code in XAML for the first ListView looks like this:
<GridViewColumn x:Name="lvSecondColumn" Header="Value" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
My Code i use for generating a column in second ListView is:
DataColumn dc = (DataColumn)colum;
GridViewColumn column = new GridViewColumn( );
column.DisplayMemberBinding = new Binding( dc.ColumnName ) );
column.Header = dc.ColumnName;
TestColumns.Columns.Add( column );
TestListView.ItemsSource = dt.DefaultView;
In WPFInspector i see there is no ContentPresenter in my dynamic Generated Column.
Picture from missing ContentPresenter from WPFInspector
How to add the ContentPresenter to my dynamic column???
You can't set both Binding and DataTemplate. According to the docs
https://msdn.microsoft.com/en-us/library/system.windows.controls.gridviewcolumn.displaymemberbinding(v=vs.110).aspx
The following properties are all used to define the content and style
of a column cell, and are listed here in their order of precedence,
from highest to lowest:
- DisplayMemberBinding
- CellTemplate
- CellTemplateSelector
If you use binding then it will generate a textbox with a ".ToString()" of the bound object. If you are aware of the structure of your items in the ListView you can just make DataTemplates with appropriate bindings in it. However when generating columns dynamically this is an issue.
You can dynamically generate a datatemplate for your column and integrate the binding in it:
public DataTemplate CreateColumnTemplate(string property)
{
StringReader stringReader = new StringReader(
#"<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
<ContentPresenter Content=""{Binding " + property + #"}""/>
</DataTemplate>");
XmlReader xmlReader = XmlReader.Create(stringReader);
return XamlReader.Load(xmlReader) as DataTemplate;
}
Then you can generate your columns like this:
GridViewColumn column = new GridViewColumn( );
column.CellTemplate = CreateColumnTemplate(dc.ColumnName);
column.Header = dc.ColumnName;
TestColumns.Columns.Add( column );
I didn't run the code there may be little mistakes.

Performance issues with CellTemplate of GridViewColumn

I am using a ListView with it's View set to GridView to display items in dynamically generated columns. The ListView's virtualization is enabled. For this example I want to display up to 10 rows with 50 columns (but the count of columns is dynamic).
The first thing I do is to create the columns in code behind in a loop.
"Items" is just an ObservableCollection of strings in my view model for one row.
int index = 0;
foreach(Header header in Headers)
{
GridViewColumn column = new GridViewColumn();
column.Header = // the view model of the header;
column.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
column.DisplayMemberBinding = new Binding("Items" + "[" + index + "]");
m_MyGridView.Columns.Add(column);
index++;
}
Now, when I change the ItemsSource of my ListView, the data gets updated quiet fast.
Unfortunately, each string in every cell has to be formatted (font color, foreground etc). So I cannot use DisplayMemberBinding, but CellTemplate to assign a DataTemplate to every cell.
For this purpose I create a DataTemplate in xaml in my control that holds the ListView. This contains just a TextBlock with some bindings to properties of the cell's view model to define the string style of the TextBlock.
Now, Items is not just a collection of string but a collection of CellViewModels that not only contain the value to display but also information of how to display it. This information is then bound inside the DataTemplate.
The CellTemplate is set up in code and assigned to each GridViewColumn's CellTemplate:
foreach(Header header in Headers)
{
GridViewColumn column = new GridViewColumn();
column.Header = // the view model of the header;
column.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
column.CellTemplate = createCellTemplate(index);
m_MyGridView.Columns.Add(column);
index++;
}
DataTemplate getCellTemplate(int index)
{
FrameworkElementFactory elementFactory = new FrameworkElementFactory (typeof (ContentControl));
elementFactory.SetBinding(ContentControl.ContentProperty, new Binding("Items" + "[" + index + "]"));
DataTemplate dt = Resources["ValueTemplate"] as DataTemplate;
elementFactory.SetValue(ContentControl.ContentTemplateProperty, dt);
DataTemplate dataTemplate = new DataTemplate ();
dataTemplate.VisualTree = elementFactory;
return dataTemplate;
}
When I do this, things get really slow when I'm changing the ItemsSource of the ListView. It now takes nearly a second to render the new cells each time I change the ItemsSource to display different data.
So is there a way to speed things up? I thought about having the formatted string already in my CellViewModel by using a Paragraph, but unfortunately DisplayMemberBinding only supports string, so I have to use the CellTemplate.
Edit: so, here is the listview i'm using:
<ListView ItemsSource="{Binding ElementName=Control, Path=ItemViewModels}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" VirtualizingStackPanel.IsVirtualizing="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.View>
<GridView x:Name="m_MyGridView"/>
</ListView.View>
</ListView>
and the DataTemplate for the cells:
<DataTemplate x:Key="ValueTemplate">
<Border Background="{Binding Path=BackgroundColor">
<TextBlock Text="{Binding Path=Value}"
Foreground="{Binding Path=ForegroundColor}"
FontStyle="{Binding Path=FontStyle}"
FontWeight="{Binding Path=FontWeight}"
</TextBlock>
</Border>
</DataTemplate>
The function getCellTemplate is called only once when the headers are created. When the ItemsSource changes, it is not called, because they have been created already.

Setting ListView ItemSource binding in code

I have a working ListView like this
<ListView Name="passageListView" ItemsSource="{Binding Path=Passages, NotifyOnTargetUpdated=True}" TargetUpdated="PassageListViewTargetUpdated">
where Passages is an ObservableCollection<>.
Now, how I do the same binding in code? (note that NotifyOnTargetUpdated=True has to be set)
I have tried to assign a Binding to passageListView.ItemsSource but this is not allowed and I can't use SetBinding() since passageListView.ItemsSourceis not a DependencyProperty?
Any ideas?
try this in the constructor of the control where the ListView is:
passageListView.SetBinding(ListView.ItemsSourceProperty,
new Binding
{
Path = new PropertyPath("Passages"),
NotifyOnTargetUpdated = true
});
If the DataContext is set properly, this should work.
The DependencyProperty could also be ItemsControl.ItemsSourceProperty, because that is the base class.

Help with Binding tabcontrol that contains a treeview

In a silverlight project i have a class:
class Foo{
List<Bar> Bars;
string BarName;
}
In my view model, I have:
List<Foo> Foos;
My TabControl is bound to Foos, and I'm using a Converter to convert my Foo class to a TabItem, with Header = BarName, and Content = Bars
My TabItem's content is just a TreeView, and I'd like to bind the TreeView's ItemSource to Bars
However I'm stuck trying to figure this out.
TabControl's ContentTemplate should be DataTemplate with TreeView and
<DataTemplate x:Key="ContentTemplate">
<sdk:TreeView ItemsSource={Binding}/>
</DataTemplate>
Update:
In code you can use template above:
yourTabItem.ContentTemplate = (DataTemplate)Application.Resources["ContentTemplate"];
Or without template:
yourTreeView.SetBinding(TreeView.ItemsSourceProperty, new Binding("Bars") { Source = yourSource });

What is the code behind for datagridtemplatecolumn, and how to use it?

I have a DataGrid in WPF. And I am trying to add Buttons to certain cells of the grid, after it is bound to a particular ItemsSource. I have tried to do this in the xaml like this:
<dg:DataGridTemplateColumn x:Name="R1" CanUserReorder="False" IsReadOnly="False">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<awc:ImageButton Content="Edit" Name="btnEdit" Visibility="Collapsed"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
However, I want to know as to how I can do this in the code behind. I need this so that I can place Buttons whenever a particular click even takes place. Any help will be highly appreciated.
use this:
DataGridTemplateColumn col1 = new DataGridTemplateColumn();
col1.Header = "MyHeader";
FrameworkElementFactory factory1 = new FrameworkElementFactory(typeof(CheckBox));
Binding b1 = new Binding("IsSelected");
b1.Mode = BindingMode.TwoWay;
factory1.SetValue(CheckBox.IsCheckedProperty, b1);
factory1.AddHandler(CheckBox.CheckedEvent, new RoutedEventHandler(chkSelect_Checked));
DataTemplate cellTemplate1 = new DataTemplate();
cellTemplate1.VisualTree = factory1;
col1.CellTemplate = cellTemplate1;
dgTransportReqsts.DataGrid.Columns.Add(col1);
I used this to add CheckBox in my DataGridTemplateColumn at runtime.
Hope this helps!!
Anurag's answer will work very well for you if you want to add the buttons before the grid is instantiated, specifically before you add the column to the grid.
If you want to add the button to the grid cell after the grid is already built, you can do it by making changes to the DataGridCell object. First you have to find it:
Find the DataGridCell by using DataGridColumn.GetCellContent
Use VisualTreeHelper to scan up the visual tree to the DataGridCell
Once this is done, there are several ways to add a button to the DataGridCell, depending on what you're trying to achieve:
Set DataGridCell.Template to a ControlTemplate containing the buttons and other styling you desire, -OR-
Set DataGridCell.ContentTemplate to a DataTemplate containing the buttons and other items you desire, -OR-
Have your column's DataTemplate include a placeholder panel to hold new buttons, search down the visual tree for this panel by Name, and add your button to it.
An alternative approach that doesn't require finding the cell is to:
Include an ObservableCollection<T> property in your view model that supplies the information to create the buttons
In your DataTemplate include an ItemsControl that reference this property and has a DataTemplate that can create the correct button out of type T
When you want to add a button, just add an item to the ObservableCollection property

Resources