Focusing Listbox item when a control is clicked inside datatemplate - wpf

I have a button and on click of button a popup opens up which has a listbox. The items inside the listbox is templated and the template contains a date picker and 2 textboxes to enter date, the hour and minute.
The issue I'm facing with this is this - what ever I do on my datepicker or textboxes, the listbox item does't get focus unless I somehow explicitly click on the spaces btw the controls. Question is-how do i set the listbox item to be the selected item when any of the controls inside the datatemplate is clicked/focused? Please note that I follow MVVM.
Code:
ListBox:
<ListBox x:Name="AsOfList" SelectedItem="{Binding SelectedAsOfDate}" ItemsSource="{Binding Path=AsOfDates}"/>
DataTemplate:
<DataTemplate DataType="{x:Type Domain:UserDefinedDate}">
<DatePicker DockPanel.Dock="Left" Name="AsOfDate" Width="115" SelectedDate="{Binding SelectedDate,
UpdateSourceTrigger=PropertyChanged}" SelectedDateFormat="Short" FirstDayOfWeek="Monday" />
</DataTemplate>
Button:
<Button Grid.Row="1" Grid.Column="1" Height="25" HorizontalAlignment="Center" VerticalAlignment="Center" Name="SelectedDate" Click="SelectedDate_Click"
ToolTip="{Binding Path=AsOfDateViewModel.ToolTip}">
<Button.Content>
<DockPanel>
<TextBlock Text="{Binding Path=AsOfDateViewModel.DisplayText}" />
<Image Height="10" Width="10" Source="Down.png" />
</DockPanel>
</Button.Content>
</Button>
Popup:
<Popup x:Name="DateSelectorPopUp" PlacementTarget="{Binding ElementName=SelectedDate}" StaysOpen="False"
IsOpen="{Binding Path=AsOfDateViewModel.IsOpen}" >
<Views:AsOfDateSelector Width="Auto" DataContext="{Binding Path=AsOfDateViewModel}"/>
</Popup>

You can start BooleanAnimationUsingKeyFrames animation to set IsSelected when GotKeyboardFocus is triggered on ListBoxItem:
<ListBox x:Name="AsOfList" SelectedItem="{Binding SelectedAsOfDate}" ItemsSource="{Binding Path=AsOfDates}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<EventTrigger RoutedEvent="GotKeyboardFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

WPF Popup in a DataGrid only closes when clicking on the datagrid header but not when clicking elsewhere

I'm working on a WPF User Interface. I have a Popup in a DataGrid which is opened on the "MouseLeftButtonDown" event of a TextBlock and asks if the user is sure to delete the row. Until then it all works fine.
The problem is that the Popup only closes if I click on the header row of the datagrid. If I click elsewhere the popup does not close. The Property "StaysOpen" is already set false.
If I use the same XAML Code outside a Datagrid everything works fine.
How can I achieve that the popup closes?
btw I'm using MVVM pattern, so it would be great if solution is integrated in XAML Code.
My XAML Code:
<UserControl.Resources>
<ResourceDictionary>
<Storyboard
x:Key="OpenDeleteReadingPopup"
BeginTime="0:0:0">
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="DeleteReadingChoicesPopup"
Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame
KeyTime="00:00:00"
Value="true" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel x:Name="OuterElement">
<toolkit:DataGrid
ItemsSource="{Binding Readings}"
SelectedItem="{Binding SelectedReading}"
IsReadOnly="False">
<toolkit:DataGrid.Columns>
<!-- other column-->
<toolkit:DataGridTemplateColumn>
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock
x:Name="DeleteReadingTextBlock"
Margin="0 1 0 0"
FontFamily="Marlett"
Text="r"
HorizontalAlignment="Right"
VerticalAlignment="Stretch">
<TextBlock.Triggers>
<EventTrigger
RoutedEvent="MouseLeftButtonDown">
<BeginStoryboard
Storyboard="{StaticResource OpenDeleteReadingPopup}" />
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
<Popup
AllowsTransparency="True"
x:Name="DeleteReadingChoicesPopup"
StaysOpen="False"
Placement="Top"
PlacementTarget="{Binding ElementName=DeleteReadingTextBlock}">
<StackPanel
Background="{DynamicResource WindowBackgroundColorBrush}"
HorizontalAlignment="Center">
<Button
x:Name="Button"
cal:Action.TargetWithoutContext="{Binding ElementName=OuterElement, Path=DataContext}"
Style="{DynamicResource DeleteButtonStyle}"
cal:Message.Attach="DeleteReading($dataContext)"
Content="{lex:Loc Sure}"/>
</StackPanel>
</Popup>
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
</StackPanel>

Storyboard in DataTemplate

