ZOrder of Expander with Canvas inside of List - wpf

I have a list which contains some controls, including an Expander. Within the expander is another list, which I want to overlay the outer list. Here's a simple repro:
<Page.Resources>
<x:Array x:Key="array1" Type="sys:String">
<sys:String>item 1</sys:String>
<sys:String>item 2</sys:String>
<sys:String>item 3</sys:String>
</x:Array>
<DataTemplate x:Key="buttonTemplate">
<Button Content="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="expanderItem">
<StackPanel>
<Expander Header="Options">
<Canvas>
<StackPanel Panel.ZIndex="999" Background="Red">
<Label>A1</Label>
<Label>A2</Label>
<Label>A3</Label>
<Label>A4</Label>
</StackPanel>
</Canvas>
</Expander>
<Label BorderBrush="Black" BorderThickness="2" Content="{Binding}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<ListBox ItemsSource="{StaticResource array1}" ItemTemplate="{StaticResource expanderItem}"/>
</Grid>
When the Expander gets opened, the inner labels get rendered at the same level as the label in the same DataTemplate and the contents later items in the list. I have tried moving the Panel.ZIndex up to the panel with no change.
If I add the following style:
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Panel.ZIndex" Value="999"/>
</Trigger>
</Style.Triggers>
</Style>
It will properly overlay items in the SAME list item, but still renders intermixed with contents from later list items.
(I suspect this is a fairly obvious layout problem, but I have not been able to find it.)

You could try writing a converter that sets the ZIndex in the datatemplate based on the index of the item in the list. The interesting thing with this would be making sure everything updates correctly as items are added / removed.
Do you need the expander to be independent of layout?

Related

Why are ListViewItems within ContentControl only selectable on first few pixels?

I want to request different types of properties from the user of my WPF application. Therefore I have a ListView thats ItemsSource binds to an ObservableCollection<PropertiesBase>. Each property derives from PropertiesBase. As an expert user should be able to edit these properties, the DataTemplate is selected depending on the EditMode Property and the type of the property.
Data display and everything else works fine, except that I am not able to select a ListViewItem. Only when I click a textbox inside a ListViewItem or the first few pixels of the item I am able to select an item.
The selection only works on the white part of the item
I have tried to play around with Focusable but it does not led me to success. I also copied my xaml of the ListViewItem directly into the Listview (without datatemplate). That worked as expected.
ListView XAML:
<ListView ItemsSource="{Binding PropertyList}" HorizontalContentAlignment="Stretch" SelectionMode="Single">
<ListView.Resources>
<DataTemplate DataType="{x:Type properties:PasswordProperty}">
<ContentControl Content="{Binding}" Background="Red">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PasswordPropertyListViewItem}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.EditMode, RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type ListView}}}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditPasswordPropertyListViewItem}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
Example ListViewItem XAML that is referenced by DataTemplate:
<ListViewItem x:Class="PasswordPropertyListViewItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Border BorderThickness="0,0,0,1" BorderBrush="DarkGray">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch">
<Label Margin="8" Content="{Binding PropertyName}"></Label>
I expect that it does not matter where I click onto the ListViewItem but the item is selected in any case (Especially the red part of the image above).
Clemens provided a solution in his comment above.
I indeed nested two ListViewItems.
The solution is to change the type of my templated control to ContentControl.
<ContentControl x:Class="PasswordPropertyListViewItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Border BorderThickness="0,0,0,1" BorderBrush="DarkGray">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch">
<Label Margin="8" Content="{Binding PropertyName}"></Label>
Thanks Clemens!

Click/focus on a ListBoxItem's content doesn't bubble up

