WPF ListBoxItem IsMouseOver - c#

I have a ListBox, which on mousing over an item shows a delete button for that item. The problem is that the IsMouseOver triggers about 4 pixels into the highlighted item, so when mousing over multiple items, instead of the delete button seeming to move up and down with you, it flickers in the gaps between the items. Is there anyway to make IsMouseOver respond to the whole item?
<ListBox Name="lstLength" ItemsSource="{Binding Source={StaticResource lengths}}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" Height="22">
<Button DockPanel.Dock="Right" Name="btnDelete" Content="X" Tag="{Binding}" Click="DeleteLength" Visibility="Collapsed" />
<TextBlock Text="{Binding}" />
</DockPanel>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="btnDelete" Property="Visibility" Value="Visible" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Each one of your items will be contained within a ListBoxItem, this is what gives the ~4 pixels between each item. It also provides the highlight and selection styling. You can style the listBoxItem via the ListBox.ItemContainerStyle property. Move your trigger to the item container and it should work as desired.

You could use a DataTrigger directly on the button (or try to apply the same RelativeSource binding in the place it is):
<Style TargetType="{x:Type Button}">
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<!-- ... -->
</DataTrigger>
</Style>

Related

How to single click checkbox in datagrid nested inside a datagrid

I have nearly googled myself to death on this one. I see a lot of solutions to my problem, but none of them are working... I can only assume it is because I am nesting datagrids with checkboxes at the center. Right now my app takes two clicks to change the check state of a checkbox. I assume the first click is to get focus on the appropriate row? or cell and the second click activates the check state change.
Here is my XAML:
<Grid>
<DockPanel ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" VirtualizingPanel.IsContainerVirtualizable="True" >
<TextBox x:Name="textBoxSearch" DockPanel.Dock="Top" Margin="10" TextChanged="TxtFilter_TextChanged" Height="25" MinWidth="250" HorizontalAlignment="Stretch"/>
<DataGrid x:Name="objDatagrid" ItemsSource="{Binding DataView}" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False"
HeadersVisibility="None" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDetailsVisibilityMode="Visible"
VirtualizingPanel.VirtualizationMode="Recycling">
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="15 0 15 0" IsExpanded="True" HorizontalAlignment="Stretch">
<Expander.Header>
<!-- Control for the expander header text -->
<custom:HighlightTextBlock Text="{Binding Path=Name}"
HighlightPhrase="{Binding ElementName=textBoxSearch, Path=Text}"
HighlightBrush="Lime"/>
</Expander.Header>
<ItemsPresenter HorizontalAlignment="Stretch" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!-- Question Container Textblock. -->
<custom:HighlightTextBlock Text="{Binding QuestionText}" FontWeight="Bold"
HighlightPhrase="{Binding ElementName=textBoxSearch, Path=Text}"
HighlightBrush="Lime"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid x:Name="objInnerDatagrid" ItemsSource="{Binding Answers}" CanUserAddRows="False" CanUserDeleteRows="False"
HeadersVisibility="None" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<CheckBox DockPanel.Dock="Top" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Content>
<!-- Answer Checkbox Content : Textblock. -->
<custom:HighlightTextBlock Text="{Binding AnswerText}"
HighlightPhrase="{Binding ElementName=textBoxSearch, Path=Text}" HighlightBrush="Lime"/>
</CheckBox.Content>
</CheckBox>
<custom:TestUC Margin="20,10,0,0" HorizontalAlignment="Stretch" Visibility="Collapsed" x:Name="SubQuestionUserControl"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</DockPanel>
</Grid>
I have tried setting styles:
<UserControl.Resources>
<vm:StringToVisibilityConverter x:Key="StringToVisibilityConverter"/>
<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<!--<Style TargetType="custom:DataGridWithNavigation" BasedOn="{StaticResource {x:Type DataGrid}}"/>-->
<!--<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
</Style>-->
<!--<Style x:Key="dataGridStyle" TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>-->
<!--<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseEnter" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>-->
<!--<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>-->
<!--<Style TargetType="DataGridCell">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsReadOnly" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="IsEditing" Value="True" />
</MultiTrigger>
</Style.Triggers>
</Style>-->
</UserControl.Resources>
I no longer have the code behind for any of those associated events to show unfortunately.
I have also tried using someone's custom datagrid class references here:
https://stackoverflow.com/a/4827377/5807358
This custom class got me the closest. In fact it worked great... until I needed to un-check that checkbox (located in the inner datagrid). There was no way of changing the check state of an already checked box without first switching to another Row of the main datagrid and then back. I fiddled with trying to customize that class even further to get what I wanted but came up short.
I should also note that I've tried every solution on the stackoverflow link posted above.
Has anyone come across this before? I can post my code-behind if anyone thinks its relevant.
Thanks
I found my answer here! :
http://blog.ditran.net/wpf-datagrid-rowdetailstemplate-double-click-focus-fix/
I'll post the code in case the post ever gets removed:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="SelectRowDetailSection"/>
</Style>
</DataGrid.RowStyle>
Event Handler:
void SelectRowDetailSection(object sender, MouseButtonEventArgs e)
{
var row = sender as DataGridRow;
if (row != null)
{
row.Focusable = true;
row.Focus();
// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
// Change keyboard focus.
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
}
}
The style above needed to be added within the outter . When I added it to the UserControl.Resources section, it did nothing.
I should note that I had to add an additional style to the DataGridRow. With the above code when a row was selected, the row header disappeared. This is likely caused by something on my end. I am using mahapps.metro to style my windows. I bet that might be the culprit.