I have a ToggleButton that open a popup, and have a ItemsControl in Popup.
I want to hide popup when click on items in items control.
<ToggleButton Content="?????" x:Name="LeaveButton" Style="{StaticResource ToggleButtonImageStyle}" Padding="13"/>
<Popup
KeyDown="UIElement_OnKeyDown"
Opened="SubMenuPopup_OnOpened"
IsOpen="{Binding IsChecked, ElementName=LeaveButton}"
StaysOpen="False"
x:Name="LeavePopup"
AllowsTransparency="True"
PopupAnimation="Fade"
PlacementTarget="{Binding ElementName=LeaveButton}"
Placement="Right">
<StackPanel Orientation="Horizontal" Margin="15">
<Polygon Points="15 15,0 30,15 45" Fill="{DynamicResource HeaderBackgroundBrush}" />
<StackPanel Width="250">
<ItemsControl ItemsSource="{Binding WorkshopList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Name}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.LeaveCommand}"
CommandParameter="{Binding Id}"
Style="{StaticResource ButtonImageTextStyle}"
Padding="20">
<Button.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click">
<BeginStoryboard Storyboard="{StaticResource HideLeavePopup}" />
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Popup>
and set a story for this.
<Storyboard x:Key="HideLeavePopup" Storyboard.TargetName="LeaveButton" Storyboard.TargetProperty="IsOpen">
<BooleanAnimationUsingKeyFrames>
<DiscreteBooleanKeyFrame KeyTime="00:00:00.1" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
but when I use this, I get following error
LeaveButton name can not be found in the name scope of type 'System.Windows.Control.Button'
Did you try this?
Inside of your storyboard, Instead of
Storyboard.TargetName="LeaveButton"
use
Storyboard.Target="{Binding ElementName=LeaveButton}"
It depends where Storyboard is defined so I'll assume it's in Window.Resources or something alike. You use TargetName as LeaveButton and TargetProperty as IsOpen. You want either LeaveButton and IsChecked or LeavePopup and IsOpen. Also try changing TargetName to Target and use binding:
<Storyboard x:Key="HideLeavePopup"
Storyboard.Target="{Binding ElementName=LeaveButton}"
Storyboard.TargetProperty="IsChecked">
<BooleanAnimationUsingKeyFrames>
<DiscreteBooleanKeyFrame KeyTime="00:00:00.1" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>

Close popup from child button's press?

