Mark Denom
Mark Denom

Reputation: 1057

How do I make my StackPanel slide to the side when I click a ListBoxItem

So I have a project with my own UserControl that is build around a ListBox and I want to make it to where when I click a Item in the ListBox, I want my StackPanel to slide to the right. And when I click another item, the StackPanel should slide back to where it started from and then slide out again.

This can be done using RadioButtons but I am not sure if you can mix those in any way, because with RadioButtons you can keep track of wether a item is selected by using a bool because the RadioButton has the isChecked property. Making it to where.. If the RadioButton is Checked it turns to true and then the Animation triggers on True and when you click a new RadioButton it turns to false because the one you had selected is not selected anymore. But then true again when you click a new one. And the false would obviously trigger the slide in animation.

So I guess I need to check if an item is selected in the ListBox so something like this maybe?

foreach(Item item in MyListBox.Items)        
    if(MyListBox.SelectedItems.Contains(item)
        MyObject.Value = true;
    else
        MyObject.Value = false;

But using XAML ofcourse databainding it.

MainWindow.xaml

<Grid>
        <Grid.Resources>
            <system:Double x:Key="SlideOffSet">50</system:Double>
            <Storyboard x:Key="SlideRight">
                <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                 From="0" To="{StaticResource SlideOffSet}"
                                 Duration="0:0:0.2" />
            </Storyboard>
            <Storyboard x:Key="SlideLeft">
                <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                 From="{StaticResource SlideOffSet}" To="0"
                                 Duration="0:0:0.2" />
            </Storyboard>
        </Grid.Resources>

        <local:MyUserControl x:Name="UserControl"/>

        <StackPanel Width="100"
                    Height="100"
                    Background="Gray">

            <StackPanel.Style>
                <Style TargetType="StackPanel">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=UserControl, Path=SelectedItem.IsSelected}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource SlideRight}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <BeginStoryboard Storyboard="{StaticResource SlideLeft}" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </StackPanel.Style>

            <StackPanel.RenderTransform>
                <TranslateTransform />
            </StackPanel.RenderTransform>

        </StackPanel>

    </Grid>

My UserControl

<Grid Background="LightGray">
        <ListBox SelectionMode="Single" 
                 ItemsSource="{Binding Items}"
                 SelectedItem="{Binding SelectedItem}"
                 x:Name="TheListBox">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border Grid.Column="2" Padding="0,0,8,0">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <!-- Selected Item -->
                                <ColumnDefinition Width="Auto"/>
                                <!-- Image Item -->
                                <ColumnDefinition Width="Auto"/>
                                <!-- Main content-->
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <!-- Item image -->
                            <Border Grid.Column="1" Padding="8">
                                <Image Source="{Binding Image}"
                                       UseLayoutRounding="True"
                                       RenderOptions.BitmapScalingMode="Fant"
                                       Height="40"
                                       Width="40"/>
                            </Border>


                            <!-- Main Content -->
                            <Border Grid.Column="2" Padding="0,0,8,0">
                                <StackPanel VerticalAlignment="Center">
                                    <!-- Main Content -->
                                    <TextBlock Text="{Binding Title, FallbackValue=Title}"
                                               TextTrimming="CharacterEllipsis"
                                               FontWeight="Bold"/>

                                    <!-- Main Content -->
                                    <TextBlock Text="{Binding Username, FallbackValue=Username}"
                                               TextTrimming="CharacterEllipsis"/>
                                    <!-- Website URl -->
                                    <TextBlock Text="{Binding Password, FallbackValue=https://facebook.com}" Foreground="Gray"
                                               TextTrimming="CharacterEllipsis"/>
                                </StackPanel>
                            </Border>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>

            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Border x:Name="Border"
                                        BorderBrush="Transparent"
                                        BorderThickness="5,0,0,0">
                                    <ContentPresenter Margin="0,0,0,0" />
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsSelected" Value="True">
                                        <Setter TargetName="Border" Property="BorderBrush" Value="LightSkyBlue" />
                                    </Trigger>


                                    <Trigger Property="IsSelected" Value="True">
                                        <Trigger.EnterActions>
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation
                                                        Storyboard.TargetProperty="Opacity"
                                                        From="0.0" To="1.0" Duration="0:0:1"/>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </Trigger.EnterActions>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>

                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>

EntryItemViewModel.cs

public class EntryItemViewModel : INotifyPropertyChanged
    {
        private string _title;

        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

AllEntriesListViewModel.cs

public class AllEntriesListViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<EntryItemViewModel> Items { get; set; } = new ObservableCollection<EntryItemViewModel>();

        public EntryItemViewModel EntryItemViewModel { get; set; } = new EntryItemViewModel();

        public AllEntriesListViewModel()
        {
            Items.Add(new EntryItemViewModel { Title = "Hello World" });
            Items.Add(new EntryItemViewModel { Title = "Hello World1" });
            Items.Add(new EntryItemViewModel { Title = "Hello World2" });
            Items.Add(new EntryItemViewModel { Title = "Hello World3" });
        }


        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

EDIT

I tried adding a Converter but it's still not reacting to me selecing a value

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((EntryItemViewModel)value != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }

<DataTrigger Binding="{Binding ElementName=MyUserControl, Path=AllEntriesListViewModel.SelectedItem.IsSelected, Converter={StaticResource EntryModelConverter}}" Value="True">

If I put a breakpoint on the property, it gets the selected EntryViewModel but the animation isnt reacting

Upvotes: 0

Views: 68

Answers (1)

flash
flash

Reputation: 1263

The reason your <DataTrigger Binding="{Binding ElementName=UserControl, Path=SelectedItem.IsSelected}" Value="True"> is not working as your UserControl does not have a property name SelectedItem. It is for the ListBox inside the UserControl.

You need to create a property "SelectedItem" in your ViewModel of UserControl (AllEntriesListViewModel) and bind SelectedItem to that property, and then if you use DataContext.SelectedItem <DataTrigger Binding="{Binding ElementName=UserControl, Path=DataContext.SelectedItem.IsSelected}" Value="True"> it would be able to access the property.

Edit:

You would also need to create one more boolean property IsSelected in the AllEntriesListViewModel, and change that property based on the SelectedItem change.

    private EntryItemViewModel _selectedItem;
    public EntryItemViewModel SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            if(_selectedItem != value)
            {
                _selectedItem = value;
                IsSelected = !IsSelected;
                OnPropertyChanged(nameof(SelectedItem));
            }                
        }
    }

and bind the data trigger to DataContext.IsSelected.

Upvotes: 2

Related Questions