WPF中自动增加行(动画)的TextBox
WPF中的Textbox控件是可以自动换行的,只要设置TextWrapping属性为”Wrap”即可,但是存在一个问题:Textbox的高度是固定的,当输入文本过多时就会出现如下情况。
Textbox虽然没有自动增加高度的属性,但是我们可以通过设置来实现这一个功能。相关xaml代码如下
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" Width="36" Height="100">
<TextBox x:Name="textBox" TextWrapping="Wrap" VerticalAlignment="Top" />
</Grid>
效果如下
这里需要注意的是
- 如果设置了textBox的Width、Height或者Margin属性,那么此textBox的大小就已经限制死了,不会自动增加,会出现上图中的情况。
- 如果想设置textBox的大小和位置,需要把textBox放在一个Grid中,通过Grid的属性来控制textBox(上文中xaml文件中的方法)
其实到此为止所需要的功能已经实现了,但是为了让让textbox更美观,我加了一个动画
效果如下:
表面上看是一个textbox,其实是一个Grid加上2个textBox(一个显示,一个隐藏),xaml代码如下
<Grid>
<TextBox x:Name="txtVisible" TextWrapping="Wrap"/>
<TextBox x:Name="txtHidden" TextWrapping="Wrap" Visibility="Hidden" VerticalAlignment="Top"/>
</Grid>
Grid是用来控制textbox的大小的,两个textbox中,显示的那个用于输入文字,隐藏的用于触发动画。原理如下
在txtVisible中输入文字的同时,txtVisible把text传递给txtHidden,当txtHidden中的内容已经满一行时,会触发SizeChange事件,这个事件再触发txtVisible高度变化动画。代码如下
public partial class txt : UserControl
{
private HeightAnimation anim;
private double _animationDuration;
public txt()
{
InitializeComponent();
// Initialize the animation
anim = new HeightAnimation(this);
AnimationDuration = 500; //default value
// Add the handlers to the required events
txtHidden.SizeChanged += TxtHidden_SizeChanged;
txtVisible.TextChanged += TxtVisible_TextChanged;
}
/// <summary>
/// Gets or sets a value indicating whether the control is animated on loaded.
/// </summary>
public bool AnimateOnLoaded { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether the control is animated.
/// </summary>
public bool IsAnimated { get; set; } = true;
/// <summary>
/// Gets or sets the duration of the animation.
/// </summary>
public double AnimationDuration
{
get { return _animationDuration; }
set
{
_animationDuration = value;
anim.Duration = new Duration(TimeSpan.FromMilliseconds(value));
}
}
/// <summary>
/// Gets or sets the text contents of the AnimatedTextBox.
/// </summary>
public string Text
{
get { return txtHidden.Text; }
set
{
txtHidden.Text = value;
txtVisible.Text = value;
}
}
private void TxtVisible_TextChanged(object sender, TextChangedEventArgs e)
{
// When the user's writing in txtVisible, we copy the text to txtHidden
txtHidden.Text = txtVisible.Text;
}
private void TxtHidden_SizeChanged(object sender, SizeChangedEventArgs e)
{
OnHeightChanged(e.PreviousSize.Height, e.NewSize.Height);
}
/// <summary>
/// To execute when the txtHidden's Height has changed.
/// </summary>
private void OnHeightChanged(double previousHeight, double newHeight)
{
//Animation type, increase txtVisible's height or decrease
anim.ChangeType = (newHeight > previousHeight) ? HeightAnimation.ChangeTypes.Increased : HeightAnimation.ChangeTypes.Decreased;
// Animate the Height from the txtHidden's previousHeight to its newHeight
anim.From = previousHeight;
anim.To = newHeight;
// Start the animation
anim.BeginAnimation();
}
/// <summary>
/// Manages the AnimatedTextBox Height's animation.
/// </summary>
private class HeightAnimation
{
private Storyboard sb;
private DoubleAnimation anim;
private double _from;
private double _to;
private Duration _duration;
private FrameworkElement _fe;
private ChangeTypes _changeType;
/// <summary>
/// The possible types of the Height change.
/// </summary>
public enum ChangeTypes
{
Increased,
Decreased
}
/// <summary>
/// Constructor of the class.
/// </summary>
public HeightAnimation(FrameworkElement fe)
{
// Set the FrameworkElement which manages the animation
_fe = fe;
// Initialize the Storyboard
sb = new Storyboard();
sb.AutoReverse = false;
// Initialize the animation
anim = new DoubleAnimation();
anim.Name = "anim";
// Set the EasingFunction on a new instance of CubicEase whose EasingMode is EaseInOut
anim.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut };
// Bind the Animation with the txtVisible TextBox
Storyboard.SetTargetName(anim, "txtVisible");
// Add the animation to the Storyboard's children
sb.Children.Add(anim);
}
/// <summary>
/// Gets or sets the type of the Height change.
/// </summary>
public ChangeTypes ChangeType
{
get { return _changeType; }
set
{
_changeType = value;
/* If the Height has inreased, set the target property to MaxHeight, else to MinHeight
* (instead of animating directly the Height, we animate MaxHeight/MinHeight to prevent the AnimatedTextBox
* from growing/shrinking suddenly) */
Storyboard.SetTargetProperty(anim, new PropertyPath(string.Format("(TextBox.{0})", (value == ChangeTypes.Increased) ? "MaxHeight" : "MinHeight")));
}
}
/// <summary>
/// Gets or sets the animation's starting Height.
/// </summary>
public double From
{
get { return _from; }
set
{
_from = value;
anim.From = value;
}
}
/// <summary>
/// Gets or sets the animation's ending Height.
/// </summary>
public double To
{
get { return _to; }
set
{
_to = value;
anim.To = value;
}
}
/// <summary>
/// Gets or sets the animation's duration.
/// </summary>
public Duration Duration
{
get { return _duration; }
set
{
_duration = value;
anim.Duration = value;
}
}
/// <summary>
/// Begins the animation.
/// </summary>
public void BeginAnimation()
{
_fe.BeginStoryboard(sb);
}
}
}