XAML Datagrid columns width Issue - wpf

I have WPF datagrid. I want to use
i) maximum available width for "Description" textblock (present inside 1st column)
ii) Auto width for Image control (present inside 1st column) and
iii) "Auto" width for rest of the column.
I have written follwing xaml code. But width is not working as per requriement.
Edit
Issue : Instead, 2nd column text is getting truncated.
screenshot:
Any suggestions?
Thanks!
<Grid Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Row="0" Grid.ColumnSpan="2" ItemsSource="{Binding SelectedItemsCollectionView}" HorizontalAlignment="Stretch" AutoGenerateColumns="False">
<DataGrid.Columns>
<!--Get maixmum available space for 1st Column-->
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid Margin="5,0,0,0">
<Grid.ColumnDefinitions>
<!--Get "Auto" space for Image control-->
<ColumnDefinition Width="Auto" />
<!--Get remaining available space for Description textblock-->
<ColumnDefinition Width="*" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Stretch="None" Margin="0,0,5,0" Source="{Binding Converter={StaticResource ShowHideToImageConverterRef}, Path=IsHidden}"
Visibility="{Binding Converter={StaticResource VisibilityConverterRef}, Path=RowType}" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{Binding Description}" VerticalAlignment="Center" HorizontalAlignment="Left" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--Get "Auto" space for 2nd Column-->
<DataGridTextColumn Binding="{Binding Comments}" Width="Auto" />
</DataGrid.Columns>
</DataGrid>
<Label Grid.Row="1" Grid.Column="0" Content="Name" ></Label>
<TextBox Grid.Row="1" Grid.Column="1" Margin="10,0,0,0" Text="{Binding EmpName}"/>
</Grid>

Issue resolved after applying below solution suggested by Scott on other stackoverflow post "DataGrid column width doesn't auto-update".
1) In your DataGridTextColumn Bindings (all except your * sized column) set NotifyTargetUpdated=True.
2) On your DataGrid, add a handler to the TargetUpdated event.
3) In your TargetUpdated event handler:
-- a) Set the DataGrid's * sized column's width to 0.
-- b) Call the UpdateLayout() method on the DataGrid.
-- c) Set the DataGrid's * sized column's width back to new DataGridLength(1, DataGridLengthUnitType.Star)

Related

WPF: DataGrid not expanding to occupy both columns of Grid when second column content is collapsed

I have a Grid which has two columns, first column has a DataGrid and second column has another grid. I expect DataGrid to expand to take full space when Visibility of second grid is set to 'collapsed'.
Following is the code snippet:
<Grid Grid.Row="1" HorizontalAlignment="Left" Width="344">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<DataGrid MaxWidth="344" Grid.Column="0" SelectedItem="{Binding Dummy, Mode=TwoWay}" Background="DarkGray"
ItemsSource="{Binding DummyList}" SelectionMode="Single" AutoGenerateColumns="False" RowHeaderWidth="0" GridLinesVisibility="All">
<DataGrid.Columns>
<DataGridTemplateColumn Header="" Width="35">
<DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="" Width="*" MinWidth="85">
<DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="" Width="*" MinWidth="90" >
<DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="" Width="*" MinWidth="80">
<DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="" Width="*" MaxWidth="25" Visibility="Collapsed">
<DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Column="1" Margin="0,1,0,0" Background="DarkGray" Visibility="Collpased">
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
</Grid>
Even when I set visibility of Grid in second column to collapsed, DataGrid do not takes up the whole space, second column remains empty. Do I have wrong expectation, If yes/no, how can I achieve this behavior?
second column has a fixed width <ColumnDefinition Width="24"/>, even if it doesn't contain child elements (ot if they are collapsed).
change width to Auto <ColumnDefinition Width="Auto"/> and give necessary width to child Grid: <Grid Grid.Column="1" Width="24". Then column will react to content visibility

WPF TreeView ItemTemplate alighments

