Excess border selection in WPF's Lisbox [duplicate] - wpf

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>

Related

WPF TextBox BorderBrush Style vs. No Style

Why does a border appear around a TextBox when a Style is used as opposed to no Style being used? Is there a way to set this using a Style so no border appears, like in the "Phone" Grid/TextBox? See below:
WINDOW XAML
<!-- City, State, Zip -->
<Grid Grid.Column="0" Grid.Row="2" Background="White" Margin="50,2,25,2">
<Viewbox>
<TextBox Style="{Binding StandardTextBox1}" Text="{Binding CityStateZip}"/>
</Viewbox>
</Grid>
<!-- Phone -->
<Grid Grid.Column="0" Grid.Row="3" Background="White" Margin="50,2,25,2">
<Viewbox>
<TextBox
Background="Transparent"
BorderBrush="Transparent"
Text="{Binding FacilityPhoneMain}"
TextAlignment="Center"
FontSize="14"
/>
</Viewbox>
</Grid>
STYLE XAML
<!-- Standard TextBox 1 -->
<Style TargetType="TextBox" x:Key="StandardTextBox1">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="FontSize" Value="14"/>
</Style>
You can modify the BorderThickness property of the TextBox in your Style to remove the border.
As for why the border is not affected in the Style, I have a hunch that this is due to dependency property precedence.
Animations have a higher precedence than property setters. Due to the default WPF Textbox template having animations that affect the BorderBrush property, the setters are not being applied.
This link has some relevant information
To quote: "Animation values always take precedence over local values, setters and triggers"

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

Change DockPanel's background to match the disabled ListView's background color

I have a ListView inside a DockPanel. I need the ListView to be stretched horizontally and centered vertically inside the DockPanel. When the ListView is disabled (IsEnabled=false) I need to change the DockPanel's background to match the disabled ListView's background color.
So, basically I want to avoid the situation that can be seen on the picture below.
<Window>
<DockPanel IsEnabled="False">
<ListView IsEnabled="False" VerticalAlignment="Center" HorizontalContentAlignment="Center">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>
However, I don't want to explicitly declare colors anywhere in my code, because I don't know what kind of background color will a ListView use in different Windows environments (I'm not sure, but I guess different Windows theme/color settings can alter the default background color of the ListView - which potentially could be different from the color that I don't want to explicitly declare).
Binding the ListView's background color to the DockPanel's background color doesn't work.
So, right now I'm using the following workaround.
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=parametersListView, Path=IsEnabled}" Value="False">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
I've extracted the resource key that my ListView's using in its ControlTemplate. On the template there is a Trigger declared on the IsEnabled property and it sets the control's background color to the color represented by the SystemColors.ControlBrushKey key.
This seems to work, but I'm not sure if this is the right way to do this.
Is there a way to do this in a more robust fashion, or this is the best that I could do?
(On a side note: using SystemColors.ControlBrush instead of SystemColors.ControlBrushKey produces a different shade of grey, I'm not sure why.)
Why not use LstFilledChild =true
Edit:
If you name your element and use the ElementName in the binding it should work.
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{Binding Background, ElementName=myList}">
<ListView Name="myList" IsEnabled="False" HorizontalAlignment="Center" HorizontalContentAlignment="Center" >
<ListViewItem>AAA</ListViewItem>
</ListView>
</DockPanel>
Since I've asked my question the application I was working on is live and it's using the solution I've originally come up which is the following. It works well for the moment.
<Window>
<DockPanel IsEnabled="False">
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=IsEnabled}"
Value="False">
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<ListView IsEnabled="False"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
x:Name="myListView"
x:FieldModifier="private">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>

Focus on Button inside ListBoxItem without code

I have a WPF application with ListBox that display list of items.
Each item has IsChecked property.
I have change the style of the ItemContainerStyle of the list box as follos:
<Style x:Key="OrgListItemStyle" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ToggleButton IsChecked="{Binding IsChecked}">
<ContentPresenter />
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that the focus, when navigating with the keyboard, is on the ListBoxItem and not the ToggleButton itself which makes it not intuitive to work with.
How ca I change the focus so it will be right on the button and not the ListBoxItem - preferable not with code.
Thank you,
Ido
You could set Focusable to false for the ListBoxItems in your Style:
<Setter Property="Focusable" Value="False"/>
Note that ListBox has some magic to remember the element that had keyboard focus that won't work if you don't allow the ListBoxItem to be selected.
If you don't want to use the features of the ListBox at all, then you may be better off using a plain ItemsControl rather than a ListBox. You would need to use a DataTemplate rather than a ControlTemplate since there is no container control:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding IsChecked}" Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

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