WPF Listbox Show Button in ItemTemplate on MouseOver - wpf

I have a listbox containing and image and a button. By default the button is hidden. I want to make the button visible whenever I hover over an item in the listbox.
The XAML I am using is below. Thanks
<Window.Resources>
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="6">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=FullPath}" Height="150" Width="150"/>
<Button x:Name="sideButton" Width="20" Visibility="Hidden"/>
</StackPanel>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>

Ok, try this in your button declaration:
<Button x:Name="sideButton" Width="20">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
So I'm using a style with a trigger to look back up the visual tree until I find a ListBoxItem, and when its IsMouseOver property flips over to True I set the button's visibility to Visible.
See if it's close to what you want.

This Style does what you need. On mouse over, the button becomes only visible when the pointer is over the ListBoxItem. The special trick is to bind to the TemplatedParent for reaching IsMouseOver and use TargetName on the Setter to only affect the Button.
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="6">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=FullPath}"
Height="150"
Width="150" />
<Button x:Name="sideButton"
Width="20"
Visibility="Hidden" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource TemplatedParent}}"
Value="True">
<Setter Property="Visibility"
TargetName="sideButton"
Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

#David is showing the right way,
But I have one suggestion to your XAML architecture. If you don't have any DataBinding on the Button it is better to put that in to the ListBoxItem style than the DataTemplate as bellow.
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="6">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=FullPath}"
Height="150"
Width="150" />
</StackPanel>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid Background="Transparent">
<Button x:Name="sideButton" Width="20" HorizontalAlignment="Right" Visibility="Hidden" />
<ContentPresenter/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility"
TargetName="sideButton"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

One solution to find what item was clicked is to add the following Event setter
XAML
C#
void ListBoxItem_MouseEnter(object sender, MouseEventArgs e)
{
_memberVar = (sender as ListBoxItem).Content;
}

Just wondering, if we use the technique above, how do we determine what item the button was clicked on?
To answer Brian's question, in the button click handler you can walk up the visual tree to find the item that contains the button:
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is ListBoxItem))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep != null)
{
// TODO: do stuff with the item here.
}

Related

WPF apply triggers to listview items to change background

I am trying to create a sort of left menu for navigation inside the desktopapplication. My idea is to use Buttons as Listview items which should behave in this way: when i hover with the mouseover them theri background should change color (becomes darkCyan) and when i click one its background color should change persistently (to darkCyan) until i click on another button of the list. The problem is that i am using a the DataTemplate property to specify how the buttons should look like and I am tryin to apply the triggers to change the background color on the ControlTemplate of the ListView. The result is that sometimes the background color changes but the command related to the button is not fired other times the contrary. I think that I am doing the things in the wrong element of the tree view, but I don't have enough knowledge of the tree view so I am not understanding what I am doing wrong. Here is the code of the XAML in which i define the styles for the Buttons and the ListView
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:TripViewModel}">
<views:TripView />
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Height"
Value="50" />
<Setter Property="Background"
Value="#555D6F" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Grid Background="Transparent">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And here is the code in which I create the ListView
<ListView Name="MenuButtons"
ItemsSource="{Binding PageViewModels}"
Background="Transparent"
IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Anyone can help?
Thanks
I have solved the issue by using a ListBox instead of a ListView and setting the ItemContainer to be a button in the following way
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>

Merge Style.Triggers with property Template in WPF

I want to set a style for my DataGrid, but I do not know where is the problem
the backgroud property does not work with its value in the presence of the Template property.
my code:
<Style x:Key="DataGridStyle1" TargetType="{x:Type DataGrid}">
<Setter Property="CellStyle" Value="{DynamicResource GridStyle1}"/>
</Style>
<Style x:Key="GridStyle1" TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="Background" Value="SeaGreen"/>
</Trigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Name="DataGridCellBorder">
<ContentControl Content="{TemplateBinding Content}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Background="Transparent" TextWrapping="WrapWithOverflow" TextTrimming="CharacterEllipsis"
Height="auto" Width="auto" Text="{Binding Text}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Help me please.
You have explicitly set TextBlock background to Transparent, so it won't pick value from DataGridCell. You should bind with background of DataGridCell using RelativeSource like this:
<TextBlock Background="{Binding Background, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=DataGridCell}}"
TextWrapping="WrapWithOverflow" TextTrimming="CharacterEllipsis"
Height="auto" Width="auto" Text="{Binding Text}"/>

I just need a simple WPF ToolTip Style that displays multiple lines

Due to all the noise about fancy, super, huge, and blah, blah, blah, tooltips, I cannot find the answer.
I just need a simple style that sets TextWrapping="Wrap" and allows me to set a width.
One that duplicates the existing / default style, but just word wraps.
<Window.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Rectangle Width="100" Height="100" Fill="Red">
<Rectangle.ToolTip>
<ToolTip Width="100">
This is some text with text wrapping.
</ToolTip>
</Rectangle.ToolTip>
</Rectangle>
</Grid>
This example is assuming you want to be able to set the width on a per-usage basis. If you want to set it as part of the style, add it to the TextBlock element.
This style prevents a tooltip from popping up on empty strings.
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<TextBlock Text="{TemplateBinding Content}"
MaxWidth="400"
TextWrapping="Wrap"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
Or using ContentTemplate:
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding}"
MaxWidth="400"
TextWrapping='Wrap' />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
If you just want to get the effects below, have a read at this post.

