Scrolling Marquee
I need to create a scrolling Marquee that only scrolls when the text would be trimmed. (CharacterEllipsis is used)
So I subclassed Textblock, and was going to setup a DoubleAnimation that would accomplish this. But how do I tell when the text would be trimmed? There doesn't seem to be a IsTextTrimmed property... that would have been just too easy.
Would a better solution be to derive from control, and override OnRender? Then I could maybe use the FormattedText class to get all the extra functionality that TextBlock already has built in.
A third, and maybe easiest way would be to do this from a Style and a Trigger and I wouldn't subclass anything. But is this too low level to accomplish this way?
I'm looking for the "best" solution here (not wanting to re-invent the wheel), so any help would greatly be appreciated.
回答
- I've mocked up with a TextBlock derivative:
public class TextBlockEx : TextBlock
I have defined three readonly dependency properties aka TextExtentWidth, TextTrimmedWidth, and IsTextTrimmed, and calculate their values in the LayoutUpdated event handler, with those three properties at hand, you should be able to properly implement the animation logic using bare-bones DoubleAnimation
{
public TextBlockEx() : base()
{
this.LayoutUpdated += delegate
{
CultureInfo cultureInfo = this.Language.GetEquivalentCulture();
FormattedText ft = new FormattedText(
this.Text,
cultureInfo,
this.FlowDirection,
new Typeface(
this.FontFamily,
this.FontStyle,
this.FontWeight,
this.FontStretch,
null),
this.FontSize,
this.Foreground);
base.SetValue(IsTextTrimmedPropertyKey, ft.Width > this.ActualWidth);
base.SetValue(TextTrimmedWidthPropertyKey, ft.Width - this.ActualWidth);
base.SetValue(TextExtentWidthPropertyKey, ft.Width);
};
}
private static readonly DependencyPropertyKey IsTextTrimmedPropertyKey = DependencyProperty.RegisterReadOnly(
"IsTextTrimmed",
typeof(Boolean),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsTextTrimmedProperty = IsTextTrimmedPropertyKey.DependencyProperty;
public Boolean IsTextTrimmed
{
get { return (Boolean)GetValue(IsTextTrimmedProperty); }
}
private static readonly DependencyPropertyKey TextTrimmedWidthPropertyKey = DependencyProperty.RegisterReadOnly(
"TextTrimmedWidth",
typeof(Double),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(0.0d));
public static readonly DependencyProperty TextTrimmedWidthProperty = TextTrimmedWidthPropertyKey.DependencyProperty;
public Double TextTrimmedWidth
{
get { return (Double)GetValue(TextTrimmedWidthProperty); }
}
private static readonly DependencyPropertyKey TextExtentWidthPropertyKey = DependencyProperty.RegisterReadOnly(
"TextExtentWidth",
typeof(Double),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(0.0d));
public static readonly DependencyProperty TextExtentWidthProperty = TextExtentWidthPropertyKey.DependencyProperty;
public Double TextExtentWidth
{
get { return (Double)GetValue(TextExtentWidthProperty); }
}
}
.
Hope this helps
すべての返信
Well, I ended up subclassing Canvas, adding a private Textblock to it, and animating Canvas.Left. However, I couldn't get my DoubleAnimationUsingKeyFrames to work out how I wanted (I could not get a constant speed that would repeat forever).
Here should be my keyframes in Plain English:
Wait 1 second
Do
Scroll left until the Textblock is off screen
DiscreteDoubleKeyFrame move the block's Canvas.Left to the Canvas's Width
Scroll until the Textblock's Canvas.Left is 0
Repeat while Visible
The catch is the speed my textblock moves should be a constant now matter how long the TextBlock is, and this speed ratio needs to be a DependencyProperty you can set.
Now, I rebuild my animation dynamically when visible or the length of the Textblock changes, so the issue is getting my KeyTimes right.
I tried several methods, even calculating the time by hand, using Keytime.Uniform or Keytime.Paced, etc.
Any ideas?
- I've mocked up with a TextBlock derivative:
public class TextBlockEx : TextBlock
I have defined three readonly dependency properties aka TextExtentWidth, TextTrimmedWidth, and IsTextTrimmed, and calculate their values in the LayoutUpdated event handler, with those three properties at hand, you should be able to properly implement the animation logic using bare-bones DoubleAnimation
{
public TextBlockEx() : base()
{
this.LayoutUpdated += delegate
{
CultureInfo cultureInfo = this.Language.GetEquivalentCulture();
FormattedText ft = new FormattedText(
this.Text,
cultureInfo,
this.FlowDirection,
new Typeface(
this.FontFamily,
this.FontStyle,
this.FontWeight,
this.FontStretch,
null),
this.FontSize,
this.Foreground);
base.SetValue(IsTextTrimmedPropertyKey, ft.Width > this.ActualWidth);
base.SetValue(TextTrimmedWidthPropertyKey, ft.Width - this.ActualWidth);
base.SetValue(TextExtentWidthPropertyKey, ft.Width);
};
}
private static readonly DependencyPropertyKey IsTextTrimmedPropertyKey = DependencyProperty.RegisterReadOnly(
"IsTextTrimmed",
typeof(Boolean),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsTextTrimmedProperty = IsTextTrimmedPropertyKey.DependencyProperty;
public Boolean IsTextTrimmed
{
get { return (Boolean)GetValue(IsTextTrimmedProperty); }
}
private static readonly DependencyPropertyKey TextTrimmedWidthPropertyKey = DependencyProperty.RegisterReadOnly(
"TextTrimmedWidth",
typeof(Double),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(0.0d));
public static readonly DependencyProperty TextTrimmedWidthProperty = TextTrimmedWidthPropertyKey.DependencyProperty;
public Double TextTrimmedWidth
{
get { return (Double)GetValue(TextTrimmedWidthProperty); }
}
private static readonly DependencyPropertyKey TextExtentWidthPropertyKey = DependencyProperty.RegisterReadOnly(
"TextExtentWidth",
typeof(Double),
typeof(TextBlockEx),
new FrameworkPropertyMetadata(0.0d));
public static readonly DependencyProperty TextExtentWidthProperty = TextExtentWidthPropertyKey.DependencyProperty;
public Double TextExtentWidth
{
get { return (Double)GetValue(TextExtentWidthProperty); }
}
}
.
Hope this helps - Thanks! that points me in the right direction.

