Reputation: 1891
I am generating a DataGrid
dynamically and adding it to a StackPanel
on my WPF application.
As the is dynamically generated, there is no mark up on XAML side for the same and I need to manage the binding and all properties programatically.
I want my DataGrid
to have the values in the cell wrapped to the next line if the text is lengthy. I understand that I need to replace the DataGridCell
with TextBlock
and set the TextWrap
property on it. All the examples that I have found suggest something on those lines itself. However, I couldn't find a way to do it completely from code behind, without XAML.
So far, I have tried to the following code, but it doesn't work.
DataGrid dg = new DataGrid();
dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView;
DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;
dg.ItemTemplate = ct;
dg.ColumnWidth = 300;
Can you please point me to the right direction here?
[Update]: Solution
On further researching I was able to get a solution to my issue. For Auto generated columns, we need to capture the AutoGeneratingColumn
event and replace the default DataGridTextColumn
by a DataGridTemplateColumn
which would have a TextBlock
in it. And we can then set the `TextWrappingProperty' to get the text wrapped.
Following is the updated code:
DataGrid dg = new DataGrid();
dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView;
DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;
dg.AutoGeneratingColumn += new EventHandler<DataGridAutoGeneratingColumnEventArgs>(dg_AutoGeneratingColumn);
dg.MaxColumnWidth = 300;
and then the Code under the Event Handler:
private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
//cancel the auto generated column
e.Cancel = true;
//Get the existing column
DataGridTextColumn dgTextC = (DataGridTextColumn)e.Column;
//Create a new template column
DataGridTemplateColumn dgtc = new DataGridTemplateColumn();
DataTemplate dataTemplate = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
dataTemplate.VisualTree = tb;
dgtc.Header = dgTextC.Header;
dgtc.CellTemplate = dataTemplate;
tb.SetBinding(TextBlock.TextProperty, dgTextC.Binding);
//add column back to data grid
DataGrid dg = sender as DataGrid;
dg.Columns.Add(dgtc);
}
Upvotes: 2
Views: 3728
Reputation: 106
An alternate approach is to use a behaviour like this.
public class DataGridWrapTextBehaviour : Behavior<DataGrid>
{
private DataGrid DataGrid
{
get { return AssociatedObject as DataGrid; }
}
private Style ElementStyle { get; set; }
private Style EditingElementStyle { get; set; }
protected override void OnAttached()
{
base.OnAttached();
this.ElementStyle = new Style( typeof( TextBlock ) );
this.ElementStyle.Setters.Add( new Setter( TextBlock.TextWrappingProperty, TextWrapping.Wrap ) );
this.EditingElementStyle = new Style( typeof( TextBox ) );
this.EditingElementStyle.Setters.Add( new Setter( TextBox.TextWrappingProperty, TextWrapping.Wrap ) );
this.DataGrid.Columns.CollectionChanged += Columns_CollectionChanged;
}
protected override void OnDetaching()
{
this.DataGrid.Columns.CollectionChanged -= Columns_CollectionChanged;
base.OnDetaching();
}
private void Columns_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
{
foreach ( var column in this.DataGrid.Columns.OfType<DataGridTextColumn>() )
{
column.ElementStyle = this.ElementStyle;
column.EditingElementStyle = this.EditingElementStyle;
}
}
}
You can then drap and drop the behaviour onto the DataGrid in Expression Blend.
Upvotes: 4
Reputation: 19885
First thing I must say is that you are using the datagrid in a wrong manner.
dg.ItemTemplate = ct;
is a wrong code!
WPF DataGrid
does not entertain ItemTemplate
property as other ItemsControls
do. You would have to supply column templates in DataGrid
for it to work correctly.
When DataGridBoundColumn
\ DataGridTextColumn
columns are supplied to the DataGrid
, you could set their ElementStyle
property like this...
<toolkit:DataGrid
AutoGenerateColumns="False">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn
Binding="{Binding SomeProperty}">
<toolkit:DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping"
Value="Wrap"/>
</Style>
</toolkit:DataGridTextColumn.ElementStyle>
</toolkit:DataGridTextColumn>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
... where toolkit
is namespace of the WPF toolkit in version .Net 3.5 or prior. In .Net 4.0, its part of the standard System.Windows.Controls
namespace.
But that exact same solution is a little complicated in your case is because your grid has AutoGenerateColumns
as true by default that generates the DataGrid.Columns
automatically. So you have no scope to set this ElementStyle
property.
So we have to take the XML and code behind approach ...
XAML:
<toolkit:DataGrid x:Name="dg">
<toolkit:DataGrid.Resources>
<Style TargetType="{x:Type TextBlock}"
x:Key="WrappedTextBlockStyle">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</toolkit:DataGrid.Resources>
</toolkit:DataGrid>
Code Behind:
dg.ItemsSource = ((DataSet)data).Table[0].DefaultView;
dg.Columns.CollectionChanged
+= new NotifyCollectionChangedEventHandler(
Columns_CollectionChanged);
void Columns_CollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems.Count > 0)
{
foreach(DataGridColumn col in e.NewItems)
{
if (col is DataGridTextColumn)
{
((DataGridTextColumn) col).ElementStyle
= dg.Resources["WrappedTextBlockStyle"] as Style;
}
}
}
}
Hope this helps...
Note: A word of caution that wrapping textblocks make the performance of WPF dataGrid slower.
Upvotes: 0