I'm trying to learn something about the ListView and now I'm solving this problem:
I have a listview defined in the behindcode. I would like to change gridviewcolumn celltemplate dynamically. For example by the use of checkbox or button, or other. Is it even possible?
Definition of my ListView is here:
lvUsers.ItemsSource = LoadListViewData();
GridView gridview = new GridView();
lvUsers.View = gridview;
DataTemplate templateCheck = new DataTemplate();
FrameworkElementFactory factoryContentControlCheck = new FrameworkElementFactory(typeof(VsCheckBox));
factoryContentControlCheck.SetValue(VsCheckBox.MarginProperty, new Thickness(0, 0, 0, 0));
DataTemplate templateBorder = new DataTemplate();
FrameworkElementFactory factoryContentControlBorder = new FrameworkElementFactory(typeof(Border));
factoryContentControlBorder.SetValue(Border.MarginProperty, new Thickness(0, 0, 10, 0));
factoryContentControlBorder.SetValue(Border.WidthProperty, Width = 10);
factoryContentControlBorder.SetValue(Border.HeightProperty, Height = 10);
factoryContentControlBorder.SetValue(Border.BackgroundProperty, Brushes.Red);
DataTemplate templateAge = new DataTemplate();
FrameworkElementFactory factoryContentControlAge = new FrameworkElementFactory(typeof(ContentControl));
factoryContentControlName.SetValue(ContentControl.MarginProperty, new Thickness(0, 0, 10, 0));
factoryContentControlAge.SetValue(ContentControl.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlAge.SetValue(ContentControl.HorizontalAlignmentProperty, HorizontalAlignment.Right);
factoryContentControlAge.SetBinding(ContentControl.ContentProperty, new Binding("Age"));
DataTemplate templateStack = new DataTemplate();
FrameworkElementFactory factoryContentControlStack = new FrameworkElementFactory(typeof(StackPanel));
factoryContentControlStack.SetValue(StackPanel.MarginProperty, new Thickness(10, 0, 0, 0));
factoryContentControlStack.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
factoryContentControlStack.SetValue(StackPanel.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlStack.AppendChild(factoryContentControlCheck);
factoryContentControlStack.AppendChild(factoryContentControlBorder);
templateStack.VisualTree = factoryContentControlStack;
DataTemplate templateStack1 = new DataTemplate();
FrameworkElementFactory factoryContentControlStack1 = new FrameworkElementFactory(typeof(StackPanel));
factoryContentControlStack1.SetValue(StackPanel.MarginProperty, new Thickness(10, 0, 0, 0));
factoryContentControlStack1.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
factoryContentControlStack1.SetValue(StackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Right);
factoryContentControlStack1.SetValue(StackPanel.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlStack1.AppendChild(factoryContentControlAge);
templateStack1.VisualTree = factoryContentControlStack1;
GridViewColumn colStack = new GridViewColumn();
colStack.Header = "Stack";
colStack.CellTemplate = templateStack;
gridview.Columns.Add(colStack);
I would like to change CellTemplate of colStack to templateStack1 in runtime by the checking a checkbox or button click.
Thank you for any of your ideas.
You can make use of DataTrigger
in order to change dynamically the ContentTemplate
of your columns. Here is an example using XAML:
<Window x:Class="WpfApp.MainWindow"
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"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox x:Name="TemplateChanger" Content="Change template"
IsChecked="{Binding IsChecked}"/>
<DataGrid x:Name="DataGrid" Grid.Row="1" AutoGenerateColumns="False"
ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="My column" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Foo}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsChecked, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="fsdfsdf"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
And the codebehind file:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfApp.Annotations;
namespace WpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : INotifyPropertyChanged
{
private bool _isChecked;
public MainWindow()
{
InitializeComponent();
Items.Add(new Item {Foo = "Foo1"});
Items.Add(new Item {Foo = "Foo2"});
Items.Add(new Item {Foo = "Foo3"});
Items.Add(new Item {Foo = "Foo4"});
}
public bool IsChecked
{
get => _isChecked;
set
{
_isChecked = value;
OnPropertyChanged();
}
}
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
public string Foo { get; set; }
}
}
The result is that whenever you toggle the checkbox's IsChecked
property the content template of the cell for the given column changes too.
EDIT How to do it with code behind only
For completness sake here is how you can achieve this with code behind only:
var datagrid = new DataGrid {AutoGenerateColumns = false};
Grid.SetRow(datagrid,1);
RootGrid.Children.Add(datagrid);
var templateColumn = new DataGridTemplateColumn
{
Header = "My column",
IsReadOnly = true
};
var cellTemplate = new DataTemplate();
var factory = new FrameworkElementFactory(typeof(TextBlock));
factory.SetBinding(TextBlock.TextProperty, new Binding("Foo"));
var style = new Style(typeof(DataGridCell));
var trigger = new DataTrigger();
var triggerBinding = new Binding("DataContext.IsChecked")
{
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1)
};
trigger.Binding = triggerBinding;
trigger.Value = true;
var triggerSetter = new Setter {Property = ContentTemplateProperty};
var triggerTemplate = new DataTemplate();
var anotherFactory = new FrameworkElementFactory(typeof(TextBlock));
anotherFactory.SetValue(TextBlock.TextProperty,"lol");
triggerTemplate.VisualTree = anotherFactory;
triggerSetter.Value = triggerTemplate;
trigger.Setters.Add(triggerSetter);
style.Triggers.Add(trigger);
templateColumn.CellStyle = style;
cellTemplate.VisualTree = factory;
templateColumn.CellTemplate = cellTemplate;
datagrid.Columns.Add(templateColumn);
datagrid.ItemsSource = Items;
IMHO this way looks a little bit messy, but the decision is yours =)