I've been staring at some code for a RowDetailsTemplate definition in one of my Silverlight datagrids and thought about refactoring it because it looked like it had repeating grids. I thought, after looking at this, that I would only need one datagrid, but as it turns out, this wierd, mysterious double grid is required to get all the items to line up:
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="20" />
<RowDefinition MinHeight="25" />
<RowDefinition MinHeight="25" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Attendee Cap" Margin="0,0,50,0" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding AttendeeCap}"/>
<TextBlock Text="Start Date" Grid.Row="1" Grid.Column="0" Margin="0,0,50,0" />
<controls:DatePicker SelectedDate="{Binding StartDate, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"/>
<input:TimePicker Value="{Binding StartDate, Mode=TwoWay}" Grid.Row="1" Grid.Column="2" MinWidth="65" />
<TextBlock Text="End Date" Grid.Row="2" Grid.Column="0" Margin="0,0,50,0" />
<input:TimePicker Value="{Binding EndDate, Mode=TwoWay}" Grid.Row="2" Grid.Column="2" MinWidth="65" />
<controls:DatePicker SelectedDate="{Binding EndDate, Mode=TwoWay}" Grid.Row="2" Grid.Column="1" />
</Grid>
</Grid>
</DataTemplate>
I thought I could get rid of the first grid definition and just keep the one that is right before the content. The problem is, if I get rid of that grid, the textbox, data picker, and timepicker don't show up.
Am I missing some unwritten rule about the RowDetailsTemplate that is causing this voodoo behaviour?
You can possibly get rid of the third ColumnDefinition entry in the first grid as there is nothing assigned to it, the only purpose at the moment of column definitions two and three will be to divide the available width into three portions. If you do this though you may want to apply a width to the first column definition and leave the second column as auto sizing to fill the remaining space.
When you have items inside a grid, they are automatically placed in column 0, row 0, unless you specify otherwise. This means your inner grid is being placed in the first column (column 0) of the outer grid because it doesn't have a Grid.Column="" entry. This column is currently using 1/3 of the available width of its parent container. IOW, the outer grid is being used to constrain the inner grid.
You could try using Snoop to determine what is going on with your current and modified layouts, or you could add borders to the grids so you can see their exact positions once rendered.
Related
When I put a Textbox in a grid column like below
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="115"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="90"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="hello"/>
<TextBox Grid.Column="1" />
<Button Grid.Column="2" Content="push me"/>
</Grid>
</StackPanel>
I get proper result, i.e. textbox width is get from parent grid
But when I type a long text, the textbox starts exceeding its column and it stops extending after several extra letters
To .Net 4.6.2, I get same result but changing to .Net 4.7.2 the problem is solved i.e. the textbox width is not changing.
My software is compiled .Net 4.0, is there a solution to solve this for lower .net than 4.7.2?
Tried Pavel's first idea: removing stackpanel and adding another grid row in, still not working in .net 4.6.2
Tried Pavel's second idea: making the width of first column "Auto" instead of "1*". This works, the textbox doesn't extend (.net 4.6.2), however I really wanted the first and second column be responsive to resize.
You can solve this be removing the StackPanel and adding RowDefinition to Grid. You can also set TextWrapping="Wrap" for TextBox for wrapping a long text
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="90"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Text="hello" MinWidth="115"/>
<TextBox Grid.Column="1" TextWrapping="Wrap"/>
<Button Grid.Column="2" Content="push me"/>
</Grid>
StackPanel Y dimension is constrained to content, it will be automatically expanded to its content, more details can be found at panels overview
To me this is a bug that Textbox text size can change its parent width (grid width). This is happening in VS2019 .Net 4->4.6.2 but not in and after .Net 4.7.2.
For anybody faces this problem, I found below workaround by defining an invisible grid which contains textblocks. I get the widths of columns and give them to controls placed in a StackPanel.
<some container>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="115"/>
<ColumnDefinition Width="3*" />
<ColumnDefinition MinWidth="90"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0"/>
</Grid.RowDefinitions>
<TextBlock x:Name="C0" Grid.Column="0" />
<TextBlock x:Name="C1" Grid.Column="1" Margin="5"/>
<TextBlock x:Name="C2" Grid.Column="2" />
</Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Width="{Binding ElementName=C0, Path=ActualWidth }" Text="hello" />
<TextBox Width="{Binding ElementName=C1, Path=ActualWidth }" Margin="5" />
<Button Width="{Binding ElementName=C2, Path=ActualWidth }" Content="push me"/>
</StackPanel>
</some container>
The margin should be the same for column in grid and control in StackPanel (see second column).
I need to position a checkbox above a column in a datagrid, and keep the checkbox aligned with the left edge of the column, even if the columns are resized (or reordered) by the user.
Putting the checkbox in the column header is probably not an option.
The first challenge is knowing when the user is resizing a column.
There is a ColumnHeaderDragStarted event, but I think that is raised when the user drags a column header to reorder it. Does it also apply to a column resize operation?
The next challenge is re-positioning the checkbox as the column is moved. How can the position of the left edge of the column ("Val1", for example) be determined w/r/t to the left edge of the containing grid? I suspect that if that value can be determined, it can be used to position the checkbox.
Thanks for any insights in to this problem --
UPDATE 2
This xaml is providing much of the desired behavior, but the alignment isn't right. If the user move the separator between "Id" and "Val1", the checkbox moves.
The goal, however, is to have the checkbox remain aligned above "Id"; it should move only when the separator between "Name" and "Id" is moved.
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name="DataGrid"
Grid.Row="1" Grid.Column="0" />
<Canvas Grid.Row="0" Grid.RowSpan="2" >
<CheckBox Content="Check One"
Canvas.Top="10"
Canvas.Left="{Binding Columns[1].ActualWidth, ElementName=DataGrid}" />
</Canvas
</Grid>
UPDATE:
This seems like it might be a useful approach:
https://stackoverflow.com/a/41094009/107037
The challenge of positioning the checkbox still exists, however...
Since someone asked:
Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox Content="Check One" Grid.Column="1" />
<CheckBox Content="Check One" Grid.Column="2" />
<DataGrid x:Name="DataGrid"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" ></DataGrid>
</Grid>
I have a 5 column grid in WPF which displays some dynamic data. Columns 1,2 and 4 are fixed width as they are effectively labels, but 2 and 5 can be of different lengths.
Column 3 should wrap to make sure all content is shown but can expand to a maximum width of 390. But column 5 should remain on a single line. When I resize the window, everything must remain on screen without a horizontal scrollbar. The middle (3rd) column should shrink down to keep the 4th and 5th columns on screen.
Current behaviour: Column 3 will wrap when the contents expand beyond 390, but resizing the window will not cause this column to resize, but will cause column 5 then 4 disappear off screen.
Here's the code so far:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid x:Name="ListItemControl"
Grid.Column="0"
MinHeight="50"
Width="auto"
VerticalAlignment="Top">
<Border Background="LightBlue"
Margin="1,0,1,1" />
<Grid VerticalAlignment="Center"
Margin="20,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="130" />
<ColumnDefinition Width="*"
SharedSizeGroup="col2" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="auto"
SharedSizeGroup="col4" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Margin="0,10,0,10"
Text="Due" />
<TextBlock Grid.Column="1"
Grid.Row="0"
Margin="0,10,10,10"
Text="Some Date" />
<TextBlock Grid.Column="2"
Grid.Row="0"
Margin="0,10,30,10"
Text="There is some potentially long text here. This is a placeholder and should wrap."
TextWrapping="Wrap"
MaxWidth="390" />
<TextBlock Grid.Column="3"
Grid.Row="0"
Margin="0,10,10,10"
Text="Who's responsible" />
<TextBlock Grid.Column="4"
Grid.Row="0"
Margin="0,10,10,10"
Text="Name - could be a very long name" />
</Grid>
</Grid>
</Grid>
Any help greatly appreciated!
Your ListItemControl Grid is inside another Grid with a single column, dont know the reason. Remove that parent Grid and you are done.
When I expand the Expander in the code below, the application becomes very sluggish and takes 2-3 seconds to respond to resize/move events triggered by the user. If I set the second column to <ColumnDefinition Width="250"/> response time remains optimal. What am I missing here? (I'm need the UI as per below but without the sluggishness)
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Toolbar .. />
<DataGrid Grid.Row="1" ItemsSource="{Binding collection1}" .. />
<Expander Grid.Column="1" Grid.RowSpan="2">
<DataGrid ItemsSource="{Binding collection2}" .. />
</Expander>
</Grid>
</UserControl>
FYI: I suspect Row Virtualization is not being used when Width=Auto is set and DataGridRow objects are being created for the entire bound data source...
UPDATE
The following also does not remove the sluggishness;
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width={Binding ElementName=expander, Path=Width} />
</Grid.ColumnDefinitions>
..
<Expander Name="expander" Grid.Column="1" Grid.RowSpan="2">
<DataGrid ItemsSource="{Binding collection2}" Width="300" .. />
</Expander>
The problem is that if you set the ColumnWidth to Auto, the column width will be calculated depending on the content, here, the dataGrid. So you will have the second column very large (even though you might not see it depending on your layout) and the dataGrid's columns will all be drawn every time.
So basically, you loose the benefits of columnVirtualization (not rowVirtualization).
This is the same reason why you should never put a DataGrid into a ScrollViewer.
Solution
Set VirtualizingStackPanel.IsVirtualizing="True" on your grid
if this does not work, you might have to take care of the resizing yourself, no real option there.
I am using a grid by the definition of appropriateness defined in this question Grid vs Stackpanel. However when working with grids you have to define the controls position inside them explicitly in the grid. This becomes a pain when having to reorder controls or when adding a new control to the grid. With the code provided as an example, is there a way to get the rows and columns for the text and text boxes to line up while being easy to modify or expand later?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Value One:" Grid.Row="0" Grid.Column="0"/>
<TextBox x:Name="TextBoxOne" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="Value Two:" Grid.Row="1" Grid.Column="0"/>
<TextBox x:Name="TextBoxTwo" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Value Three:" Grid.Row="2" Grid.Column="0"/>
<TextBox x:Name="TextBoxThree" Grid.Row="2" Grid.Column="1"/>
</Grid>
I wrote a custom control I use that makes it extremely easy to do this, but before I created it I generally used this sort of thing:
<ControlTemplate x:Key="ColumnsTemplate" TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="7*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" ContentSource="Header" />
<ContentPresenter Grid.Column="1" />
</Grid>
</ControlTemplate>
<ItemsControl ... ItemTemplate="{StaticResource ColumnsTemplate}">
<HeaderedContentControl Header="Value One:">
<TextBox x:Name="TextBoxOne" />
</HeaderedContentControl>
<HeaderedContentControl Header="Value Two:">
<TextBox x:Name="TextBoxTwo" />
</HeaderedContentControl>
...
</ItemsControl>
This allows easy add/remove of items from the ItemsControl, or better yet, data binding.
If you prefer auto-sizing on the grid rather than star sizing (3* and 7*) you can use a shared sizing scope by setting IsSharedSizeScope on the ItemsControl and SharedSizeGroup on the first ColumnDefinition.
Another option is GridView, but I find it more difficult to use for this purpose.