views:

50

answers:

1

I encounter a problem that I cannot solve. I hope to find an answer here. I need a listbox to hide half way when a certain listboxitem is selected. I setup a storyboard with opacity mask animation which work fine in blend. My problem I cannot initiate BeginStoryboard. I tried numerous ways and no success. I need to hide the listbox to reveal the content behind it. I generate listboxitems from XML data file and based on the name node I planned to initiate storyboard playing.

Here what I have. I created DataTemplate which I set in ListBoxItem Style:

<DataTemplate x:Key="SelectedListBoxItemDataTemplate">
        <StackPanel x:Name="DataItemSelected" Orientation="Horizontal" Margin="12,0,0,0" >
                <TextBlock FontFamily="Arial" Text="►" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="{Binding XPath=state}" Margin="-4, 0,6,4"/>
                <Image x:Name="ListBoxImage" Source="{Binding XPath=icon}" Margin="4,4,14,4" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" />
                <TextBlock x:Name="textBlock" Text="{Binding XPath=name}" LineHeight="22" Foreground="#FFFFFFFF" FontSize="16"  />  
                <Border x:Name="PART_Icon" Background="{x:Null}" Width="{Binding NodeValue.Width}" HorizontalAlignment="Left" Padding="3,0"></Border>
            </StackPanel>

        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding XPath=name}" Value="SERVERS">
                <Setter TargetName="PART_Icon" Property="Background" Value="Black" />
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource HideListBox}" x:Name="HideListBox_BeginStoryboard"/>
                </DataTrigger.EnterActions>  
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate> 

I need to run this storyboard which I keep in Window.Resources:

<Storyboard x:Key="HideListBox">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[0].(GradientStop.Offset)" Storyboard.TargetName="Nav_ListBox">
            <EasingDoubleKeyFrame KeyTime="0" Value="0.069"/>
            <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[1].(GradientStop.Offset)" Storyboard.TargetName="Nav_ListBox">
            <EasingDoubleKeyFrame KeyTime="0" Value="0.069"/>
            <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"/>
        </DoubleAnimationUsingKeyFrames>
        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="Nav_ListBox">
            <EasingColorKeyFrame KeyTime="0" Value="White"/>
            <EasingColorKeyFrame KeyTime="0:0:0.4" Value="White"/>
        </ColorAnimationUsingKeyFrames>
        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="Nav_ListBox">
            <EasingColorKeyFrame KeyTime="0" Value="#00000000"/>
            <EasingColorKeyFrame KeyTime="0:0:0.4" Value="#00000000"/>
        </ColorAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(LinearGradientBrush.StartPoint)" Storyboard.TargetName="Nav_ListBox">
            <EasingPointKeyFrame KeyTime="0" Value="1.076,0.501"/>
            <EasingPointKeyFrame KeyTime="0:0:0.4" Value="1,0.5"/>
        </PointAnimationUsingKeyFrames>
        <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.OpacityMask).(LinearGradientBrush.EndPoint)" Storyboard.TargetName="Nav_ListBox">
            <EasingPointKeyFrame KeyTime="0" Value="0.035,0.501"/>
            <EasingPointKeyFrame KeyTime="0:0:0.4" Value="0.2,0.5"/>
        </PointAnimationUsingKeyFrames>
    </Storyboard>

I am getting errors that "Nav_ListBox" object cannot be found. I understand that listbox object is not avaible from the datatemplate level. I am wondering what will be the right solution to enable animation to play and eventualy to remove on click the othe listboxitem. Thank you in advance.

+1  A: 

I put together something quick to hopefully help you on your way (new default WPF application, MainWindow's DataContext set to itself). I ended up using an IValueConverter to get the Name from the generated XmlLinkedNode out of the SelectedItem of the ListBox, but there should be a more elegant way using XPath statements I'm not familiar with. Basically declare your Storyboard on the ListBoxes Style, not in the Datatemplate:

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <XmlDataProvider x:Key="persons"
                         XPath="persons/person"
                         Source="xmldata.xml" />
        <local:SelectionConverter x:Key="selectionConverter" />
    </Window.Resources>

    <Grid>
        <ListBox Background="White" ItemsSource="{Binding Source={StaticResource persons}}" x:Name="lst">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding XPath=name}" />
                        <TextBlock Text="{Binding XPath=prop}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.Style>
                <Style>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=lst, Path=SelectedItem, Converter={StaticResource selectionConverter}}"
                                     Value="b">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard Duration="0:0:1">
                                        <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
                                                        To="Green" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard Duration="0:0:1">
                                        <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
                                                        To="White" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ListBox.Style>
        </ListBox>
    </Grid>
</Window>

MainWindow codebehind:

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    }
}

SelectionConverter.cs

namespace WpfApplication1
{
    public class SelectionConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return (value == null) ? null : (value as XmlLinkedNode).SelectNodes("name")[0].InnerText;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

Sample data (Add to your project as XML File):

<?xml version="1.0" encoding="utf-8" ?>
<persons>
    <person>
        <name>a</name>
        <prop>3</prop>
    </person>
    <person>
        <name>b</name>
        <prop>3</prop>
    </person>
    <person>
        <name>c</name>
        <prop>3</prop>
    </person>
</persons>
MrDosu
Thank you for your help. Unfortunatelly, I am geting error "The tag 'SelectionConverter' does not exist in XML namespace, even though I added it.
vladc77
Did you add the SelectionConverter class to your namespace which you defined in the XAML
MrDosu
Yes, I copied your converter class and pasted into MainWindow.xaml.cs, then I added namespace - xmlns:local="clr-namespace:WpfApplication1". It is really starnge. It should work. I checked all naming multiple times, but I am getting the same error in VS and Blend
vladc77
I also added reference to XML - using System.Xml;
vladc77
I tried to avoid using converter this way: <DataTrigger Binding="{Binding ElementName=lst, XPath=@name}" Value="b">. It is still does not play storyboard.
vladc77
The Converter class needs to be publically visible in your namespace. Best use Project->Add->Class in Project Explorer and name it accordingly. If you break in the Convert method you can easily look at the type of data that gets passed as SelectedItem.
MrDosu
I edited to post to relect the project structure clearer. Only usings are missing (Ctrl+. them in).
MrDosu
Great. Thank you it is working now. Thank you for sharing this approach with me. I am wondering that if it is possible to do the same witout Converter. Something like this - "<DataTrigger Binding="{Binding ElementName=lst, XPath=@name}" Value="b">. " Getting a value of the node and based on it intitiate storyboard running. I need it because I will have more than one value that should trigger the animations to run and also I will need to add other objects behind, such us sub level items of the list box.
vladc77