I currently have a DataGrid
that is bound to a DataTable
via ItemSource
. I want to be able to apply a CellTemplate ( MyTemplate ) to all columns in the DataGrid
with a certain type ( MyType ).
Since the DataTable
has a dynamic number of columns, I cannot disable AutoGenerateColumns
and manually define DataGridTemplateColumns
in the WPF.
Here is my DataTable
in WPF:
<DataGrid HeadersVisibility="Column" VerticalScrollBarVisibility="Visible" CanUserAddRows="False"
IsSynchronizedWithCurrentItem="False" FontSize="12" BorderThickness="0,1,0,0" RenderTransformOrigin="0.5,0.5"
ItemsSource="{Binding MyDataTable}" AutoGenerateColumns="True" AutoGeneratingColumn="GeneratingColumnEvent"/>
The DataTemplate
I want to assign is defined in the UserControl's
Resource Dictionary (and works when used in a explicitly defined DataGridTemplateColumn
).
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<SharedResourceDictionary Source="{Resources Directory}/MyTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
</ResourceDictionary>
</UserControl.Resources>
My AutoGenerateColumn event is defined like this:
private void GeneratingColumnEvent(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType == typeof (MyType))
{
var newCol = new DataGridCandidateTemplateColumn
{
CellTemplate = (DataTemplate) FindResource("MyTemplate"),
ColumnName = e.PropertyName,
};
e.Column = newCol;
e.Column.Header = e.PropertyName;
}
}
With the custom DataGridCandidateTemplateColumn
class defined like this:
class DataGridCandidateTemplateColumn : DataGridTemplateColumn
{
public string ColumnName { get; set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
// The DataGridTemplateColumn uses ContentPresenter with your DataTemplate.
ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem);
// Reset the Binding to the specific column. The default binding is to the DataRowView.
if (cp != null)
BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName));
return cp;
}
}
If I don't try to apply the template, the column uses the toString representation of MyType . If I do apply the template like above, nothing appears in the column's cells. What am I missing?
It's a bit unclear what exactly you are trying to achieve by using that TemplateColumn. In case it's intended just for formatting, then you can add ResourceDictionary file and reference it in your Windows XAML, something like in the following example:
<ResourceDictionary 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">
....
<Setter Property="Margin" Value="0,0,0,3" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="{StaticResource somevalue}" />
<Setter Property="GridLinesVisibility" Value="All"/>
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource Somevalue}" />
<Setter Property="VerticalGridLinesBrush" Value="{StaticResource SomeValue}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="{StaticResource ColumnHeaderBackgroundColor}"/>
<Setter Property="Foreground" Value="{StaticResource ColumnHeaderForegroundColor}"/>
<Setter Property="BorderBrush" Value="{StaticResource ColorBorderColumnHeader}" />
<Setter Property="BorderThickness" Value="1,0,0,0"/>
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="Padding" Value="6,6,10,6"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>
<Style TargetType="DataGridRowHeader">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Width" Value="0"/>
</Style>
<Style TargetType="DataGridRow">
<Setter Property="Foreground" Value="{StaticResource GridFontColor}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="DataGridRow.IsSelected" Value="True">
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="4,4,2,4"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Padding="{TemplateBinding Padding}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ContentPresenter SnapsToDevicePixels="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
Otherwise, use that GeneratingColumnEvent
and set the Columns
' individual properties, like width
, String
format, etc.
private void GeneratingColumnEvent(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
try
{
//Modify Columns names in Header, and apply proper String Format if needed
// Collapse the ID column
if (e.Column.Header.ToString() == "ID")
{ e.Column.Visibility = Visibility.Collapsed; }
// Item
else if (e.Column.Header.ToString() == "Item")
{ e.Column.Width = new DataGridLength(4.5, DataGridLengthUnitType.Star); }
// Price
if (e.Column.Header.ToString() == "Price")
{
e.Column.Header = "Px";
(e.Column as DataGridTextColumn).Binding.StringFormat = "C2";
e.Column.Width = new DataGridLength(1.7, DataGridLengthUnitType.Star);
}
}
catch { }
}
Answering my own question. I apologize since my solution will not work for everyone.
The reason why I had to work with a dynamic number of columns was because the number of columns was 3 + 5n
where n was number of categories of data contained in my datatable.
I was able to leverage the data (more below) in a way to make it possible for me to disable AutoGenerateColumns
and define the columns as part of another event (using a loop to generate the 5n
categories). By being able to define each column, I was able to assign my templates without relying on DataGridAutoGeneratingColumnEventArgs
to determine the right template.
Instead of binding my datagrid to a datatable where I pre-processed my data to have 3 + 5n
columns, I instead wrapped the 5n
portion of the data with a custom class. I also wrapped each row in a class and made the datagrid use a List of these classes as its ItemSource
.
This allowed me to create the columns as part of the initialization process of the datagrid. When it came to defining the columns for the 5n
part of the datagrid, I used a converter like this:
Column Definition:
private void GenerateColumnEvent(object sender, EventArgs e)
{
var table = sender as DataGrid;
var context = table.DataContext
//Regular column definitions
foreach(var category in context.CategoryIDList)
{
var binding = new Binding("CategoryWrapper")
{
Converter = new FindCategoryProperty(),
ConverterParameter = new Tuple<categoryID, string>(category, "Property1")
};
var column = new DataGridTextColumn
{
Binding = binding,
//Other column defining things (templates, headers, etc)
};
table.Columns.Add(column);
//More column definitions
}
}
Converter Definition:
class FindCategoryProperty: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && parameter != null)
{
var categoryList = (IList<CategoryClass>) value;
var categoryStringTuple = (Tuple<categoryID, string>) parameter;
var categoryID = categoryStringTuple.Item1;
var child = categoryList.FirstOrDefault(c => c.CategoryID == categoryID);
if (child != null)
{
switch (categoryStringTuple.Item2)
{
case "Property1":
return child.getProperty1();
case "Property2":
return child.getProperty2();
//etc
default:
return "";
}
}
}
return "";
}
//Left the ConvertBack unimplemented
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.