Create DataGrid column separator - wpf

Is there a way to create a visual separator between two particular columns in a DataGrid? It doesn't need to be fancy, maybe just a double line or a thicker border.

In case it's something like this
You can achieve using a custom style:
<Window.Resources>
<Style x:Key="DataGridColumnSeparatorStyle" TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Rectangle VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="Gray"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Start"/>
<DataGridTextColumn Header="End"/>
<!-- Separator column -->
<DataGridTemplateColumn MinWidth="0" Width="2" CellStyle="{StaticResource DataGridColumnSeparatorStyle}"/>
<DataGridTextColumn Header="Start"/>
<DataGridTextColumn Header="End"/>
</DataGrid.Columns>
</DataGrid>
If you generate the columns in code-behind, either by autogenerate columns or other, you can still create the separator column by getting the resource from the XAML:
DataGridTextColumn s1 = new DataGridTextColumn() { Header = "Start" };
DataGridTextColumn s2 = new DataGridTextColumn() { Header = "Start" };
DataGridTextColumn e1 = new DataGridTextColumn() { Header = "End" };
DataGridTextColumn e2 = new DataGridTextColumn() { Header = "End" };
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.MinWidth = 0;
column.Width = 2;
var separatorStyle = (Style)FindResource("DataGridColumnSeparatorStyle");
column.CellStyle = separatorStyle;
dataGrid.Columns.Add(s1);
dataGrid.Columns.Add(e1);
dataGrid.Columns.Add(column);
dataGrid.Columns.Add(s2);
dataGrid.Columns.Add(e2);

I think you have two options. The simplest option will probably be to use a Style and set your DataGrid.CellStyle to the defined style.
<Style x:Key="DataGridBorder" TargetType="DataGridCell">
<Setter Property="BorderBrush" Value="LightGray" />
<Setter Property="BorderThickness" Value="1,1,1,1" />
</Style>
...
<DataGrid CellStyle="{StaticResource DataGridBorder}">
...
The other option is to use a CellTemplate in conjunction with the DataGridTemplateColumn.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="1,1,1,1" Margin="-6,-6,-6,-6">
<Grid Margin="6,6,6,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Whatever}" Grid.Column="0" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I haven't tested either of these, and you may have to play with the margins a little bit.

Try this. When navigating through cells using keyboard Tab key, it won't focus the seperator column:
<DataGridTemplateColumn MinWidth="2" MaxWidth="2" IsReadOnly="True" CanUserResize="False">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Gray" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="2" />
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Focusable" Value="False" />
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
Source

Related

wpf DataGrid change wrapping on cells

I have a WPF DataGrid with AutoGenerate Columns. I have been able to override the column headers using code and also force wrapping on the column headers when I shrink the columns. When I try to force text wrapping on the cells my binding breaks... it shows the same value in every column.
Here is the XAML I am using to format
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
Again, the ColumnHeaderStyle works fine, but the CellStyle is not working.
Suggestions?
Update:
Column headers are set as follows:
if (e.Column.Header.ToString() == "Product_Description")
e.Column.Header = "Product";
if (e.Column.Header.ToString() == "Original_Gross_Weight")
e.Column.Header = "Orig. Net Wt.";
The wrapping of the headers works well. Just the wrapping of the content does not work.
On the binding it seems that once one replaces the DataGridCell data style, the full object for for the row is placed into the content presenter instead of the current property of the column.
It appears you override AutoGeneratingColumn so why not simply turn of auto generate and define the columns by hand?
Here is a working version where the text is wrapped for the data:
<Window.Resources>
<model:People x:Key="People">
<model:Person First="Joe" Last="Smith" Phone="303-555 5555" />
<model:Person First="Mary" Last="Johnson" Phone="720-555 5555" />
<model:Person First="Frank" Last="Wright" Phone="202-555 5555" />
</model:People>
</Window.Resources>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{StaticResource People}">
<DataGrid.Columns>
<DataGridTextColumn Header="First" Binding="{Binding First}" />
<DataGridTextColumn Header="The Name" Binding="{Binding Last}" />
<DataGridTextColumn Header="Phone Number" Binding="{Binding Phone}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping"
Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Result

How To Find ScrollViewer Inside of WPF ContentControl?

WPF 4.5 / C#
I've got an app where I have several WPF Windows each utilizing this custom content control. I use it in the XAML like this:
<ContentControl Name="myControl" Style="{StaticResource ReservedSpaceScrollBar}"
In the code behind, I need to be able to access the ScrollViewer inside, so I can call .ScrollToTop()
I've tried this, but it doesn't work:
((ScrollViewer)this.myControl.FindName("Scroll")).ScrollToTop();
...but .FindName doesn't find the ScrollViewer. What am I doing wrong? How make this work?
The XAML for the Style is below...
<Style TargetType="{x:Type ContentControl}" x:Key="ReservedSpaceScrollBar">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ScrollViewer PanningMode="Both" VerticalScrollBarVisibility="Auto" x:Name="Scroll" FocusVisualStyle="{x:Null}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter />
<Border Width="{x:Static SystemParameters.VerticalScrollBarWidth}" x:Name="Placeholder" Grid.Column="1" />
</Grid>
</ScrollViewer>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ComputedVerticalScrollBarVisibility, ElementName=Scroll}" Value="Visible">
<Setter TargetName="Placeholder" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Look into the VisualTreeHelper
Using that class you can look into children of elements; for example:
var childCount = VisualTreeHelper.GetChildrenCount(this.myControl);
for (int i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(this.myControl, i);
if (child.GetValue(NameProperty).ToString() == "Scroll")
{
((ScrollViewer)child).ScrollToTop();
}
}

