WPF Datgrid binding text with tooltip to to different properties - c#

I want to show the body of an email in a tooltip and a shortened version in the column
<DataGridTextColumn Binding="{Binding Body}"
Header="{x:Static t:Resource.GridBody}" Width="100">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="ToolTip"
Value="{Binding Text, RelativeSource= {RelativeSource Self}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
I get the ellipsis on each character, I have a property that has the first three lines and I would like to bind that to the column and the body to the ToolTip

Instead of using style I used a DataGridTemplateColumn instead of a DataGridTextColumn
<DataGridTemplateColumn Header="{x:Static t:Resource.GridBody}" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShortBody}" TextTrimming="CharacterEllipsis" ToolTip="{Binding Body}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Related

Add Custom Rows at the top of DataGrid - WPF

I am new in WPF and I have a project that needs to have this output below
How can I do this? I just know a basic DataGrid code like this below:
<DataGrid AutoGenerateColumns="False" Name="myGrid" Margin="10">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}, Path=Item.Header}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTextColumn Header="Case Title" Binding="{Binding Path=Name}" Width="160" />
<DataGridComboBoxColumn Width="100" x:Name="dtgCbxColUnit" SelectedValueBinding="{Binding Unit, Mode=TwoWay}" DisplayMemberPath="{Binding Unit}" />
<DataGridTextColumn Header="Case 1" Binding="{Binding Path=Case1}" Width="80"/>
<DataGridTextColumn Header="Case 2" Binding="{Binding Path=Case2}" Width="80" />
<DataGridTextColumn Header="Case 3" Binding="{Binding Path=Case3}" Width="80" />
<DataGridTextColumn Header="Case 4" Binding="{Binding Path=Case4}" Width="80" />
<DataGridTextColumn Header="Case 5" Binding="{Binding Path=Case5}" Width="80" />
<DataGridTextColumn Header="Case 6" Binding="{Binding Path=Case6}" Width="80" />
</DataGrid.Columns>
</DataGrid>
How can I resolve this?
The DataGrid cell templating is done vertically (by columns) not horizontally (by rows). It means you can't make an exception for a few rows. If you want to have custom rows you have to apply it to all rows using DataGridTemplateColumn
<DataGrid.Columns>
<DataGridTemplateColumn Header="People">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<!-- normal template -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="{Binding A}" Background="Green"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RowType}" Value="0">
<Setter Property="Template">
<Setter.Value>
<!-- first extra template -->
<ControlTemplate>
<TextBlock Text="{Binding A}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RowType}" Value="1">
<Setter Property="Template">
<Setter.Value>
<!-- second extra template -->
<ControlTemplate>
<CheckBox Content="{Binding A}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
On the other hand DataGrid "styling" is done the opposite way using RowStyle. So you can write a trigger to customize one row completely with the cost of ignoring all cells (or columns) in that row:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding RowType}" Value="0">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button Content="{Binding A}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
combined results:
Normally I would suggest to use your own implementation of the general ItemsControl if you think these customizations will grow in the future.
There is another workaround where you create a gap at the top of the grid and overlay stuff onto it using a Canvas or any control with ClipToBounds=False explained here

WPF MVVM DataGrid contents don't fit to row height change

I need to make my row height variable so that I can allow for some rows to add additional information. Setting the RowHeight value doesn't seem to make any difference. There is no value to set for height at the level of DataGridTextColumn as all the contents are bound (MVVM).
<Border Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right" Margin="9" Width="auto" Visibility="{Binding LogVisibility}" VerticalAlignment="Stretch">
<DataGrid AutoGenerateColumns="False" VerticalContentAlignment="Center" ItemsSource="{Binding EventLog}" RowHeight="100" Background="White" CellStyle="{StaticResource cellStyle}" ColumnHeaderStyle="{StaticResource headerStyle}" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Type" SortMemberPath="CategoryDescription">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" MaxHeight="15" MaxWidth="15" VerticalAlignment="Center"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding CategoryDescription}" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Type" Binding="{Binding CategoryDescription}"></DataGridTextColumn>-->
<DataGridTextColumn Header="Date" Binding="{Binding Date}"/>
<DataGridTextColumn Header="Details" Binding="{Binding TypeDescription}" MaxWidth="400"/>
</DataGrid.Columns>
</DataGrid>
</Border>
Having set the value of RowHeight="{x:Static sys:Double.NaN}" doesn't change anything and instead I see truncated text as seen here:
If I set an arbitrary fixed height of RowHeight="100" (although not ideal), the rows content doesnt expand either, and show an ugly outline:
I added vertical scrolling, but I don't need horizontal scrolling, so I was hoping to have variable height so that longer text would wrap and fit, how can I achieve this?
Update (solution) - Thanks to Nomad developer
There was an offender style at the top of my XAML that applied to all the cells and was restricting them from expanding:
<Style TargetType="DataGridCell" x:Key="cellStyle" >
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" Margin="5,5,5,5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="TextBlock.VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Height" Value="35"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
I have removed this style now, and the final Datagrid is (using <DataGridTextColumn.ElementStyle>):
<DataGrid AutoGenerateColumns="False"
VerticalContentAlignment="Center"
ItemsSource="{Binding EventLog}"
MinRowHeight="30"
Background="White"
ColumnHeaderStyle="{StaticResource headerStyle}"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Type" SortMemberPath="CategoryDescription">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" MaxHeight="15" MaxWidth="15" VerticalAlignment="Center" Margin="5,0,5,0"/>
<TextBlock Text="{Binding CategoryDescription}" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,5,0"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Date" Binding="{Binding Date}">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap"/>
<Setter Property="TextBlock.TextAlignment" Value="Justify" />
<Setter Property="TextBlock.VerticalAlignment" Value="Center"/>
<Setter Property="TextBlock.Margin" Value="5"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Details" Binding="{Binding TypeDescription}" MaxWidth="400">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap"/>
<Setter Property="TextBlock.TextAlignment" Value="Justify" />
<Setter Property="TextBlock.VerticalAlignment" Value="Center"/>
<Setter Property="TextBlock.Margin" Value="5"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Which helped me achieve this:
Yes that's possible if you add custom style with textblock wrapping for your DataGridTextColumn
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
All you need is add TextWrapping as Wrap or WrapWithOverflow You can check out the difference here.
Also to make it work, you need to remove your RowHeight or you can change it from
RowHeight="100"
to
MinRowHeight="100"
It ensures that your rows height will at least be 100, and if text doesnt fit - can go longer in size for that specific row, but with original rowheight it cant change size and has fixed 100 height for all rows. Btw, 100 seems to be too high, may be 20-ish would be neat.
Give a try to this code, I also added TextAlignment to Justify just in case you might find it useful.
<Border Grid.Row="1"
Grid.Column="1"
Width="auto"
Margin="9"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Visibility="{Binding LogVisibility}">
<DataGrid VerticalContentAlignment="Center"
AutoGenerateColumns="False"
Background="White"
CanUserAddRows="False"
CellStyle="{StaticResource cellStyle}"
ColumnHeaderStyle="{StaticResource headerStyle}"
ItemsSource="{Binding EventLog}"
MinRowHeight="20">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Type"
SortMemberPath="CategoryDescription">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image MaxWidth="15"
MaxHeight="15"
VerticalAlignment="Center"
Source="{Binding Image}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding CategoryDescription}"
TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Type" Binding="{Binding CategoryDescription}"></DataGridTextColumn>-->
<DataGridTextColumn Binding="{Binding Date}"
Header="Date" />
<DataGridTextColumn MaxWidth="400"
Binding="{Binding TypeDescription}"
Header="Details">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
<Setter Property="TextBlock.TextAlignment" Value="Justify" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Border>