Using styles within a data template

I have a class called item, and it contains just two properties. I will display them on the screen as buttons with a style. This question relates to how I can style the button based on the IsSelected value, when the element I want to affect is in the style not the data template. I have already tried with a Trigger but been unable to get it to work.
The class is below.
public class Item : ObservableObject
{
private string _title;
private bool _isSelected;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
I use a data template to display these items in a ItemsControls.
<ItemsControl ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource ResourceKey=ItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Using the following style and data template.
<Style x:Key="ItemButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="ButtonBorder" BorderThickness="2,2,2,0" BorderBrush="#AAAAAA" CornerRadius="6,6,0,0" Margin="2,20,0,0" Background="Black">
<ContentPresenter
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="ItemTemplate">
<Button Height="60" Style="{StaticResource ItemButton}" Name="Button">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Title}"
HorizontalAlignment="Left" Margin="5,5,5,3" FontSize="25" Foreground="#6B6B6B" FontFamily="Arial" />
<Button Style="{StaticResource NoChromeButton}" Margin="0,0,5,0">
<Button.Content>
<Image Height="20" Source="/WpfApplication1;component/Image/dialogCloseButton.png"></Image>
</Button.Content>
<Button.ToolTip>
Close
</Button.ToolTip>
</Button>
</StackPanel>
</Button>
</DataTemplate>
I need to change the Background of "ButtonBorder" from Black to White when IsSelected is True, on the object Item.
I have added in a Trigger in the Data Template
This does not work, I guess its because the style overrides the DataTemplate, thus the background stays white. Yet when i try to do a trigger in the style, I can't access the property IsSelected?
DataTemplate Trigger
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="Button" Property="Background" Value="White"/>
</DataTrigger>
</DataTemplate.Triggers>
Style trigger
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
Am i missing something?
Make your ButtonBorder.Background be {TemplateBinding Background}, which means it will use whatever background color is assigned to the templated Button, then you can change your Button's background based on a Trigger
<Style x:Key="ItemButton" TargetType="Button">
<Setter Property="Background" Value="Black" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="ButtonBorder" Background="{TemplateBinding Background}" ... >
<ContentPresenter ... "/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SelectableItemButton" TargetType="Button" BasedOn="{StaticResource ItemButton}">
<Setter Property="Background" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="ItemTemplate">
<Button Height="60" Style="{StaticResource SelectableItemButton}">
...
</Button>
</DataTemplate>
I'm also making a SelectableItemButton Style which inherits from ItemButton, and just implements the trigger
Shouldn't the target be "ButtonBorder" instead of "Button" in :
<Setter TargetName="Button"....
Also, to access the property IsSelected you need to set the TargetType in the style ....

combo box inside a user control disappears when style is applied in wpf

I'm trying to apply a style to a combo box but instead of getting applied the combo box itself disappears. Please check the following xaml code for user control.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna"
x:Class="Guardian.PAS.PASFramework.UI.WPF.PASComboBox"
xmlns:local="clr-namespace:Guardian.PAS.PASFramework.UI.WPF"
Height="26" Width="100" VerticalAlignment="Center" >
<UserControl.Resources>
<Style x:Key="comboBoxStyle" TargetType="{x:Type local:PASCustomComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PASCustomComboBox}">
<ControlTemplate.Triggers>
<Trigger Property="local:PASCustomComboBox.IsEnabled" Value="false">
<Setter Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Canvas Name="canvas" Height="23" Width="Auto" VerticalAlignment="Center">
<Label Height="23" Name="lblCaption" Width="20" VerticalAlignment="Center">aaa</Label>
<local:PASCustomComboBox Height="23" x:Name="cmbComboBoxControl" VerticalAlignment="Center" Width="50"
IsEditable="True" Style="{StaticResource comboBoxStyle}">
</local:PASCustomComboBox>
<Button Height="23" Name="btnSearch" Width="25" Click="btnSearch_Click" Visibility="Collapsed"
VerticalAlignment="Center">...</Button>
<Label Height="23" Name="lblDescription" VerticalAlignment="Center" Width="20" Foreground="Blue">
</Label>
</Canvas>
</UserControl>
Here PASCustomComboBox is a class which inherites from combo box.
public class PASCustomComboBox : ComboBox
{
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Down || e.Key == Key.Up)
{
e.Handled = true;
return;
}
base.OnPreviewKeyDown(e);
}
}
The problem is that you are redefining the ControlTemplate without any visual tree in it:
<Style x:Key="comboBoxStyle" TargetType="{x:Type local:PASCustomComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PASCustomComboBox}">
<ControlTemplate.Triggers>
<Trigger Property="local:PASCustomComboBox.IsEnabled" Value="false">
<Setter Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You'll want the triggers on the style instead of the controltemplate:
<Style x:Key="comboBoxStyle" TargetType="{x:Type local:PASCustomComboBox}">
<Style.Triggers>
<Trigger Property="local:PASCustomComboBox.IsEnabled" Value="false">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
You dont specify any visual elements in the control template of your style, just a trigger.
It will render the empty template iirc.
Better edit the Trigger collection of the ComboBox in your style to add that trigger and you will keep the default ControlTemplate.

Resources