i have create my own tree-view with multi column Headers using standard wpf controls
Dock Panel
DataGrid , Just for creating the columns and having sorting and resizing capabilities. The height of datagrid is only 25 , we only need to show columns not data here.
The TreeView Control with hierarchy
i have add the image just to understand the problem and the XAML code
The Account Type should be always align at the left no matter how many levels are expanded in the "first column"
somewhere in the xaml of the TreeViewItemTemplate i lost the idea.. Can anyone help me to fix the alignments
<DockPanel DataContext="{StaticResource cust}">
<Button Command="{Binding rld}" Content="reload" DockPanel.Dock="Top"/>
<!--Unbound DataGrid just to display the headers-->
<DataGrid Height="25" DockPanel.Dock="Top" ItemsSource="{Binding Customers}" AutoGenerateColumns="False" Margin="0,0,0,0">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" x:Name="col0"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding AccountType}" Header="Account Type" x:Name="col1"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<!--Actual Binding with Tree View and item Template to display the properties-->
<TreeView ItemsSource="{Binding Customers}" DockPanel.Dock="Top">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding rel}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=col0,Path=ActualWidth}"></ColumnDefinition>
<ColumnDefinition Width="{Binding ElementName=col1,Path=ActualWidth}" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{Binding AccountType}" VerticalAlignment="Center" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</DockPanel>
The TreeView naturally indents child items, so its normal what you see.
A possible fix could be to set a negative left margin in the AccountType TextBlock with the same value of the indent, therefore neutralizing the gap.
You need to apply it only if its parent is expanded. This can easily be done modifying a bit your viewmodels.
<TextBlock Grid.Column="1" Margin="{Binding marginPropertyInVM}" Text="{Binding AccountType}" VerticalAlignment="Center"/>
You can limit the size of your first column by changing your template grids column definition to:
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="{Binding ElementName=col1,Path=ActualWidth}" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{Binding AccountType}" VerticalAlignment="Center" />
</Grid>
This way the first column will only be allowed the remaining width once the 2nd column and indent have been allocated their space.

Fit a Textbox and a button in DataGrid Cell

I am trying to fit Textblock and a button in DataGrid Cell. The Textblock holds a portion of my text and when I click the button a dialog is display. I general my code looks like the one below
<DataGridTemplateColumn Header="Message" Width="Auto" MinWidth="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientetion="Horrizontal">
<TextBlock MinWidth="200" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
<Button DockPanel.Dock="Right" Width="90" Margin="1" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I want the Button to be always at righter side of the cell and its width to be fixed. The TextBlock needs to be variable, for example when I resize the window, and so the DataGrid, the TextBlock should stretch also.
The problem is that, I can not achieve this behaviour / view. The TextBlock varies on each DataGrid line and in some case the button is not at the righter of the cell.
I tried to change the StackPanel to Grid or DockPanel but still I can not get the desirable result.
Using Grid
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="100" Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
<Button Grid.Column="1" Margin="1" />
</Grid>
Any thoughts to share?
StackPanel doesn't really have the concept of aligning to the right. It stacks the elements as close as it can. You can get around this in different ways but in this case, use a DockPanel instead:
<DockPanel>
<Button DockPanel.Dock="Right" Width="90" Margin="1" />
<TextBlock MinWidth="200" TextTrimming="CharacterEllipsis" Text="{Binding Text}" />
</DockPanel>
Note that I moved the TextBlock to be the last child element of the DockPanel. DockPanel, after laying out the other child elements, allocates the remaining space to the last child element (unless you specify LastChildFill=false). In this case, we want the TextBlock to take up the remaining space.
UPDATE: based on the comments above, in addition to changing the panel type to a DockPanel (or Grid), you can use DataGridTemplateColumn.Width to a fixed value instead of Auto. This would make the column load with the specified with but the user can still modify the column with if they want to:
<DataGridTemplateColumn Header="Message" Width="60" MinWidth="60">
I'd set a static value to the DataGridTemplateColumn.Width--it can help with rendering performance. Set the size on your buttons too, so it doesn't size to fit text.
This works for me (I used the border for visualization purposes):
<Window ...
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Model}"
x:Key="VmItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Grid.Column="0" TextTrimming="CharacterEllipsis" Text="{Binding Original}" />
</Border>
<Button Grid.Column="1" Margin="1" Content="{Binding Encoded}" MinWidth="90" MaxWidth="90"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Message" CellTemplate="{StaticResource VmItem}" Width="300" MinWidth="100"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Proof:

How do I get a DataGrid to fill a TabItem

