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
相关产品推荐
相关产品推荐

