滚动文本更新问题

时间:2009-04-22 16:47:40

标签: c# wpf xaml animation

我有一些从this post on MSDN修改的滚动文字动画。不过,我有两个问题。

首先,我需要能够定期更新文本。但是,当OnTick()触发时,我收到以下错误,“调用线程无法访问此对象,因为另一个线程拥有它。”我尝试了一些不同的东西,并发布了一种我尝试过的方法。

第二个是不是来回滚动,我真的需要文本表现为一个真正的选框并向一个方向移动,内容不断流动,没有间隙,即“abcdeabcde ...”而不是“abcde” 。这需要两个故事板与同一文本串联运行还是有另一种方法来实现这一目标?

  Storyboard storyboard = new Storyboard(); 
  Timer timer; 
  public void OnLoad(object sender, RoutedEventArgs e)
  {
      _presenter.OnViewReady();
      StartMarquee(); 
  }
  public MyControl()
  {
        InitializeComponent();
        Loaded += OnLoad;
        timer = new Timer(OnTick, null, 10000, 10000);
  }
  private void OnTick(object state)
  {
      storyboard.Stop(marqueeText);
      storyboard = new Storyboard();
      marqueeText.Text =
            "Fusce id massa sed tortor volutpat viverra. Mauris ut quam. Fusce iaculis magna at urna. In sed dui vitae quam faucibus ullamcorper. Donec hendrerit magna eget neque. Mauris sit amet risus dictum mauris ultricies ornare. Phasellus lectus leo, mattis eget, ultrices vel, suscipit eu, tellus. Integer ut enim. Suspendisse hendrerit mattis sem. Aenean interdum elementum libero. ";
      StartMarquee();
  }
  protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  {
      base.OnRenderSizeChanged(sizeInfo);
      marqueeText.Text =
          "Is it possible to create a marquee or scrolling text in WPF?  Is it possible to create a marquee or scrolling text in WPF?  Is it possible to create a marquee or scrolling text in WPF? Is it possible to create a marquee or scrolling text in WPF?  Is it possible to create a marquee or scrolling text in WPF?  Is it possible to create a marquee or scrolling text in WPF?";
  } 
  private void StartMarquee() 
  {
      var canvas = CommonFunctions.FindVisualParent<Canvas>(marqueeText);
      if (marqueeText.ActualWidth < canvas.ActualWidth) return;
      var duration = new Duration(TimeSpan.FromSeconds(marqueeText.ActualWidth / 60));
      var animation = new DoubleAnimation(-marqueeText.ActualWidth, canvas.ActualWidth, duration); 
      animation.RepeatBehavior = RepeatBehavior.Forever; 
      Storyboard.SetTargetName(animation, "rtTTransform"); 
      animation.AutoReverse = false; 
      Storyboard.SetTargetProperty(animation, new PropertyPath(TranslateTransform.XProperty));

      storyboard.Children.Add(animation);
      storyboard.Begin(marqueeText);
  }

在视图中,控件声明为

        <Canvas Grid.Column="1" HorizontalAlignment="Stretch" ClipToBounds="True" Margin="10,0">
            <TextBlock Canvas.Left="0" Canvas.Top="0" x:Name="marqueeText" TextWrapping="NoWrap" VerticalAlignment="Center"
                   Grid.Column="1" Foreground="{x:Static Brushes.White}" ClipToBounds="False" FontSize="16">   
                <TextBlock.RenderTransform>  
                    <TransformGroup>  
                        <ScaleTransform ScaleX="1" ScaleY="1"/>   
                        <SkewTransform AngleX="0" AngleY="0"/>   
                        <RotateTransform Angle="0"/>   
                        <TranslateTransform x:Name="rtTTransform"/>   
                    </TransformGroup>  
                </TextBlock.RenderTransform>
            </TextBlock>
        </Canvas>

在此先感谢我仍然在努力解决这个问题,并会根据我找到的任何更改进行更新。

[已编辑] 删除了AutoReverse,让事情变得更加混乱,更多地朝着我想要完成的事情发展。

2 个答案:

答案 0 :(得分:2)

我最终通过创建一个在后台运行的线程来解决问题,该线程每隔一段时间更新文本,当文本完成滚动时,拉动最新文本。

包含完整的示例,以备将来帮助其他人。

    public Thread Updater;
    public MyControl()
    {
        InitializeComponent();
        Loaded += OnLoad;
        Updater = new Thread(ExecuteMarqueeUpdate);
        Updater.Name = "MARQUEEUPDATE";
        Updater.IsBackground = true;

        UpdateMarqueeInfo();
        marqueeText.Text = currentMarqueeText;
        StartMarquee();
    }
    public void ExecuteMarqueeUpdate()
    {
        while (true)
        {
            UpdateMarqueeInfo();
            Thread.Sleep(60000);
        }


    private string currentMarqueeText;

    public void UpdateMarqueeInfo()
    {
        Random r = new Random();
        int i = r.Next(5, 8);
        string s = "";
        for(int x = 0; x < i; x++)
        {
            s += "Is it possible to create a marquee or scrolling text in WPF? ";
        }
        currentMarqueeText = s;
    }

    public void StartMarquee()
    {
        var canvas = (Canvas)marqueeText.Parent;
        if (marqueeText.ActualWidth < canvas.ActualWidth) return;
        var duration = new Duration(TimeSpan.FromSeconds(marqueeText.ActualWidth / 60));
        var animation = new DoubleAnimation(canvas.ActualWidth, -marqueeText.ActualWidth, duration);
        Storyboard.SetTargetName(animation, "rtTTransform");
        Storyboard.SetTargetProperty(animation, new PropertyPath(TranslateTransform.XProperty));
        animation.Completed += OnMarqueeScrollComplete;

        storyboard.Children.Clear();
        storyboard.Children.Add(animation);
        storyboard.Begin(marqueeText);
    }

    private void OnMarqueeScrollComplete(object sender, EventArgs e)
    {
        if (!Updater.IsAlive)
        {
            Updater.Start();
        }

        // Stop the running animation then reset the text.
        // The data updates via the background thread, so just pull as available.
        storyboard.Stop();
        marqueeText.Text = currentMarqueeText;

        // Restart the marquee animation.
        StartMarquee();
    }

答案 1 :(得分:0)

对故事板动画不太了解,但我可以帮助“调用线程无法访问此对象”。

您的问题是计时器事件在计时器线程中触发,并且为了更新UI,您必须在UI线程中运行。解决此问题的最简单方法是使用DispatcherTimer