Use checkbox as togglebutton in expander - wpf

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

Related

Excess border selection in WPF's Lisbox [duplicate]

I have a ListBox in which each item is a StackPanel. The StackPanel consist of an Image and a TextBlock below it:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding Path=ImageFilePath}"/>
</Image.Source>
</Image>
<TextBlock Text="Title" TextAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
It looks like this:
When the user select an item, I get the default blue rectangle that surround the StackPanel:
Now, I want to make a different border for the selected-item, but I want it to surround only the image.
I know how to make a control template and put a custom border around the ContentPresenter, but this, of course, will surround the whole StackPanel, not only the Image.
I don’t know if making changes to the ContentPresenter is possible, and if it is a good idea at all. If there is other way to achieve the look I want, it will be fine as well.
Right, the ListBox's own ContentPresenter isn't helpful for what you're doing. You want to a) eliminate the ListBox's own selection visuals and b) replace them with something more suitable in the DataTemplate for your items.
The default selection visual is applied by the default template for ListBoxItem. So replace that template. Using a Style in the resources for your ListBox, apply your own control template to ListBoxItem. Not much to it, just present the content and don't provide a selection background. Then you handle the selection visuals with a trigger in your data template, where your image and your label are defined and you can apply changes to one and not the other. The below example works for me.
Note that there's some fiddling with the HorizontalAlignment on the Border element to make it cling to the Image element within it. Also, I wrote a quickie test viewmodel whose Items property is called Items; I assume this is not the name of the collection member you're using to populate your own ListBox.
<ListBox
Margin="8"
ItemsSource="{Binding Items}"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
x:Name="HighlightBorder"
BorderThickness="4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
>
<Border.Style>
<Style TargetType="Border">
<!-- MUST set default BorderBrush via a style, if you set it at all.
As an attribute on the Border tag, it would override the effects of
the trigger below.
-->
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</Border.Style>
<Image Source="{Binding ImageFilePath}" />
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter TargetName="HighlightBorder" Property="BorderBrush" Value="Orange" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

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!!

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....

WPF: How to make a Expander overflow and fill window

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.

How can I use a custom TabItem control when databinding a TabControl in WPF?

I have a custom control that is derived from TabItem, and I want to databind that custom TabItem to a stock TabControl. I would rather avoid creating a new TabControl just for this rare case.
This is what I have and I'm not having any luck getting the correct control to be loaded. In this case I want to use my ClosableTabItem control instead of the stock TabItem control.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True"
Controls:ClosableTabItem.TabClose="TabClosed" >
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type Controls:ClosableTabItem}" >
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
EDIT: This is what I ended up with, rather than trying to bind a custom control.
The "CloseCommand" im getting from a previous question.
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border
Name="Border"
Background="LightGray"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="25,0,0,0"
SnapsToDevicePixels="True">
<StackPanel Orientation="Horizontal">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="20,1,5,1"/>
<Button
Command="{Binding Path=CloseCommand}"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
Margin="1,1,5,1"
Background="Transparent"
BorderThickness="0">
<Image Source="/Russound.Windows;component/Resources/Delete.png" Height="10" />
</Button>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter TargetName="Border" Property="BorderBrush" Value="DarkBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
found a way,
derive a class from TabControl and override this function, in my case I want the items of the tab control (when bound) to be CloseableTabItems
public class CloseableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}
HTH Someone
Sam
You don't want to set the DataType of the DataTemplate in this case. The value of the ItemTemplate property is used whenever a new item needs to be added, and in the case of a tab control it will be used to create a new TabItem. You should declare an instance of your class within the DataTemplate itself:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" Controls:ClosableTabItem.TabClose="TabClosed">
<TabControl.ItemTemplate>
<DataTemplate>
<Controls:ClosableTabItem>
<TextBlock Text="{Binding Path=Id}" />
</Controls:ClosableTabItem>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will cause a new ClosableTabItem to be created whenever a new tab is added to the TabControl.
Update; From your comment, it sounds like that the ItemTemplate controls what is created within the TabItem, rather than changing the TabItem itself. To do what you want to do, but for a TreeView, you would set the HeaderTemplate. Unfortunately, I don't see a HeaderTemplate property of TabControl.
I did some searching, and this tutorial modifies the contents of the tab headers by adding controls to TabItem.Header. Maybe you could create a Style for your TabItems that would add the close button that your class is currently adding?

Resources