WPF radiobuttons IsHitTestVisible not working

I'm using radiobuttons inside a listbox and cannot disable them.
After searching I found links about IsEnabled and IsHitTestVisible. When I set IsEnabled to false the button and text turns grey however it's still clickable. Setting IsHitTestVisible does nothing to change this. My XAMLcode looks like this:
<ListBox ItemsSource="{Binding Source}" BorderThickness="0" VerticalAlignment="Stretch" Background="Transparent" SelectedItem="{Binding SelectedSource, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton IsHitTestVisible="{Binding IsEnabled}" IsEnabled="{Binding IsEnabled}" Margin="0 2 0 0" FontSize="12" FontFamily="Segoe UI Regular" Content="{Binding name}" GroupName="GroupList" >
<RadioButton.Style>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
</Style>
</RadioButton.Style>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Property in c# that calculates and returns bool.
public bool IsEnabled
{
get
{
IsEnabledCalculate();
return isEnabled;
}
}
Any ideas about how to disable selection?
Using IsHitTestVisible on a RadioButton in ListBox was a good start. This way, when you click the RadioButton, the event goes "through" it and is handled by ListBoxItem (the item gets selected). You can then manipulate IsChecked property by binding it do IsSelected property of the ListBoxItem.
This is however still not enough to achieve waht you want. First, you're binding your IsEnabled property on the wrong level: the RadioButton. As you noticed, this still allows the ListBoxItem to get selected. So, you need to bind your IsEnabled property to the IsEnabled property of ListBoxItm instead:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Path=IsEnabled}"/>
</Style>
</ListBox.ItemContainerStyle>
There still is one minor issue here. When your IsEnabled property is changed in the codebehind, the ListBoxItem will get disabled but the RadioButton will still be checked. To prevent this and uncheck the RadioButton whenever the item gets disabled you can use a DataTrigger:
<DataTemplate>
<RadioButton GroupName="GroupList" IsHitTestVisible="False"
Margin="0 2 0 0" FontSize="12" FontFamily="Segoe UI Regular"
Content="{Binding name}" >
<RadioButton.Style>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>
</DataTemplate>

ListView with RadioButton in ItemTemplate does not update SelectedItem

I have a Listview with RadioButtons inside the ItemTemplate:
<ListView ItemsSource="{Binding }">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton IsChecked="{Binding Choice1}" />
<RadioButton IsChecked="{Binding Choice2}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It is possible to select the current item on the "background". But if I click a RadioButton the SelectedItem is not set.
Desired behavior: the ListViewItem is selected whenever a "childitem" is clicked. Like Outlook when you set a flag, the current mail is selected.
Do I have to bubble up the click event somehow?
This behavior comes from the fact, that the IsKeyboardFocusWithin-Property of the ListViewItem is false in that case. While you are clicking the RadioButton, the RadioButton has the Keyboard-Focus.
Solution
Add a simple trigger like this
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
Complete Sample
<ListView ItemsSource="{Binding }">
<ListView.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton IsChecked="{Binding Choice1}" />
<RadioButton IsChecked="{Binding Choice2}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Note
Be sure you choose the right TargetType (ListViewItem, ListBoxItem or TreeViewItem)
Hope this helps

ListBox Loading Animation

I have a list box that should be filed when user click on Button,
sometimes the data loaded is quickly, and sometimes it take some time... Is there a simple way to load some animation like a clock, or something which can give the user indication that the process is running?
I use mvvm with button commands
<ListBox Width="30"
Visibility="{Binding IsDataLoaded,
Converter= {StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding Collection}"
FontSize ="15"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DarkGray" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Content="Go" Command="{Binding GoCommand}" IsEnabled="{Binding IsGoEnabled}" IsDefault="True" Width="60"
/>
Sounds like you're looking for a BusyIndicator like in the Extended Toolkit.

How can I know if item is selected inside the ItemTemplate?

I have a ListBox that use my custom ItemTemplate. I want to set Visibility property in my TextBlock (inside my template) depending on selected item. I think of doing it using triggers. But how can I know inside my template if current item is selected or not?
<DataTemplate x:Key="myTemplate">
<StackPanel Orientation="Horizontal">
<Image Tag="{Binding priority}" Loaded="SetIconPriority"/>
<Image Tag="{Binding alarm}" Loaded="SetIconAlarm"/>
<!-- I want this TextBlock to be visible only when item is selected -->
<TextBlock Text="{Binding description}"/>
</StackPanel>
</DataTemplate>
edit:
It works, thanks! Code:
<TextBlock Grid.Column="2" Grid.Row="1" Text="{Binding opis}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Using a RelativeSource binding with AncestorType being ListBoxItem.
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
(May want to reverse the logic and Collapse on False instead, avoids the default value Setter)

Categories