I have a Popup that contains a "close" button. The popup is opened by a toggle button (its IsOpen property is bound to a ToggleButton as provided by this answer). How can I close the popup when the button is pressed? This is my XAML:
<Canvas x:Name="LayoutRoot">
<ToggleButton x:Name="ToggleButton"
Style="{DynamicResource ToggleButtonStyle}" Height="51" Canvas.Left="2.999" Width="50.333" IsHitTestVisible="{Binding ElementName=Popup, Path=IsOpen, Mode=OneWay, Converter={StaticResource BoolInverter}}"/>
<Popup x:Name="Popup" IsOpen="{Binding IsChecked, ElementName=ToggleButton}" StaysOpen="False" AllowsTransparency="True">
<Canvas Height="550" Width="550">
<Grid Height="500" Width="500" Canvas.Left="25" Canvas.Top="25" d:LayoutOverrides="Width, Height, Margin">
<Grid.Effect>
<DropShadowEffect BlurRadius="15" ShadowDepth="0"/>
</Grid.Effect>
<Grid.RowDefinitions>
<RowDefinition Height="0.132*"/>
<RowDefinition Height="0.868*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Background" Fill="#FFF4F4F5" Margin="0" Stroke="Black" RadiusX="6" RadiusY="6" Grid.RowSpan="2"/>
<Border x:Name="TitleBar" BorderThickness="1" Height="70" VerticalAlignment="Top" Margin="0,0.5,0,0" CornerRadius="5">
<DockPanel>
<TextBlock TextWrapping="Wrap" Text="FOOBAR POPUP TITLE" FontSize="24" FontFamily="Arial Narrow" Margin="17,0,0,0" d:LayoutOverrides="Height" VerticalAlignment="Center" FontWeight="Bold"/>
<Button x:Name="CloseButton" Content="Button" VerticalAlignment="Center" DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="0,0,13,0" Style="{DynamicResource CloseButtonStyle}"/>
</DockPanel>
</Border>
<Border BorderThickness="1" Height="413" Grid.Row="1" Background="#FF2F2F2F" Margin="12">
<Rectangle Fill="#FFF4F4F5" RadiusY="6" RadiusX="6" Stroke="Black" Margin="12"/>
</Border>
</Grid>
</Canvas>
</Popup>
</Canvas>
A better approach than code behind is to use an event trigger on the button click event:
<Button>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsChecked" Storyboard.TargetName="ToggleButton">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
Disclaimer: I haven't run this code through VS so it might have a typo or 2
Other answers didn't work for me, because I was using a DataTemplate for the buttons inside the popup. After lot's of searching I found that I should use Storyboard.Target instead of Storyboard.TargetName. Otherwise the x:Name was not found and there was some namespace exception.
<ToggleButton x:Name="MyToggleButtonName" Content="{Binding MyToggleButtonString}"/>
And later inside the Popup that has a ListBox which is populated from some ItemsSource:
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name, Mode=OneWay}"
Command="{StaticResource MyCommandThatTakesAParameter}"
CommandParameter="{Binding Name}">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsChecked" Storyboard.Target="{Binding ElementName=MyToggleButtonName}">
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
This way it is possible to get a somewhat working ComboBox which can execute commands with the buttons inside it. (A normal ComboBox can't launch commands for some odd reason.)
One way of doing it is to add event handler for your CloseButton:
<Button x:Name="CloseButton" Click="OnButtonClick" Content="Button" VerticalAlignment="Center" DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="0,0,13,0" Style="{DynamicResource CloseButtonStyle}"/>
And in OnButtonClick event handler set state of your
TuggleButton.IsChecked = false;
I have't tested code in VS, so there might be some typos

Items collection must be empty before using ItemsSource in Silverlight

Inside custom control I'm trying to bind my ItemsControl.ItemsSource and get error. Here is how template looks:
<Style TargetType="controls:IdattFilterBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:IdattFilterBox">
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="PART_ItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Caption}" />
<ComboBox Grid.Column="1">
<ComboBoxItem Content="Contains" />
<ComboBoxItem Content="Begins with" />
<ComboBoxItem Content="Ends with" />
</ComboBox>
<TextBox Grid.Column="2" Text="{Binding FieldFilter1, Mode=TwoWay}" />
<TextBox Grid.Column="3" Text="{Binding FieldFilter2, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<Border Grid.ColumnSpan="2" x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>true</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
<Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
<Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
</Grid>
</Border>
</ItemsControl>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In code I' trying to set this PART's ItemSource:
this.WrapperItemsControl.ItemsSource = filterData;
On this line I get error(error message in subject). Why can't I set ItemsSource and what this error means? I want controls in DataTemplate to bind to objects that stored in filterData.
EDIT:
PART_ItemsControl is what my WrapperItemsControl
this.WrapperItemsControl = GetTemplateChild(PartItemsControl) as ItemsControl;
EDIT2:
Screenshot showing that there is one item (Border?) Where does it come from?!
EDIT3
DOH! I placed validation border in a wrong spot
You can't use ItemsSource if you manually add items to your ItemsControl. For example, you would get an error if you tried this:
<ItemsControl ItemsSource="{Binding MyItems}">
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
</ItemsControl>
You may think you're not doing that, but you actually are. You're adding a single item to your ItemsControl, and that item is of type DataTemplate. Look:
<ItemsControl ...>
<DataTemplate ... />
That syntax means "create a DataTemplate and add it to the ItemsControl's Items property". (Items is the default property for ItemsControl, so any elements you nest under ItemsControl, if you don't otherwise specify, get added to Items.)
I think you intended to assign that DataTemplate to the ItemsControl's ItemTemplate property, rather than adding it to Items. Try this instead:
<ItemsControl ...>
<ItemsControl.ItemTemplate>
<DataTemplate ... />
</ItemsControl.ItemTemplate>
</ItemsControl>

Item rendered via a DataTemplate with any Background Brush renders selection coloring behind item

I have a ListBox which uses a DataTemplate to render databound items. The XAML for the datatemplate is as follows:
<DataTemplate x:Key="NameResultTemplate">
<WrapPanel x:Name="PersonResultWrapper" Margin="0" Orientation="Vertical" Background="{Binding Converter={StaticResource NameResultToColor}, Mode=OneWay}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand x:Name="SelectPersonEventCommand" Command="{Binding Search.SelectedPersonCommand, Mode=OneWay, Source={StaticResource Locator}}" CommandParameter="{Binding Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock x:Name="txtPersonName" TextWrapping="Wrap" Margin="0" VerticalAlignment="Top" Text="{Binding PersonName}" FontSize="24" Foreground="Black" />
<TextBlock x:Name="txtAgencyName" TextWrapping="Wrap" Text="{Binding AgencyName}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0" FontStyle="Italic" Foreground="Black" />
<TextBlock x:Name="txtPIDORI" TextWrapping="Wrap" Text="{Binding PIDORI}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0" FontStyle="Italic" Foreground="Black" />
<TextBlock x:Name="txtDescriptors" TextWrapping="Wrap" Text="{Binding DisplayDescriptors}" Margin="0" VerticalAlignment="Top" Foreground="Black"/>
<Separator Margin="0" Width="400" />
</WrapPanel>
</DataTemplate>
Note that there is a value converter called NameResultToColor which changes the background brush of the rendered WrapPanel to gradient brush depending on certain scenarios.
All of this works as I'd expect, except when you click on any of the rendered ListBox items. When you click one, there is only the slightest sign of the selection coloring (the default bluish color). I can see a trace bit of it underneath my gradient-brushed item. If I reset the background brush to "no brush" then the selection rendering works properly. If I set the background brush to a solid color, it also fails to render as I'd expect.
How can I get the selection coloring to be on top? What is trumping the selection rendering?
The problem is that your item's template is being drawn over the selection being drawn by the ListBoxItem. If you want to ensure that the color is kept, you can add a DataTrigger to set the background of the WrapPanel to null when the item is selected:
<DataTemplate x:Key="NameResultTemplate">
<WrapPanel x:Name="PersonResultWrapper">
...
</WrapPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter TargetName="PersonResultWrapper" Property="Background" Value="{x:Null}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Resources