WPF: How to make a Expander overflow and fill window - wpf

I'm trying to create a expander that has a togglebutton/header as a slim bar to the left but when expanded fills over the rest of the window, even over material that's already there.
I'm not really sure what the best way to do it is. I thought perhaps of a grid with 2 columns. First would have the expander, second the other material. Next I would have a trigger that would set the second column width to zero when the Expander IsExpanded.
I'm not really sure how to get that to work or even how to do it properly.
Here is some code example:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Name="SecondColumn" Width="*" />
</Grid.ColumnDefinitions>
<Expander ExpandDirection="Right" IsExpanded="True">
<Expander.Resources>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander" >
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True" >
<Setter TargetName="SecondColumn" Property="ColumnDefinition.Width" Value="0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Resources>
<ListBox >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Expander>
<TabControl Grid.Column="1" />
</Grid>
I wan't the listbox to be seen when expanded, otherwise the TabControl
Any ideas?

It sounds like you're wanting to do something similar to Karl Shifflett's example here. He's just modifying the z-index of the content control in this case and setting the row height manually to give the illusion of a popup, so you'd want to make sure you're not trying to ZIndex other visual elements similarly.
You will want to make sure you're setting ColumnSpan and RowSpan on your Expander so that when it does expand it covers the content of those rows.

Related

Height of WPF TreeViewItem controltemplate not collapsing

I have an issue that I have been trying to solve for a week and have not only played around extensively trying to figure this out but have done lots of research on StackOverFlow and other websites on how to fix this problem. Just to be clear, I have been learning WPF for about 3 or so months and come from WinForms and am still in the learning phase.
Here is my problem.
I have a TreeViewItems that I am adding to a TreeView control. These TreeView items use a Style that creates a custom look that I am trying to accomplish that is pretty much the look and feel of the entire application. The Style uses an explicit Setter.Value against the Template property to create the custom look of the TreeView item. It has its own custom expander arrow, TextBlock header that is bound to the TreeViewItem header, and also of course a ContentPresenter and a ItemsPresenter. There is also a trigger that is wired up to the value of the TreeViewItem's IsExpanded value so that way the ItemsPresenter can be shown or hidden when the TreeViewItem is expanded or collapsed. Everything works as it should except the collapse and expand part. Of course the ItemsPresenter hides and shows like it should but the TreeViewItem itself does not actually collapse its height when the IsExpanded is false. To show what I mean, here are 2 pictures to illustrate what is going on. I added a green border around the grid in the template of the style to show that the individual TreeViewItem itself is not shrinking its "height" when collapsed.
Expanded
Pic of expanded tree view item
Collapsed
Pic of collapsed tree view item
As you can see, the green border, or the treeview item itself is still the same height when collapsed as it is when expanded. Here is the XAML used to create the custom style of the TreeViewItem's themselves.
TreeViewItem Style XAML Code:
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Border x:Name="MyBorder" BorderThickness="1" BorderBrush="LawnGreen">
<Grid HorizontalAlignment="Left" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ui:TreeViewItemExpander x:Name="TreeViewItemExpander" Grid.Row="0" Grid.Column="0" IsPointingDown="{TemplateBinding IsExpanded}"/>
<!--This represents the text for the tree view item itself-->
<TextBlock Text="{TemplateBinding Header}" Grid.Row ="0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="White"/>
<ContentPresenter x:Name="ContentPresenter" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ItemsPresenter x:Name="ItemsPresenter" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ItemsPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This code below is how I am using the style
<TreeView Style="{StaticResource TreeViewStyle}" Width="200" Margin="419,337,19,328">
<controls:CustomTreeViewItem Header="Folder 1" Style="{StaticResource TreeViewItemStyle}">
<Button Content="Item 1" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20"/>
<Button Content="Item 2" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20"/>
</controls:CustomTreeViewItem>
</TreeView>
Thanks for any input or help that anybody can provide. I hope that I was clear enough.
I figured it out. What I did was just add a setter in the IsExpanded trigger to set the property of the grid row that the ItemsPresenter resides in. All I did was set the height of the row to 0 which essentially hides the items. Here is the code of the trigger itself from the code above with the change applied.
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="Items" Property="Visibility" Value="Visible"/>
<Setter TargetName="ItemsRow" Property="Height" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
I am interested though if anybody else has a better solution. After I figured this out, I then thought about animating the height of the row until it is 0 to give a better effect, but was not successful. I found out that StoryBoards are freezable and are frozen when inside of a Style or ControlTemplate. That means if you wanted it to be animated, then the animation of the item collapsing would have to be implemented with code behind, or on each TreeViewItem individually? I am sure there is a better way. If I find out how to accomplish this, I will update this post for everybody to reference. Please feel free to add to this post on a better solution!!

