[C#] Laufschrift läuft unsanft / unschön

Lesterdor

Erfahrenes Mitglied
#1
Guten Abend liebe Community,

ich habe mir einen kleinen Prototypen für eine Laufschrift erstellt. Dabei "läuft" das Label vom rechten Bildschirmrand zum Linken. Dort angekommen, fängt es wieder bei rechts an, sodass es unendlich wirkt. Vergleichbar mit einem Newsticker.

Mein Problem ist leider, dass das Label sehr ruckelartig seine Position verschiebt. Es wirkt einfach unsanft/unsauber. Ich verschiebe das Label alle 33 ms um 1.0 nach links.

Mein Renderer läuft hardwareseitig ( Tier 2).
Framework: 4.5
XAML:
Code:
<Window x:Class="Canny.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="811" Name="main">
    <Canvas Name="myCanvas" Background="Black">
        <Label Name="lb1" Content="Lauftext+++" Foreground="WhiteSmoke" FontSize="25" Canvas.Left="618" Canvas.Top="10" FontFamily="Courier New" FontWeight="ExtraBold" FontStretch="UltraExpanded"/>
    </Canvas>
</Window>
XAML.CS:
Code:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;

namespace Canny
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer timer;
        double newPos;
        readonly double actualWidthLb;
        int renderingTier;
        public MainWindow()
        {
            InitializeComponent();
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(33.0);
            timer.Tick += timer_Tick;
            newPos = myCanvas.ActualWidth;
            actualWidthLb = lb1.ActualWidth;
            timer.Start();
            renderingTier = (RenderCapability.Tier >> 16);
            
        }

        void timer_Tick(object sender, EventArgs e)
        {
            newPos -= 1.0;
            Canvas.SetLeft(lb1,newPos);
            if (Canvas.GetLeft(lb1) + actualWidthLb <= 0)
            {
                Canvas.SetLeft(lb1, myCanvas.ActualWidth);
                newPos = myCanvas.ActualWidth;
            }
        }
    }
}
Über Ratschläge bedanke ich mich im Voraus und verbleibe

mit freundlichen Grüßen

- Les
 
#2
Huhu

Habe es gerade mal ausprobiert scheint aber nur ganz leicht zu ruckeln. Ich denke mal das 1.0 ein zu grosser wert ist, ich hab das Gefühl mit 15ms und 0.5newPos geht es ganz flüssig.
 

ksk

Erfahrenes Mitglied
#3
Hallo Les,

der SilverLight Profi bin ich nicht aber ich hatte mal ein Projekt in welcher ich eine Animation realisieren musste.
Habe dazu einen DispatcherTimer genommen und jede Millisekunde eine Methode aufgerufen in der die Poition
geändert wurde. Das sah sehr flüssig und gschmeidig aus.
Jedoch änderte ich dabei den Margin Wert des jeweiligen Objektes.
Code:
private void StartAnim()
{
  dpt = new System.Windows.Threading.DispatcherTimer();
  dpt.Interval = new TimeSpan(0, 0, 0, 0, 1);
  dpt.Tick += new EventHandler(dpt_Tick);
  dpt.Start();
}
void dpt_Tick(object sender, EventArgs e)
{
  if (img.Margin.Left < 28)
  {
  img.Margin = new Thickness(Convert.ToDouble(img.Margin.Left + 2),
  img.Margin.Top, img.Margin.Right, img.Margin.Bottom);
  }
  else
  { img.Margin = new Thickness(28, img.Margin.Top, img.Margin.Right, img.Margin.Bottom); }
}
Ist zwar keine Elegante Lösung aber probier mal obst ein Unterschied merkst.
Übrigens, über Expression Blend kannst diese Sachen ähnlich wie in Flash über
eine eigene TimeLine usw. realisieren genannt Storyboards.
Dies auch über Code-Behind natürlich, bsp über ein PlaneProjection
Code:
PlaneProjection pp = new PlaneProjection();
pp.LocalOffsetX = x;
pp.LocalOffsetY = y;
pp.LocalOffsetZ = z;
obj.Projection = pp; // die Projection an das Objekt zuweisen
Hier ein Ansatz in XAML
Code:
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
  <EventTrigger.Actions>
  <BeginStoryboard>
  <Storyboard>
  <DoubleAnimation Storyboard.TargetName="objectToAnimate"
  Storyboard.TargetProperty="(Canvas.Top)"
  To="350" Duration="0:0:3"/>
  </Storyboard>
  </BeginStoryboard>
  </EventTrigger.Actions>
</EventTrigger>
</Canvas.Triggers>
<Rectangle x:Name="objectToAnimate" Canvas.Left="5" Fill="Red"
  Canvas.Top="5" Height="25" Width="25" Stroke="Blue" StrokeThickness="3" />
</Canvas>
Schau dir noch dazu folgende Links an
http://msdn.microsoft.com/en-us/library/cc189019(VS.95).aspx
http://msdn.microsoft.com/en-us/library/ms742305%28v=vs.110%29.aspx
http://msdn.microsoft.com/en-us/library/cc189090%28v=vs.95%29.aspx


Lg
ksk
 
#4
Hallo,

es ruckelt, weil du einen Timer verwendest.

Timer werden nicht immer genau dann ausgelöst wann man es erwartet - deshalb ruckelts bei dir. Man könnte es mehr als Wunschzeit betrachten(hängt davon ab wie stark dein programm/system ausgelastet ist), zumal man einen Timer minimal nur alle 15 komma irgendwas Millisekunden einstellen kann. Man kann zwar 1 ms einstellen, wird jedoch nie an diesen Wert kommen, Grund dafür ist der Taktquartz deines Rechners.

Timer zu verwenden ist ziemlich...uncool. Ist zwar sehr leicht, aber für etwas gehobene Anforderungen absolut unpassend.

Wenn du deine Berechnung in einen Thread auslagerst und die Position über einen Timespan berechnest, dürfte es dein Problem lösen. Am einfachsten ist du verwendest den ThreadPool und mittels Dispatcher.Invoke() kannst du deine Aufrufe in den UI-Thread synchronisieren.

vg