I've got a ListBox that's declared like this:
<ListBox ItemsSource="{Binding Contracts}" SelectedItem="{Binding SelectedContract}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Content="{Binding Name}">
<ListBoxItem.ToolTip>
<Grid>
[code omitted for reasons of clarity]
</Grid>
</ListBoxItem.ToolTip>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I expected the normal selection behavior since I play with the item's ToolTip rather than its content structure.
However, clicking an item's name doesn't focus/select that item. Only by clicking that tiny space between each item (easiest way would be the space between an item's name and the ListBox's border) the item gets focused/selected.
Of course, I googled around and thought I'd found the culprit (event doesn't bubble up). But any solution provided here on SO or elsewhere, e. g. adding code like this:
<ListBoxItem.Style>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListBoxItem.Style>
turned out to not solve the problem. So, I assume I do something wrong and I'm just too blind to see it. And while there might be solutions using code-behind, I prefer to stick with clean and pure XAML.
Please help me, understanding my mistake and solving it.
if the purpose is add ToolTip for ListBoxItem, you can use ItemContainerStyle. ListBox creates ListBoxItems for each databound item, adding ListBoxItem into
DataTemplate isn't necessary, if it breaks some functionality, try to avoid it
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="ToolTip">
<Setter.Value>
<Grid>
<TextBlock Text="{Binding .}"/>
</Grid>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding .}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
edit: I used Snoop app to check your variant with ListBoxItem in DataTemplate. There is 2 ListBoxItems in visual tree of each ListBox element, maybe one of prevent selection of another

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>

WPF: Simple MSWord-like color picker button

Is there a quick and easy way to implement a color picker button like the one in MS Word?
I want the user to choose from a number of predefined colors in a dropdown. The selected color should be displayed when the button is closed. When the user clicks the button, an event or command should be triggered. I want to use one button instance to set the text color in a RichTextBox, and another one to set the background color (similar to MS Word).
My first approach was to use a ComboBox with a DataTemplate which contains a button.
This does not work: The button's command is not triggered when the user clicks it. Instead, the dropdown of the ComboBox opens. I don't know the exact reason, but maybe the IsHitTestVisible=false attributes in the ComboBox default template? See: http://msdn.microsoft.com/en-us/library/dd334408%28v=vs.95%29.aspx
<UserControl x:Class="MyBrushPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Height="Auto" Width="Auto" MinWidth="50">
<UserControl.Resources>
<!--
Provide two different DataTemplates:
- SelectionBoxTemplate is used to display the selected color when the ComboBox is closed.
It contains a button which should be clickable (does not work).
- NormalItemTemplate is used to display a color in the opened ComboBox popup.
See:
http://stackoverflow.com/questions/2214696/wpf-how-to-customize-selectionboxitem-in-combobox
-->
<DataTemplate x:Key="NormalItemTemplate">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate">
<!-- This button cannot be clicked: PreviewMouseDown and Button_Click events are never triggered. -->
<Button Click="Button_Click" PreviewMouseDown="Button_PreviewMouseDown">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</Button>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<ComboBox IsReadOnly="True" IsEditable="False"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectableBrushes, Mode=OneWay}"
SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedBrush, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="50"
MaxDropDownHeight="200"
ItemTemplate="{StaticResource CombinedTemplate}"
>
<ComboBox.Resources>
<Style TargetType="ComboBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="200" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
</Style>
</ComboBox.Resources>
</ComboBox>
</UserControl>
Is there a way to make the button work correctly?
Are there any better approaches?
EDIT: Using the Snoop tool revealed that the IsHitTestVisible attribute of the unnamed ContentPresenter inside the ComboBox is actually set to false (ValueSource: ParentTemplate). If I set this property to true using Snoop, the button becomes clickable.
Can I change this property from a style?
At least, the following doesn't work:
<ComboBox.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="IsHitTestVisible" Value="True" />
</Style>
</ComboBox.Resources>
Assuming you are not just doing this as a learning exercise (which make using a library pointless)...
Take a look at: Extended WPF Toolkit - Color Picker
I've used it before for simple a plug-and-play WPF color-picker and it should solve your problem nicely.

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