I have got a ListBox.ItemTemplate that has got a ToggleButton and inside of it a checkbox and a ProgressBarEdit from devexpress.
Then I have a style that targets the checkbox. When the mouse is over the checkbox it changes its opacity.
The double animation and the binding works fine, however the listbox itemssource is bound and I would like to make all the checkboxes opacity change when the mouse is over any checkbox and not just one.
I tried to bind Storyboard.TargetProperty to my viewmodel property but it is not a dependency property so I cannot do it.
This is the animation:
<Style x:Key="FadeOutButton" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=chkImport}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="Opacity"
To="0.02" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
This is part of the ListBox control:
<ListBox x:Name="FileDetailsListBox"
Grid.Column="2"
BorderThickness="0"
ItemsSource="{Binding FileDetailsList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton x:Name="FileToggleButton"
Margin="-1,0,-1,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
BorderThickness="0,0">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<CheckBox x:Name="chkImport"
Margin="0,0,0,3"
HorizontalContentAlignment="Center"
IsChecked="{Binding Import,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Opacity="{Binding DataContext.ImportOpacity,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ElementName=ImportView}"
Style="{StaticResource FadeOutButton}" />
<dxe:ProgressBarEdit Grid.Row="2"
Height="20"
Margin="-3,0,-3,0"
EditValue="{Binding ProgressValue}"
ShowBorder="False"
StyleSettings="{Binding IsMarquee,
Converter={StaticResource conv}}" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
How can I change all the checkboxes opacity from the Listbox when Control.MouseEnter is triggered?
I thought the way to go was binding the ImportOpacity property from my viewmodel in the style, but the animation does not allow it.
Thanks
Update:
I cannot post images because I am new here, but I would like to change the Opacity for all the checkboxes created by the datatemplate when the mouse enter in any checkbox. The animation works fine for individual checkboxes like the code does at the moment.
My viewmodel has got a property called "ImportOpacity" and it is bound to the checkbox.opacity (you can see it in the xaml above) My idea was to use this property to make the Opacity from 0 to 100 when the mouse is over any checkbox.
Instead of animate the Opacity property of the CheckBox and bind it to ImportOpacity with TwoWay mode, I think you should animate the ImportOpacity property in your ViewModeldirectly.
Here is a reference about how to animate property in ViewModel.
Related
I have a problem with an animation within a template of an ItemsControl that is part of the template of another ItemsControl. I want a path, that is representing an Icon, to change its color and constantly rotate when a certain condition becomes true.
The DataTrigger generally works, causing the Fill of the Path changing from Gray to LightGreen when the producing Property changes to true. However, the animation does not start. When I let the animation start with the Loaded Event (as you can see in the commented section), it starts properly. So I know that the animation, as well as the DataTrigger, is configured correctly.
When I put the same Path (just copy and paste) in the outside ItemsControl and change the DataTrigger to a Property of the corresponding DataType, the animation also works as expected. So there seems to be a problem with the nested ItemsControls, but I have no idea what it might be.
<ItemsControl ItemsSource="{Binding Computers}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:ClientComputerWrapper}">
<Border Margin="5" BorderBrush="Black" BorderThickness="1" Padding="5">
<StackPanel>
<!-- Some Content -->
<ItemsControl ItemsSource="{Binding PlcReaderStatuses}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type resources:PlcReaderStatusResource}">
<DockPanel LastChildFill="False">
<!-- Some Content -->
<Path DockPanel.Dock="Left" HorizontalAlignment="Left" Data="{StaticResource GearIconGeometry}" Stretch="Uniform" RenderTransformOrigin="0.5, 0.5">
<Path.RenderTransform>
<RotateTransform x:Name="gearPathTransform"/>
</Path.RenderTransform>
<Path.Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="Gray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsProducing}" Value="True">
<Setter Property="Fill" Value="LightGreen"/>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="rotateStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)" To="360" Duration="0:0:2" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="rotateStoryBoard"/>
</DataTrigger.ExitActions>
</DataTrigger>
<EventTrigger RoutedEvent="Loaded">
<!--<BeginStoryboard x:Name="rotateStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)" To="360" Duration="0:0:2" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>-->
</EventTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've finally found a solution. After trying to replace the DoubleAnimation with a ColorAnimation, using (Path.Fill).(SolidColorBrush.Color) as TargetProperty, I received an error that the path is not pointing to a DependencyProperty. This was only solved when I defined the SolidColorBrush explicitly in a Setter which led my to try the same with the RenderTransform of the Path:
<Path.Style>
<Style TargetType="Path">
<Setter Property="RotateTransform">
<Setter.Value>
<RotateTransform x:Name="gearPathTransform"/>
</Setter.Value>
</Setter>
<!-- ... -->
</Style>
Afterwards the animation works as expected. I still don't know why this is only necessary in the inner ItemsControl and it is also nasty that there is no error as is for the ColorAnimation, but at least it is working now.
I have a button above a listbox which has it's items in a horizontal allignment. If my button has focus and I press the down key on the keyboard then the listbox get focus. If I then press key up then the listbox still has focus. Can this behaviour be changed so when I press key up my button has focus again?
EDIT: I should add I'm using MVVM so would like to keep the solution out of codebehind if possable.
Below is the complete code for my listbox.
<ListBox Grid.Row="2" ItemsSource="{Binding Movies}" TextSearch.TextPath="Title" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Template>
<ControlTemplate>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled">
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="poster">
<Image x:Name="posterImage" RenderOptions.BitmapScalingMode="LowQuality" Source="{Binding Poster}" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualHeight}">
</Image>
<TextBlock Text="{Binding PosterOverlayText}" Style="{DynamicResource PosterOverlayText}" Width="{Binding ActualWidth, ElementName=posterImage}"/>
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="st" ScaleX="0.85" ScaleY="{Binding ScaleX, RelativeSource={RelativeSource Self}}"/>
</TransformGroup>
</Grid.LayoutTransform>
<Grid.InputBindings>
<MouseBinding Command="{x:Static Commands:MediaFiles.PlaySelectedMovie}" Gesture="LeftDoubleClick" />
</Grid.InputBindings>
</Grid>
<DataTemplate.Resources>
<CubicEase x:Key="ease" EasingMode="EaseOut"/>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="0.85"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.InputBindings>
<KeyBinding Command="{x:Static Commands:MediaFiles.PlaySelectedMovie}" Gesture="ENTER"/>
</ListBox.InputBindings>
</ListBox>
Don't know if there is a straight-forward MVVM accepted solution to this problem. The reason is that ListBox actually uses the key up and key down events to navigate through the list items (which is the desired effect most of the times) whereas the buttons will let the events go to the FocusManager class.
However, as for everything out there, there are workarounds so here's how I would do it:
First of all you need to intercept the Key-Down event and look for directional inputs (up-down-left-right). I would think that you would still like to navigate with the keys in your list so, in the event handler I would make sure to test if I am on the first (or last) element and then use the MoveFocus() method with TraversalRequest - FocusNavigationDirection.Up / Down / Next / Previous to move the focus.
Last, to ensure your code-behind is clean, create a new ListBox derived control and have your event there. Actually, in the derived control, don't use the event, overide OnKeyDown method. This way, you are writing the focus logic in your custom control and not the View.
Code:
protected override void OnKeyDown(KeyEventArgs e)
{
if ((e.Key == Key.Up ||e.Key == Key.Left) && SelectedIndex == 0)
{
//.Previous will navigate correctly to the last element
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
return;
}
if (e.Key == Key.Down && this.SelectedIndex == this.Items.Count - 1)
{
//.Next will navigate to the first item in the list rather than skip to the next control. We will use Down or Right.
//this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
return;
}
if (e.Key == Key.Right && this.SelectedIndex == this.Items.Count - 1)
{
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
return;
}
base.OnKeyUp(e);
}
I'm trying to get a message panel animated in WPF but has so far achieved no success.
This is the situation:
I have a user control with a StackPanel containing an ItemsControl bound to an (observable) collection in the control's View Model object (ViewModel.Messages).
When I need to present the user with messages I ad those (as MessageVM instances) to the observable collection.
The ItemsControl's visibility is bound to an integer property called ViewModel.CountVisibleMessages and there's a converter taking care of translating 0 to Visibility.Hidden and positive values to Visibility.Visible.
This works just fine. When a message gets added to the collection the StackPanel automatically becomes visible and as the user (or a timer) removes the last message it gets hidden. The StackPanel height is automatically adjusted to fit all messages of course.
To make everything look nicer I would prefer it if the StackPanel resized itself using an animation running for, say, 300 ms. (Ultimately I would also like it to accelerate and deccelerate but that's beyond my ambition right now.
I have experimented for a few hours now but I feel I'm not even close.
Below is my current (not even close to working) XAML at the moment:
<StackPanel Orientation="Vertical"
VerticalAlignment="Top"
Visibility="{Binding CountVisibleMessages, Converter={StaticResource IntToVisibility}}"
Height="Auto"
Background="{DynamicResource HmiBackColorLightBrush}">
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding CountVisibleMessagesChanged}" Value="True" ><!-- I suppose I shopuld've used a Routed Event here but I just needed to get it triggered -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Margin.Bottom"
From="100" <!-- Just a made up value to test the concept -->
To="0"
Duration="0:0:0:3"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<ItemsControl ItemsSource="{Binding Messages}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{DynamicResource Message}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="15" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Text}" Margin="3" Style="{Binding MessageType, Converter={StaticResource MessageTypeToStyle}, ConverterParameter={x:Type TextBlock}}" /> <!-- using dynamic styling here -->
<RadioButton Grid.Column="1" Style="{DynamicResource HmiCloseMessageButton}" IsChecked="{Binding IsVisible, Converter={StaticResource BoolToVisibility}, ConverterParameter=true}" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
(I do realize the above XAML won't get the StackPanel to auto-resize slowly. It's just an experiment to get anything happening).
This can't be too difficult I suppose (it's a pretty standard UI behavior in many programs) so I'd appreciate it if anyone could point me in the right directions.
Cheers
Are you sure you want to adjust the bottom margin? The stack panel VerticalAlignment is top. If you want to change the Height then bind your StoryBoard Property to Height. Do you know if your StoryBoard is firing?
The key point is ExitActions are necessary for EnterAction based dataTrigger animations. So the following seems to be working in my case ....
<StackPanel Grid.Row="0" Height="100" x:Name="MyGrid">
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyCheckBox,
Path=IsChecked}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.Target="{Binding
RelativeSource={RelativeSource
AncestorType={x:Type
StackPanel}},
BindsDirectlyToSource=True}"
Storyboard.TargetProperty="Height"
From="100"
To="200"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.Target="{Binding
RelativeSource={RelativeSource
AncestorType={x:Type
StackPanel}},
BindsDirectlyToSource=True}"
Storyboard.TargetProperty="Height"
To="100"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
<CheckBox x:Name="MyCheckBox" Grid.Row="1" />
Let me know if this helps.
I'm attempting to bring a templated item forward (ZIndex) in a databound stackpanel. Since a stackpanel creates a ContentPresenter for each item within it, my visual tree looks like this:
ItemsControl
Border
ItemsPresenter
StackPanel
ContentPresenter
ToggleButton
ContentPresenter
ToggleButton
ContentPresenter
ToggleButton
When a ToggleButton is clicked, it fires off a storyboard like this:
<Storyboard x:Key="MyStoryboard" >
<Int32AnimationUsingKeyFrames BeginTime="0:0:0.000" Duration="0:0:0.350"
Storyboard.TargetProperty="TemplatedParent.(Panel.ZIndex)" >
<Int32KeyFrameCollection>
<DiscreteInt32KeyFrame KeyTime="0:0:0.000" Value="99" />
</Int32KeyFrameCollection>
</Int32AnimationUsingKeyFrames>
</Storyboard>
<Style x:Key="SubStateOptionButtonStyle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<!-- Toggle Button Contents here -->
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MyStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However, the TemplatedParent.(Panel.ZIndex) gives me a strange exception:
Value cannot be null.
Parameter name: dp
at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
at System.Windows.Media.Animation.Storyboard.ProcessComplexPath(HybridDictionary clockMappings, DependencyObject targetObject, PropertyPath path, AnimationClock animationClock, HandoffBehavior handoffBehavior, Int64 layer)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.BeginCommon(DependencyObject containingObject, INameScope nameScope, HandoffBehavior handoffBehavior, Boolean isControllable, Int64 layer)
at System.Windows.Media.Animation.BeginStoryboard.Begin(DependencyObject targetObject, INameScope nameScope, Int64 layer)
etc.
I've double checked via code-behind that the ToggleButton's TemplatedParent is indeed the ContentPresenter and I can set the ZIndex with something like this:
Panel.SetZIndex((sender as ToggleButton).TemplatedParent as UIElement, 99);
But I'd like to animate it with a storyboard, preferably with straight XAML, since it needs to stay forward for the duration of the storyboard, then go back to where it was.
I've considered inheriting from ItemsControl to prevent the ContentPresenter from being created but that's a heavy-handed solution to something that seems like it should be easy.
Here is the answer to this question:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/7b9b8209-063d-46b2-a03f-98b393cf9514
(by Min Zhu [MSFT])
Your original code doesn't work because TemplatedParent is not a dependency property. So it is not a valid path for animation.
The code TI82 suggests is correct. It still doesn't work because the TemplatedParent is the Button. But the element you want to animate is the item container.
To animate the item container, you can use ItemsControl's ItemContainerStyle. Button.Click is a bubbling routed event, so it will bubble from the button to the item container as well.
Try the following code.
<Window.Resources>
<Storyboard x:Key="MyStoryboard">
<ColorAnimation Storyboard.TargetName="ButtonRect"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="Red"
Duration="0:0:0.350"
FillBehavior="Stop"/>
</Storyboard>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Panel.ZIndex" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}, Mode=OneWayToSource, Path=(Panel.ZIndex)}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Height="60" Margin="0,0,0,-10" >
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="15">
<Rectangle x:Name="ButtonRect" RadiusX="15" RadiusY="15" Fill="AliceBlue" />
</Border>
<TextBlock Text="{Binding}" FontSize="15" TextAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MyStoryboard}" />
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="MyButtonTemplate">
<Grid>
<Button Style="{StaticResource MyButtonStyle}" />
</Grid>
</DataTemplate>
<Storyboard x:Key="MyStoryboard2">
<Int32AnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.ZIndex)"
BeginTime="0:0:0.000" Duration="0:0:0.350" FillBehavior="Stop">
<Int32KeyFrameCollection>
<DiscreteInt32KeyFrame KeyTime="0:0:0.000" Value="99" />
</Int32KeyFrameCollection>
</Int32AnimationUsingKeyFrames>
</Storyboard>
<Style x:Key="MyItemContainerStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource MyStoryboard2}"/>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="ABCDEF"
ItemTemplate="{StaticResource MyButtonTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
I have a simple ListBox.ItemTemplate containing a Label and a TextBox bound to a CSLA Bindable List. When I select the TextBox the CurrentItem does not change, it only changes if I select the Label. I have IsSynchronizedWithCurrentItem='True'.
<ListBox x:Name="ItemsDataGrid"
ItemsSource="{Binding Source={StaticResource AuditItems},Path=Items}"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="{Binding Path=TypeRef}" />
<TextBox x:Name="TextBoxQty"
Grid.Column="1"
Text="{Binding Path=TaliQty}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Try adding this to your ListBox. It selects the item any time any contained element (like TextBox) gets keyboard focus. A similar method could also be used with just a simple setter in the Trigger but that tends to interfere with the CurrentItem setting on the ICollectionView:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="SetSelected">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="0:00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="SetSelected"/>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
This is happening because the TextBox is handling the MouseDown event. As it is set to bubble up it will not reach the containing ListBoxItem. The simplest way to fix this would be to just handle the selection of the ListBoxItems in the PreviewMouseDown, which will occur and tunnel down before the actual MouseDown event bubbles up.
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewMouseDown"
Handler="ListBoxItem_PreviewMouseDown" />
</Style>
</ListBox.ItemContainerStyle>
And in the Code behind for the xaml file:
private void ListBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var item = (sender as ListBoxItem);
if (item != null)
item.IsSelected = true;
}