How to add templates for datagrid columns?

I'm adding dynamically an itemsource to the datagrid:
datagrid.ItemsSource = _table.DefaultView;
foreach (DataColumn column in _table.Columns)
{
if (column.DataType == typeof(bool))
{
var dgrcl = new DataGridCheckBoxColumn
{
IsThreeState = false,
Header = column.Caption,
Binding = new Binding(column.ColumnName),
Width = new DataGridLength(15, DataGridLengthUnitType.Star)
};
datagrid.Columns.Add(dgrcl);
}
else
{
var dgrcl = new DataGridTextColumn();
dgrcl.Binding = new Binding(column.ColumnName);
dgrcl.Header = column.Caption;
datagrid.Columns.Add(dgrcl);
}
}
But if I add a new row to Datagrid (with checkboxcolumn) - the checkbox is threestate.
I tried to add next code in xaml:
<Style TargetType="CheckBox" x:Key="dgrChkBoxStyle">
<Setter Property="IsThreeState" Value="False"></Setter>
<Setter Property="IsChecked" Value="True"></Setter>
</Style>
<DataGrid Name="datagrid" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" AlternatingRowBackground="Honeydew" AlternationCount="2" AutoGenerateColumns="False">
<DataGridCheckBoxColumn>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox" BasedOn="{StaticResource dgrChkBoxStyle}"></Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
</DataGrid>
But there is exception that "Items collection must be empty before using ItemsSource".
I am new in WPF,please any advise, how to make checkboxes with only two states?
you should write the column definition like so :
<DataGrid Name="datagrid" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" AlternatingRowBackground="Honeydew" AlternationCount="2" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn>
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox" BasedOn="{StaticResource dgrChkBoxStyle}"></Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
</DataGrid.Columns>
</DataGrid>
You could access the DataGridCheckBoxColumn directly. How about?
<Style x:Key="DgCheckBoxColumnStyle" x:TargetType="{x:Type DataGridCheckBoxColumn}">
<Setter Property="IsThreeState" Value="False" />
<Setter Property="Binding" Value="{Binding <SomePath>, TargetNullValue="True"}" />
</Style>
<DataGridCheckBoxColumn Style="{StaticResource DgCheckBoxColumnStyle}" />
For more information, please refer to the msdn. Should be easier than how you're trying to achieve it.
If you really want to access the controls in the columns you must provide one style for DataGridBoundColumn.ElementStyle and one for DataGridBoundColumn.EditingElementStyle.

Dynamically Show/Hide WPF DataGrid Header Column

I am using VS 2010. I want to display Datagrid header only on grid mouse over. I wrote following code, but it is not working.
<StackPanel>
<DataGrid ItemsSource="{Binding SelectedItemsCollectionView}"
AutoGenerateColumns="False" CellStyle="{StaticResource CellStyle}"
GridLinesVisibility="None" RowHeight="28" HeadersVisibility="None"
ClipToBounds="True" AllowDrop="True" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=DisplaySequence}"/>
</DataGrid.Columns>
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="HeadersVisibility" Value="None" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="HeadersVisibility" Value="Column" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
</DataGrid>
</StackPanel>
Any suggestions?
Thanks!
Just Remove HeadersVisibility="None" in DataGrid tag. Its working fine.
HeadersVisibility is a dependency property.They have Value resolution strategy.
Local value has more precedence compare to the trigger value.
Source. For detail see here

Show "No record found" message on a WPF DataGrid when it's empty

If there is no record available, I want to add a TextBlock on data grid, below the header, showing the message "No Record Found."
Consider the attached image for reference.
Its been a long time since the question has been posted. But I thought this might be useful to someone else.
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<DataGrid Name="dgProjects" ItemsSource="{Binding Projects}" AutoGenerateColumns="True" />
<TextBlock Text="Employee has no projects" Visibility="{Binding Items.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=dgProjects}" />
For simplicity purpose I have set AutoGenerateColumns="True". Please define the columns. This way when a empty datasource is bound, the column names will be shown along with 'Empty row' message.
Finally I am able to findout the way.
When the grid in empty, add a default row on grid
Create a RowDetailTemplate which contain a text block with a message "No Record Found"
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="No Record Found" Width="400"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
Set the style on datagrid
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsRecordExists,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:MainWindow}}}" Value="false">
<Setter Property="RowHeight" Value="0"></Setter>
<Setter Property="RowDetailsVisibilityMode" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
By default (record available on datagrid) row detail template will be collapsed.
DataTrigger that checks CLR poperty, if it is false then show the row detail template.
The reason for setting the rowheight as 0 to hide the default row which we haved added on 1st step.
Add the grid inside the stackpanel
Place below border code next to datagrid
<Border HorizontalAlignment="Stretch" VerticalAlignment="Center"
BorderThickness="1,0,1,1" BorderBrush="Black" Height="35">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourListName.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="No record fount" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
It will show/hide based on your collection/list count.
I find that it is easy to center a text block over the grid and set its visibility based on the number of rows. I am usually using MVVM and will bind the visibility to a View Model property:
<Grid>
<toolkit:DataGrid>
<toolkit:DataGrid.Columns>
.
.
.
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
<TextBlock Text="No Records Found" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding EmptyMessageVisibility, Mode=OneWay, FallbackValue=Visible}" />
</Grid>

Resources