why Cell Header and Cell contents are not aligned in datagrid?

I have this xaml code:
<DataGridTextColumn Header="number" Binding="{Binding Path=Number, Mode=OneWay}" Width="70" CanUserResize="False" >
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="FrameworkElement.HorizontalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Status" Binding="{Binding Path=ProcessingState}" Width="100">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="FrameworkElement.HorizontalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
but when it shows on screen, it generate this view:
As can be seen, the cells are not aligned with header. How can I make them aligned?
If you just want to centre the text in the cells, then you could use a DataGridTemplateColumn like this:
<DataGrid ItemsSource="{Binding SalesPeriods}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="number" Width="70" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Number, Mode=OneWay}"
HorizontalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Status" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ProcessingState, Mode=OneWay}"
HorizontalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
If you prefer to use a DataGridTextColumn, then you could still achieve this goal using the ElementStyle property:
<DataGridTextColumn Header="number" Binding="{Binding Path=Year, Mode=OneWay}"
Width="70" CanUserResize="False" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

Binding and Styling a DataGridColumnHeader

I have a DataGridColumn like this:
<DataGridTextColumn
Binding="{Binding
Path=Name,
UpdateSourceTrigger=PropertyChanged}"
HeaderStyle="{StaticResource HeaderWrapped}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock
Text="{Binding
Path=DataContext.Name,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
The HeaderWrapped style is in a ResourceDictionary that is imported in the control. It looks like this:
<Style x:Key="HeaderWrapped" TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
When I run the app, the header Text is bound properly, but the TextWrapping is not set. I'm guessing that the DataGridTextColumn.HeaderTemplate overwrites the template from my ResourceDictionary. Is there a way that I can keep the styling for the headers in a ResourceDictionary but still bind the Text property of the header?
What you're trying to do is basically setting a Style for the Header at first, and then tell it to forget about it and use a brand new template for the header. you can't set both DataGridTextColumn.HeaderStyle.ContentTemplate and DataGridTextColumn.HeaderTemplate
However I can think of one workarounds to this problem:
<DataGridTextColumn
Binding="{Binding
Path=Name,
UpdateSourceTrigger=PropertyChanged}"
Tag="{Binding
Path=DataContext.Name,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
HeaderStyle="{StaticResource HeaderWrapped}">
</DataGridTextColumn>
<Style x:Key="HeaderWrapped" TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Tag}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

How to use targetname with usercontrol and trigger

I made UserControl of DataGrid. I placed this new component into page1.xaml. I would like to use some template and setting based on value in Data1.
Could you help me with this code how to avoid the error message?
<my:MyDataGrid Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" Margin="29,295,0,0" Name="myDataGrid1"
VerticalAlignment="Top" Height="151" Width="176" SelectionChanged="myDataGrid1_SelectionChanged">
<my:MyDataGrid.Columns>
<DataGridTemplateColumn Header="Col1" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Data1}" x:Name="mytext" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data1}" Value="1">
<Setter TargetName="mytext" Property="Foreground" Value="Red" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</my:MyDataGrid.Columns>
</my:MyDataGrid>
I got error message:
Cannot set Name attribute value 'mytext' on element 'TextBlock'.
'TextBlock' is under the scope of element 'MyDataGrid', which already
had a name registered when it was defined in another scope.
You can add you data trigger to a style attached to a TextBlock
<my:MyDataGrid Grid.Column="1" Grid.Row="1" ...>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Col1" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Data1}" x:Name="mytext">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Data1}" Value="1">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</my:MyDataGrid>

Categories