I have the following layout
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="10">
...
</StackPanel>
<TabControl>
<TabItem Header="Summary">
<DataGrid ItemsSource="{Binding SummaryData}"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
HeadersVisibility="Column"
CanUserSortColumns="False" />
</TabItem>
...
</TabControl>
</DockPanel>
Without the DataGrid, the TabControl and TabItems fill the rest of the container perfectly, but when I add the DataGrid it stretches everything out to display all of the rows and columns.
EDIT: more clarity
I am looking to have the DataGrid stretch veritcally and horizontally to fill the TabItem. If it needs more space, I'd like to have the DataGrid's scrollbars appear.
I got the following to do what I want.
<TabItem Header="Summary" >
<Grid x:Name="SummaryGrid">
<DataGrid Height="{Binding ElementName=SummaryGrid, Path=ActualHeight}"
ItemsSource="{Binding SummaryData}"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
HeadersVisibility="Column"
CanUserSortColumns="False" />
</Grid>
</TabItem>
It works until I change the size of the parent panel. The ActualHeight isn't updated. It is good enough for now though.
Is it the kind of behavior you are looking for ?
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="10">
<TextBox Text="tttt" />
</StackPanel>
<TabControl VerticalAlignment="Top" HorizontalAlignment="Left">
<TabItem Header="Summary">
<DataGrid CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
HeadersVisibility="Column"
CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" Binding="{Binding}"></DataGridTextColumn>
</DataGrid.Columns>
<sys:String>Entry</sys:String>
</DataGrid>
</TabItem>
</TabControl>
</DockPanel>
By setting those alignments you will have the DataGrid stretch to the available space in the TabItem. but I'm not sure that's exactly waht you are looking for.
You can play with VerticalAlignment and HorizontalAlignment to get exactly what you need. (setting it to Top/Left/Stretch to get only TabItem, or DataGrid or both stretch the way you want)
The accepted answer is a work around...
The tab control will expand within its container along with any tab items. By making a grid its container and specifying row/column to be * sized (expanding within window) that will trickle down to datagrid.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="15"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="*"/>
<RowDefinition Height="10"/>
<RowDefinition Height="100"/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<TabControl Name="tabOperations"
Grid.Row="1"
Grid.Column="1"
TabStripPlacement="Top"
Margin="0,0,0,4"
>
<TabItem Header="Provider Accounts" >
<DataGrid AutoGenerateColumns="True"
IsReadOnly="True"
ItemsSource="{Binding ProviderAccounts}"/>
</TabItem>
</TabControl>
<GridSplitter Grid.Row="2"
Height="3"
Grid.Column="1"
HorizontalAlignment="Stretch" />
<TextBox Grid.Row="3" Grid.Column="1"
TextWrapping="Wrap" Text="{Binding Status}"
Margin="0,4,0,0"
/>
So regardless of size of the main window, it spans.

Data grid height is too tall when using word-wrap in text block column

I desire to make data grid with one column representing rather lengthy text values. So my goal functionality is:
Column width to be as wide as remaining window space (window can be re-sized)
Word-wrap text as necessary
Limit data grid height to remaining height of window and provide vertical scroll as necessary
The following code meets the first two items and provides a functioning vertical scroll bar, but the data grid height is bizarrely too tall for the content it is showing. Removing word-wrap from the text block fixes this... but I need the word-wrap.
How can I keep the word-wrap functionality without the data grid height getting too excessive?
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- other controls in different parts of the data grid -->
<DataGrid Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left"
Margin="0,6,6,6" Name="dgMessages" VerticalAlignment="Top"
Background="DarkGray" HeadersVisibility="None"
AlternatingRowBackground="Gainsboro" CanUserResizeColumns="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="false" BorderBrush="Black" HorizontalGridLinesBrush="{x:Null}"
ItemsSource="{Binding Messages, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<DataGrid.Columns>
<dg:DataGridTemplateColumn Width="*">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"
TextWrapping="WrapWithOverflow"
Padding="5,5,5,5" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</ScrollViewer>
"dg" namespace is "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Removing the scrollviewer and setting the height of the row containing the data grid to "*" fixed this. Wrapping text in the data grid column is treated normally when the data grid is not in a row that is automatically sized to content.
Here is the end code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- other controls in different parts of the data grid -->
<DataGrid Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left"
Margin="0,6,6,6" Name="dgMessages" VerticalAlignment="Top"
Background="DarkGray" HeadersVisibility="None"
AlternatingRowBackground="Gainsboro" CanUserResizeColumns="False"
CanUserResizeRows="False" CanUserSortColumns="False"
AutoGenerateColumns="false" BorderBrush="Black" HorizontalGridLinesBrush="{x:Null}"
ItemsSource="{Binding Messages, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<DataGrid.Columns>
<dg:DataGridTemplateColumn Width="*">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" TextWrapping="WrapWithOverflow" Padding="5,5,5,5" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>

Resources