WPF Style: Generalizing

Following being WPF style, is there a way to generalize the hard-coded column names (Name and Code), so that I could specify them when actually applying this style on a ComboBox? Even better, if I could even modify the number of columns?
<Style TargetType="ComboBox" x:Key="MultiColumnComboBoxStyle">
<Style.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border>
<Grid HorizontalAlignment="Stretch" TextElement.FontWeight="Normal">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Code" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" />
<Rectangle Grid.Column="1" Width="1" Fill="Black" />
<TextBlock Grid.Column="2" Text="{Binding Path=Code}" Margin="5,0,5,0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Grid.IsSharedSizeScope="True" IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
instead of using a style you could consider craeting a custom control with a dependency property for your columns.
a little bit of setup involved but It will better meet your needs, especialy if you want to reuse it.
an example would be something like the following. Some of this is psuedo code you should be able to fill out.
<!-- In your generic.xaml file -->
<Style TargetType="MyCustomComboBox" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="MyCustomComboBox" >
<!-- your template code goes here -->
<Grid x:Name="_myCustomGrid />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
//then in a cs file inherit from the combo box
public class MyCustomColumnComboBox : ComboBox //get all the combobox functionality
{
public IList ComboColumns
{
get { return (IList)GetValue(ComboColumnsProperty);}
set { SetValue(ComboColumnsProperty,value);}
}
public static readonly DependencyProperty ComboColumnsProperty = DependencyProperty.RegisterProperty(...);
private Grid _grid;
public override OnApplyTemplate()
{
//pull your template grid info here, then use that when setting the columns.
_grid = GetTemplateChild("_myCustomGrid") as Grid;
//from here you can check to see if you have your list yet,
//if you don't then you maintain the grid for when you do have your list.
// This can behave different depending on if you are in wpf or silverlight,
// and depending on how you were to add the items to the dependency property.
}
}
In Summary, for you, add the custom control with the custom dependency property, then in your theme/generic.xaml drop in your template and name the grid to what you want to pull into your template in the on apply template function. from there you are either ready to set up or can set up your columns that you specified in the dependency property.
NOTE : The dependency property isn't actually necessary but it can help to buy you a little bit more flexibility later on using things like the dependency properties on change callback to update if necessary.

Share column width between Grids in items of an ItemsControl

I'm writing a control to display and edit objects in a form. The control (FormDataView) is an ItemsControl where each item is a FormField control made of a Grid, with the field name in the left column and the editor (e.g. TextBox) in the right column. In order to align the editors, I want the first column in each Grid to share the same width.
So I tried to use IsSharedSizeScope and SharedSizeGroup, but it doesn't work, the first column has a different width in each FormField.
Here are the styles for these controls:
<Style TargetType="{x:Type ctl:FormDataView}" BasedOn="{StaticResource ResourceKey={x:Type ItemsControl}}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Grid.IsSharedSizeScope="True"
IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ctl:FormField}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctl:FormField}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="headerColumn" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
Content="{TemplateBinding Header}"
Margin="3"
TextElement.FontWeight="Bold" />
<ContentPresenter Grid.Column="1"
Name="PART_Display"
ContentTemplate="{TemplateBinding DisplayTemplate}"
Margin="2"/>
<ContentPresenter Grid.Column="1"
Name="PART_Editor"
ContentTemplate="{TemplateBinding EditorTemplate}"
Margin="2"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsInEditMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ctl:FormDataView}}}"
Value="True">
<Setter TargetName="PART_Display" Property="Visibility" Value="Collapsed" />
<Setter TargetName="PART_Editor" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Notice how Grid.IsSharedSizeScope is set in the ItemsPanel of FormDataView, while SharedSizeGroup is set in the template of FormField. This correctly expresses what I want to do: each FormField should use the same width for the first column. However, according to the documentation for the SharedSizeGroup property, this scenario is not supported:
Grid size-sharing does not work if you set IsSharedSizeScope to true
within a resource template and you define SharedSizeGroup as outside
that template.
OK, so I can understand why it doesn't work... but I don't know how to work around this limitation.
Any idea?
N.B.: I don't want to assign a fixed width to the first column of course...
Sadly I have no access to my Visual Studio Enviroment so I couldnt check the following tips...
Assign Grid.IsSharedSizeScope="True" to FormDataView itself and not to the ItemsPanel. Do you really need StackPanel as the items panel? cant you live without that?
See if the above change works first...
if not then revamp your item level code and assign SharedSizeGroup="headerColumn" in your item data template of your FormDataView and not in the ControlTemplate of individual FormField.
Let me know if this helps....

