I have an WPF application that contains a grid. The grid is split into 3 columns with the 3rd grid having zero width upon loading.
I have two datagrids in the other two columns. When the selected item in one of the datagrid changes the other datagrid changes it display values, i.e. a master detail template. This all works fine.
There is one value in the datagrid that if selected I wish this 3rd column to changes its width from zero to 2*. I don't know how to do this?
I wish to achieve this through XAML. I have been looking at data triggers & value converters. I written some code below quickly to test. I have read that setting the column to width=0 there is probably higher on the dependency property setting precedence list. Is there anyway to do this or will I need to use code behind?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Column="0"
ItemsSource="{Binding OrderList}"
SelectedItem="{Binding OrderSelected}"
AutoGenerateColumns="True">
</DataGrid>
<TextBox Grid.Column="1" Text="{Binding OrderSelected.Name}">
</TextBox>
<Grid x:Name="columnHideSeek" Grid.Column="2" Background="Blue">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding OrderSelected.Name}" Value="Mark">
<Setter Property="Grid.Width" Value="10"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</Grid>
Using the correct DataTrigger, this is totally possible. First, add the Trigger to the UI element that you want to change... the ColumnDefinition, not the Grid:
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="{x:Type ColumnDefinition}">
<Setter Property="Width" Value="10" />
<Style.Triggers>
<DataTrigger Binding="{Binding OrderSelected.Name}" Value="Mark">
<Setter Property="Width" Value="2*" />
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
Next, don't set the Width on the ColumnDefinition element, but in the Style instead. Otherwise the Width on the ColumnDefinition element will override the value set from the DataTrigger.
Related
I have a grid in row =o and column=1
On button click, i want to change this grid.row =0 and grid.column=0.
I have tried doing it by creating a style with data trigger to set the grod.column=0.As this is the local value of gris and this is not available in the style propertiea.The value of Grid.column is not getting changed.Can you please help me how can i change the grid.column value in xaml to meet my requirement.
Changing column is as easy as changing value of attached property.
Below is an example with ToggleButton moving between first and second column depending on IsChecked value:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<ToggleButton>
<ToggleButton.Style>
<Style>
<!-- <Setter Property="Grid.Column" Value="0" /> optional, see below for explanation -->
<Style.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="Grid.Column" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</Grid>
Demo:
It shouldn't matter if DataTrigger is used.
Be aware about value precedence when dealing with dependency properties, e.g. setting value locally will render trigger useless (you will have to use another approach, e.g. StoryBoard to override it):
<ToggleButton Grid.Column="0"> <!-- trigger will not be able to change it -->
...
Style setter is the right place to set initial value (see commented part in first xaml above).
Context: I'd like to create a DataGrid with one row, which should take the whole place.
As I learned, I Need to set the Width of the DataGrid to assure this code works:
<StackPanel x:Name="stackMails" Orientation="Horizontal">
<TextBlock Text="Personal E-Mails:" />
<DataGrid x:Name="dgPersonalMail" VerticalScrollBarVisibility="Auto" ItemsSource="{Binding SupportConfiguration.PersonalMailAddresses}" CanUserAddRows="True" CanUserDeleteRows="True" CanUserResizeRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="E-Mail" Binding="{Binding}" Width="*" />
</DataGrid.Columns>
</DataGrid>
Since I'd like to make the size of the DataGrid dynamic, I bound the width of the DataGrid to the ActualWidth of the Stackpanel:
<Style TargetType="DataGrid">
<Setter Property="Width">
<Setter.Value>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=StackPanel, AncestorLevel=1}" />
</Setter.Value>
</Setter>
<Setter Property="MaxWidth" Value="400" />
<Setter Property="MaxHeight" Value="350" />
<Setter Property="MinHeight" Value="150" />
</Style>
This is a solution I found on SO as well, seeing here: How do I make XAML DataGridColumns fill the entire DataGrid? (Binding it on the Window has the same behavior.)
But now the Problems start: Seeing this Thread WPF What happens when a child's width is bound to a parent's actualwidth .
This is exactly the behavior I get: Without MaxWidth, the size of the DataGrid grows forever.
I kindahow see, why it grows (Updating child Updating parent etc. etc.). But I guess I'm not the first Person who'd like to make this work, so I guess I'm doing something wrong here?
We have a file of standard styles. One style, SectionGroup, we use on all our GroupBox elements. It has a custom template which uses a Border to put an underline below the header, among other things.
On one page, we have a checkbox next to a GroupBox header; when the user unchecks the checkbox, the contents of the GroupBox hide (visibility collapsed) and the header remains. Unfortunately the underline under the header then looks ugly; we also want to hide this.
I've given it my best attempt, so the parent SectionGroup style now looks like this:
<Style x:Key="SectionGroup" TargetType="GroupBox">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,1</Thickness>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid Margin="0">
...
<Border Grid.Row="0" BorderThickness="{DynamicResource HeaderBorderThickness}" >
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=GroupBox}}"/>
</Border>
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
By defining the HeaderBorderThickness resource and using it as a DynamicResource, I can override it in my page (as explained in this answer):
<GroupBox>
<GroupBox.Style>
<Style TargetType="GroupBox" BasedOn="{StaticResource SectionGroup}">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,0</Thickness>
</Style.Resources>
<!-- TODO triggers here.. -->
</Style>
</GroupBox.Style>
<GroupBox.Header>Section One</GroupBox.Header>
...contents...
</GroupBox>
So indeed, by redefining a Thickness of the same key, the DynamicResource works as expected and there is no underline on the header.
Now I need to toggle it based on a trigger/binding. I'm pretty new to this, but elsewhere in this page I have figured out to do stuff like this:
<Grid Visibility="{Binding Path=FooBoolean, Converter={StaticResource BooleanToVisibility}}">
I think there's a little more magic involved in our viewmodel class (followed the example of existing bindings & properties), but it works.
Now the question is -- how do I bind the boolean value in FooBoolean, to the HeaderBorderThickness resource value? Or what other means can I use to accomplish my goal?
It seems to me that you could do this in a much more WPF way with a DataTrigger, perhaps something like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=
GroupBox}}"/>
<Rectangle Grid.Row="1" Height="1" Margin="0,2,0,0" Fill="Black">
<Rectangle.Style>
<Style>
<Setter Property="Rectangle.Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{IsChecked, ElementName=YourCheckBox}"
Value="False">
<Setter Property="Rectangle.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
This method enables you to set the Width, Height, Padding and whatever other properties of the line. If you can't access the CheckBox directly from the Style, then you could try adding a bool property to bind to both the Checkbox.IsChecked property and the DataTrigger.Binding property. Or just manage the Rectangle.Visibility in your own method.
I have a grid with several items inside, not all of them are visible: some are collapsed. Is there a simple way in WPF to resize the already visible items in the grid when a new item shows up?
Actually my needs are simpler than that: I only have 2 items that must be stacked vertically. Most of the time only one will be visible, but when the second appears I need the first one to resize to a 2/3 of the size of the grid and the second to be placed below filling the remaining place: 1/3.
I feel like I have to play with the Grid.Row properties (and maybe with Grid.RowSpan) but I don't know how to achieve the desired behavior without messing it up in the code-behind.
What I've tried is mixing the auto-size with the LastChildFill property of DockPanel without success.
You could try something like:
<Grid Background="Green">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition>
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Setter Property="Height"
Value="0.25*" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=secondRect,
Path=Visibility}"
Value="Collapsed">
<Setter Property="Height"
Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
</Grid.RowDefinitions>
<!-- Row 1 -->
<Rectangle Grid.Row="0"
Fill="Blue" />
<!-- Row 2 -->
<Rectangle x:Name="secondRect"
Grid.Row="1"
Fill="Tomato" />
</Grid>
So you're setting a Style.Trigger on the second Grid row to check if the element it contains is Collapsed and if so set it's Height to "0" and if not it's "0.25*" while Grid Row 1 has it's Height as * or "all remaining space" which would tie in with this fine.
Alternate:
You could do it with Grid.RowSpan as well like you mentioned.
<Grid Background="Green">
<Grid.RowDefinitions>
<RowDefinition Height="0.75*" />
<RowDefinition Height="0.25*" />
</Grid.RowDefinitions>
<!-- Row 1 -->
<Rectangle Grid.Row="0"
Fill="Blue">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Grid.RowSpan"
Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=secondRect,
Path=Visibility}"
Value="Collapsed">
<Setter Property="Grid.RowSpan"
Value="2" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<!-- Row 2 -->
<Rectangle x:Name="secondRect"
Grid.Row="1"
Fill="Tomato"
Visibility="Collapsed" />
</Grid>
Now you got the Trigger on the 1'st Row's "element" than the Grid.RowDefinition and when you detect the second row's element becomes Collapsed you switch the first one's RowSpan to 2 else it stays at 1.
This question already has answers here:
How to get DataTemplate.DataTrigger to check for greater than or less than?
(4 answers)
Closed 2 years ago.
tl;dr: I want detail views to appear in 2 columns if 2 items are selected, and to span 2 columns if only 1 item is selected.
I have a DockPanel with 2 ListBoxes docked on the left and a 2-column Grid on the right.
When an item is selected in listBox1, I display a details view in Column 0 of the Grid.
When an item is selected in listBox2, I display a details view in Column 1 of the Grid.
<DockPanel>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<ListBox x:Name="listBox1" ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem1}"/>
<ListBox x:Name="listBox2" ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem2}"/>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Content="{Binding SelectedItem1}" ContentTemplateSelector="{StaticResource SelectedItemTemplateSelector}"/>
<ContentControl Grid.Column="1" Content="{Binding SelectedItem2}" ContentTemplateSelector="{StaticResource SelectedItemTemplateSelector}"/>
</Grid>
</DockPanel>
This works fine. However, I'd like to modify it so that the details view will span both grid columns when there is an item selected in one of the ListBoxes but not in the other.
My first thought was to create a couple of DataTriggers that modify the ColumnSpan of the ContentControls based on the SelectedIndex of the ListBoxes.
This works fine for the details view that normally lives in Grid.Column="0". However, it doesn't work for the details view that normally lives in Grid.Column="1" since the original column number is hard-coded:
<ContentControl Grid.Column="0" Content="{Binding SelectedItem1}" ContentTemplateSelector="{StaticResource SelectedItemTemplateSelector}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=listBox2, Path=SelectedIndex}" Value="-1">
<Setter Property="Grid.ColumnSpan" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<ContentControl Grid.Column="1" Content="{Binding SelectedItem2}" ContentTemplateSelector="{StaticResource SelectedItemTemplateSelector}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=listBox1, Path=SelectedIndex}" Value="-1">
<!--Can't modify Grid.Column since it's hardcoded above!-->
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
If I could create a DataTrigger that could do a value comparison against ListBox.SelectedIndex (ie. >=0), then I could avoid hard-coding the column numbers in the first place.
Is there any way to do something like this in XAML?
You can always do the hidden element binding trick. the example below, you just change bindme to 2, and the Grids span will change. I do this on all types of stuff you can't animate with a storyboard.
<Rectangle x:Name="bindme" Width="0" Height="0" Tag="1" />
<Grid Grid.ColumnSpan="{Binding Tag, ElementName=bindme}">