[WPF] News ticker met swap animations

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik probeer een UI te maken in de stijl van een "news ticker". Ik krijg een lijst met items die ik in een horizontale balk wil weergeven, allemaal naast elkaar. Elk item heeft een index en een text:

C#:
1
2
3
4
5
public class TickerItem
{
    public int Index { get; set; }
    public string Text { get; set; }
}


De balk moet de items in de juiste volgorde weergeven, gesorteerd op Index. De lijst komt echter niet gesorteerd binnen maar op willekeurige volgorde (niet heel belangrijk verder).

Er zijn twee vereisten waaraan ik wil voldoen, en ik krijg niet beiden tegelijk voor elkaar:
  1. Soms wisselen items van plek (bijv item 3 en 4 wisselen om). Op dat moment wil ik dat de items in mijn news ticker via een 'swap animation' omdraaien. Oftewel item 3 schuift langzaam naar rechts, en item 4 schuift naar links, visueel via een animatie.
  2. De breedte van de items is afhankelijk van de lengte van de text en niet fixed width. Sommige items kunnen dus heel lang zijn, sommige maar heel kort.
Hier een voorbeeld van de swap animatie die ik wil zien:
Afbeeldingslocatie: https://i.imgur.com/iJykJsg.gif


Uiteindelijk moet deze hele ticker ook nog eens scrollen maar dat is voor nu even niet belangrijk.


Ik zie twee manieren om zoiets te implementeren maar krijg niet beide doelen voor elkaar.
In beide manieren gebruik ik een ItemsControl die via databinding aan de lijst met TickerItems is gekoppeld.


Optie 1: ItemsControl met StackPanel items panel.

Als ik een StackPanel gebruik als ItemsPanel dan krijg ik als vanzelf een goeie layout. Alle items liggen netjes naast elkaar en bredere items nemen automatisch meer ruimte in beslag.

Het sorteren op index zou ik hier kunnen doen door middel van een CollectionViewSource of iets dergelijks wat mijn lijst sorteert.

Waar ik hier stuk loop is: hoe zorg ik dat items via een animatie met elkaar wisselen als de index verandert? Ik zou niet weten hoe ik dit moet aanpakken. De items staan "vast" op hun positie door de StackPanel, en als de index verandert dan nemen ze meteen elkaars plek in zonder dat daar een animatie tussen kan.


Optie 2: ItemsControl met Grid panel
In een vergelijkbaar probleem heb ik dit soort swap animatie opgelost door de StackPanel te vervangen met een Grid, en dan handmatig de layout van de items te regelen via een animatie op de Margin property.

XML:
1
2
3
4
5
6
7
8
9
10
11
12
            <ItemsControl ItemsSource="{Binding Items}" VerticalAlignment="Top">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="local:TickerItem">
                        <local:TickerItemView HorizontalAlignment="Left"></local:TickerItemView>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>


Elke keer als de Index verandert start ik een ThicknessAnimation die de Margin van elk item naar de juiste plek schuift.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    public partial class TickerItemView : UserControl
    {
        public static readonly DependencyProperty TickerIndexProperty = DependencyProperty.Register(
            "TickerIndex", typeof(int), typeof(TickerItemView), new PropertyMetadata(OnTickerIndexChanged));

        public int TickerIndex
        {
            get { return (int) GetValue(TickerIndexProperty); }
            set { SetValue(TickerIndexProperty, value); }
        }

        public TickerItemView()
        {
            InitializeComponent();

            BindingOperations.SetBinding(this, TickerIndexProperty, new Binding("Index"));
        }

        private void OnTickerIndexChanged(int index)
        {
            var margin = new Thickness(index * ActualWidth, 0, 0, 0);

            var storyboard = new Storyboard();
            var move = new ThicknessAnimation(margin, new Duration(TimeSpan.FromMilliseconds(400)))
            {
                EasingFunction = new SineEase()
            };
            storyboard.Children.Add(move);
            Storyboard.SetTarget(move, this);
            Storyboard.SetTargetProperty(move, new PropertyPath(MarginProperty));
            storyboard.Begin();
        }

        private static void OnTickerIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TickerItemView)d).OnTickerIndexChanged((int)e.NewValue);
        }
    }


Dit werkt prima als de items een fixed width hebben. Maar dit gaat helemaal mis zodra items verschillende widths hebben, aangezien ik mijn margin alleen bepaal door middel van de index en width van het huidige item. Op een of andere manier moet ik hier de width van alle voorgaande items weten om zo de juiste positie van het item te kunnen berekenen.

Op dit punt ben ik eigenlijk gestopt want ik heb het gevoel dat ik hier uren op kan vastlopen terwijl er misschien een veel betere oplossing is.

Ziet iemand een makkelijkere manier om dit te doen?

Mijn iRacing profiel