XAML: UniformGrid where you can define cells as not unifrom in size?

As a part of an ItemsControl style I'd like to have something that works similar ot Grid/UniformGrid as an ItemsPanel.
In its simplest form, the Style would look like this:
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ItemsPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Columns="2" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style>
<Setter Property="Control.Margin" Value="10"/>
</Style>
</Setter.Value>
</Setter>
</Style>
and I would use it like this:
<WrapPanel Orientation="Vertical">
<ItemsControl>
<TextBlock Text="Label1:"/>
<TextBlock Text="ThisWillBeBound" />
</ItemsControl>
<ItemsControl>
<TextBlock Text="Label2:"/>
<TextBlock Text="ThisWillBeBound" />
</ItemsControl>
<ItemsControl>
<TextBlock Text="Label3:"/>
<TextBlock Text="ThisWillBeBound" />
</ItemsControl>
<ItemsControl>
<TextBlock Text="Label4:"/>
<TextBlock Text="ThisWillBeBoundButIsSomewhatLongerThenTheOtherProperties" />
</ItemsControl>
<ItemsControl>
<TextBlock Text="Label5:"/>
<TextBlock Text="ThisWillBeBound" />
</ItemsControl>
<ItemsControl>
<TextBlock Text="Label6:"/>
<TextBlock Text="ThisWillBeBound" />
</ItemsControl>
</WrapPanel>
As you can see, you would throw a Label for a property into this plus a binding to that property and they would stick together even if things would wrap around becuase of different sizes of the app window for example.
The nice thing here about theUniformGrid is that it seems that if one Label is longer the others will get the same space meaning the bound properties would line up neatly (why this happens I don't understand because these should be seperate UniformGrids I guess). Problem here is that the cell width of the Label part and the Property part is the same, so if the property is long there will be a large gap between the Label and the Property.
Using normal Grid instead would mean I'd have to set the Grid.Column="0" and Grid.Column="1" everyplace I put items into this ItemsControl. That's not really what I wan't.
Is there a way for me to have some sort of grid that lines up like the uniform one but doesn't stratch all columns when one only needs to be and assumes you are putting into columns the same way you declare the items?
The way I've typically seen this sort of scenario handled is via a combination of Grid+IsSharedSizeScope+ListView+DataTemplate.
In this case, the label text (i.e. "Label1:") and entry text (i.e. "ThisWillBeBound") would be in a single item type and a collection of that type of item would be bound to the list view. The Grid.IsSharedSizeScope allows for sharing column size info between different entries while using a template - very handy.
There's a great example of this at http://www.wpftutorial.net/DataTemplates.html. Is this the sort of scenario you're looking for?

Use checkbox as togglebutton in expander

Im quite new to wpf and have to following problem.
I need to create a List (i am using a listbox) of items that can be expanded (expander).
The problem is, that they can be expanded, only if they have been 'selected'.
Each listboxitem should have a checkbox and some text.
So very basic example to illustrate what i mean:
<listbox>
<item>(checkbox) John Doe</item>
<item>(checkbox) Mike Murray</item>
</listbox>
If any (so multiple is allowed) of the checkboxes in the listbox are checked, then
the item expands showing more data.
Again an example:
<listbox>
<item>
(checkbox-checked) John Doe
Some extra data shown in expanded area
</item>
<item>
(checkbox-unchecked) Mike Murray</item>
</listbox>
I cant get a expander to use a checkbox as 'togglebutton'.
Could anyone help me out? Some example code would be very welcome...
This should do the trick:
<ListBox>
<ListBox.Resources>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Content="{TemplateBinding Header}"
/>
<ContentControl
x:Name="body"
Grid.Row="1" Content="{TemplateBinding Content}"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="body" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<Expander Header="One">
Content one
</Expander>
<Expander Header="Two">
Content two
</Expander>
</ListBox>
I've defined a Style here that changes the Template of any Expander controls to which the Style is applied. (And since I've put the Style in the ListBox.Resources it'll automatically apply to an Expander controls in the list.)
The trick to getting the CheckBox to work is that when you put it (or indeed any ToggleButton based control) into an Expander template, you need to use a data binding configured with its RelativeSource set to the TemplatedParent. This enables two-way binding - it means that not only does the CheckBox reflect the current state of the expander, it is also able to change the current state.
All you need to add a check box in the header is this code:
<telerik:RadExpander.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center"/>
<TextBlock Margin="5,0,0,0">Legend</TextBlock>
</StackPanel>
</telerik:RadExpander.Header>
I am using Rad Control, The same can be done using the standard expander

Resources