You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中克隆XAML TextBox对象执行平移动画时出现‘TextBoxAnim找不到’异常的技术求助

解决WPF克隆控件后动画找不到目标的异常问题

嘿,刚接触WPF遇到这种命名相关的坑很正常,我来帮你捋清楚问题出在哪,以及怎么解决~

问题原因

你遇到的System.InvalidOperationException: The name 'TextBoxAnim' could not be found...异常,核心是**WPF命名作用域(Name Scope)**的问题:

  • 原XAML里的Storyboard用了Storyboard.TargetName="TextBoxAnim",这个名称是绑定到原nInput控件内部的TranslateTransform实例的,它只在原控件的命名作用域里有效。
  • 当你通过CloneXaml方法克隆控件时,虽然克隆后的控件内部也有一个叫TextBoxAnim的TranslateTransform,但它属于新的命名作用域,原Storyboard里硬编码的TargetName找不到这个新实例。
  • 另外,你原XAML里的宽度/高度动画还绑定了TargetName="nInput",克隆后这个名称指向的还是原控件,不是克隆后的cloneInputForAnimation,这也会有问题。

解决方案:两种可行的修复方式

方式1:动态创建动画(推荐,更灵活)

放弃在XAML里预定义Storyboard,直接在C#代码中针对克隆后的控件创建动画,完全绕开命名作用域的问题:

// 克隆控件(你的原有代码)
TextBox cloneInputForAnimation = CloneXaml(nInput);
UserInteraction.Children.Add(cloneInputForAnimation);
nInput.Text = "";

TextBox targetInput = CloneXaml(cloneInputForAnimation);
targetInput.Visibility = Visibility.Hidden;
targetInput.Width = Double.NaN;
targetInput.Height = 50;
nList.Children.Add(targetInput);

Point targetPosition = targetInput.TransformToAncestor(rootView).Transform(new Point(0, 0));
Point currentPos = cloneInputForAnimation.TransformToAncestor(rootView).Transform(new Point(0, 0));
Vector translateToDo = targetPosition - currentPos;

// --------------------------
// 动态创建所有动画(替换原有从Resources取Storyboard的代码)
// --------------------------
// X轴平移动画
DoubleAnimation animX = new DoubleAnimation
{
    To = translateToDo.X,
    Duration = TimeSpan.FromSeconds(0.8)
};
Storyboard.SetTarget(animX, cloneInputForAnimation.RenderTransform);
Storyboard.SetTargetProperty(animX, new PropertyPath(TranslateTransform.XProperty));

// Y轴平移动画
DoubleAnimation animY = new DoubleAnimation
{
    To = translateToDo.Y,
    Duration = TimeSpan.FromSeconds(0.8)
};
Storyboard.SetTarget(animY, cloneInputForAnimation.RenderTransform);
Storyboard.SetTargetProperty(animY, new PropertyPath(TranslateTransform.YProperty));

// 宽度动画
DoubleAnimation animWidth = new DoubleAnimation
{
    To = targetInput.ActualWidth,
    Duration = TimeSpan.FromSeconds(0.8)
};
Storyboard.SetTarget(animWidth, cloneInputForAnimation);
Storyboard.SetTargetProperty(animWidth, new PropertyPath(TextBox.WidthProperty));

// 高度动画
DoubleAnimation animHeight = new DoubleAnimation
{
    To = targetInput.ActualHeight,
    Duration = TimeSpan.FromSeconds(0.8)
};
Storyboard.SetTarget(animHeight, cloneInputForAnimation);
Storyboard.SetTargetProperty(animHeight, new PropertyPath(TextBox.HeightProperty));

// 合并到Storyboard并启动
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animX);
storyboard.Children.Add(animY);
storyboard.Children.Add(animWidth);
storyboard.Children.Add(animHeight);

storyboard.Begin(cloneInputForAnimation);

方式2:修改原有Storyboard的目标(适合保留XAML定义的场景)

如果你想继续用XAML里的Storyboard,需要在克隆后手动更新每个动画的目标,替换掉硬编码的TargetName:

// 克隆控件后,获取Resources里的Storyboard
Storyboard myStoryY = cloneInputForAnimation.Resources["myStoryboardY"] as Storyboard;
Storyboard myStoryX = cloneInputForAnimation.Resources["myStoryboardX"] as Storyboard;
Storyboard myStoryHeight = cloneInputForAnimation.Resources["myStoryboardHeight"] as Storyboard;
Storyboard myStoryWidth = cloneInputForAnimation.Resources["myStoryboardWidth"] as Storyboard;

// 获取克隆后的TranslateTransform
TranslateTransform clonedTransform = cloneInputForAnimation.RenderTransform as TranslateTransform;

// 更新平移动画的目标
DoubleAnimation myAnimY = myStoryY.Children[0] as DoubleAnimation;
Storyboard.SetTarget(myAnimY, clonedTransform); // 替换TargetName为实际实例

DoubleAnimation myAnimX = myStoryX.Children[0] as DoubleAnimation;
Storyboard.SetTarget(myAnimX, clonedTransform);

// 更新宽高动画的目标(原XAML绑定的是nInput,现在要改成克隆后的控件)
DoubleAnimation myAnimHeight = myStoryHeight.Children[0] as DoubleAnimation;
Storyboard.SetTarget(myAnimHeight, cloneInputForAnimation);

DoubleAnimation myAnimWidth = myStoryWidth.Children[0] as DoubleAnimation;
Storyboard.SetTarget(myAnimWidth, cloneInputForAnimation);

// 继续设置动画参数并启动
myAnimY.To = translateToDo.Y;
myAnimX.To = translateToDo.X;
myAnimHeight.To = targetInput.ActualHeight;
myAnimWidth.To = targetInput.ActualWidth;

cloneInputForAnimation.BeginStoryboard(myStoryY);
cloneInputForAnimation.BeginStoryboard(myStoryX);
cloneInputForAnimation.BeginStoryboard(myStoryHeight);
cloneInputForAnimation.BeginStoryboard(myStoryWidth);

小提示(给刚学WPF的你)

  • 命名作用域:WPF里的元素名称(x:Name)是在所属的容器(比如Window、UserControl)的命名作用域里注册的,动态创建/克隆的控件如果没加入到有命名作用域的容器,或者没手动注册名称,用TargetName的Storyboard就找不到目标。
  • 优先用Storyboard.Target:相比硬编码TargetName,直接绑定到控件实例的Target方式更灵活,尤其适合动态创建或克隆控件的场景。

内容的提问来源于stack exchange,提问作者DreamingCodes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 22:09:05