【问题标题】:WPF 'Children' property value in the path '(0).(1)[0].(2)' points to immutable instance of 'System.Windows.Media.TransformCollection'路径 '(0).(1)[0].(2)' 中的 WPF 'Children' 属性值指向 'System.Windows.Media.TransformCollection' 的不可变实例
【发布时间】:2018-11-28 22:04:16
【问题描述】:

我已经为此奋斗了几天,只是找不到答案。希望这里有人可以提供帮助。

我们有一个动画键盘,当用户选择需要键盘输入的 Textblock 控件时会弹出该键盘。动画键盘的代码很好。但它调用代码来调整包含 textblock 控件的网格,以便 textblock 控件始终位于动画键盘的正上方。我看到的问题是,当包含网格的页面关闭时,路径 '(0).(1)[0].(2)' 中的 'Children' 属性值异常指向不可变实例'System.Windows.Media.TransformCollection' 在这一行:
_AppWindowControl.IsEnabled = false;

移除键盘时调用的代码(被“完成”按键隐藏)是这样的:

    /// <summary>
    /// Animation to hide keyboard
    /// </summary>
    private void HideKeyboard()
    {
            if (_verticalOffset != 0)
            {
                TranslateTransform tt = new TranslateTransform();
                DoubleAnimation slide = new DoubleAnimation(_verticalOffset, 0, TimeSpan.FromMilliseconds(400));
                var name = "myTransform" + tt.GetHashCode();
                _mainGrid.RegisterName(name, tt);
                name = "mySlide" + slide.GetHashCode();
                _mainGrid.RegisterName(name, slide);
                _mainGrid.RenderTransform = tt;
                tt.BeginAnimation(TranslateTransform.YProperty, slide);
                _verticalOffset = 0;
            }

            Storyboard sb = (Storyboard)this.TryFindResource("HideKeyboard");
            sb.Completed += new EventHandler(HideKeyboard_Completed);
            sb.Begin(this);
    }

我添加了名称注册,希望能解决问题。但事实并非如此。如果我删除分配_mainGrid.RenderTransform = tt;,则 appWindow 将关闭而不会出现任何错误。

另外,我说关闭键盘时会出现问题。这段代码只是最容易显示的。当键盘出现时,会调用 AdjustScreen,它会创建类似的 TranslateTransform 分配给 _mainGrid.RenderTransform。同样,如果我删除分配,则不会出现问题(也不会出现动画)。否则会出现上述同样的错误。

任何帮助将不胜感激。谢谢!

编辑。这是来自 xaml 文件的 StoryBoard:

        <Storyboard x:Key="HideKeyboard">
        <DoubleAnimationUsingKeyFrames AccelerationRatio=".75" BeginTime="00:00:00" DecelerationRatio=".25" Storyboard.TargetName="KeyboardGrid" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" />
            <!--<SplineDoubleKeyFrame KeyTime="00:00:00.20" Value="-10" />-->
            <!--<SplineDoubleKeyFrame KeyTime="00:00:00.45" Value="450" />-->
            <SplineDoubleKeyFrame KeyTime="00:00:00.25" Value="450" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>`enter code here`

另外,我有一个解决方法,基本上在此处更改 _mainGrid.RenderTransfrom 之前存储它。然后,当调用 HideKeybaord_Completed 处理程序时,它会将其还原。这种方法有效。但这似乎很hackish。

崩溃是应用程序崩溃。大多数时候,我们无论如何都会退出 UI,所以没有人注意到。但是,当我向模型添加新视图时,它会在关闭我的视图时崩溃,因此它不会返回到之前的视图。

【问题讨论】:

  • 对于一个简单的任务来说,代码似乎有点过于复杂。你也可以分享故事板的代码HideKeyboard。您看到的异常仅在输出/调试窗口中,否则应用程序崩溃。
  • 为了笑容,我完全消除了键盘故事板。因此,键盘可见或不可见。根本没有动画。另外,我注释掉了 tt.BeginAnimation() 调用。因此,剩下的就是变换的创建和分配,但它从未被使用过。仅此一项就会在尝试关闭应用程序时使应用程序崩溃。同样,因为转换显然是不可变的。如果我知道如何改变这一点,我会很成功。
  • 如果我理解正确,您希望键盘在文本框获得焦点时滑入,在关闭或文本框失去焦点时滑出。我可以根据附加属性编写一个解决方案,你可以吗?我问它是因为在这种情况下,我们可能还需要调整您的一些代码。此外,如果您可以共享代码的工作示例,那么将其与您的解决方案集成起来会更容易。

标签: wpf rendertransform


【解决方案1】:

如果您没有将 TransformGroup 定义为您正在制作动画的事物的 RenderTransform,则可能会出现此错误。它应该看起来像这样:

<Ellipse>
    <Ellipse.RenderTransform>
        <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" />
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Ellipse.RenderTransform>
</Ellipse>

【讨论】:

    【解决方案2】:

    我试图做出一种虚拟键盘的行为

    xaml

    <StackPanel xmlns:l="clr-namespace:CSharpWPF">
        <StackPanel.Resources>
            <UniformGrid Columns="5"
                         Rows="2"
                         x:Key="dummyKeyboard">
                <Button Content="1" />
                <Button Content="2" />
                <Button Content="3" />
                <Button Content="4" />
                <Button Content="5" />
                <Button Content="A" />
                <Button Content="B" />
                <Button Content="C" />
                <Button Content="D" />
                <Button Content="E" />
            </UniformGrid>
        </StackPanel.Resources>
        <TextBox Text="regular textbox" />
        <TextBox Text="virtual keyboard enabled textbox"
                 l:InputProvider.VirtualKeyboard="{StaticResource dummyKeyboard}" />
        <TextBox Text="another regular textbox" />
        <TextBox Text="another virtual keyboard enabled textbox"
                 l:InputProvider.VirtualKeyboard="{StaticResource dummyKeyboard}" />
        <TextBox Text="one more regular textbox" />
    </StackPanel>
    

    例如,我定义了一个包含一些按钮的 dummyKeyboard。然后我通过设置InputProvider.VirtualKeyboard="{StaticResource dummyKeyboard}"将此键盘分配给一些文本框,您可以手动分配给所有文本框,甚至可以通过文本框的样式分配

    InputProvider 类

    namespace CSharpWPF
    {
        public class InputProvider : DependencyObject
        {
            public static object GetVirtualKeyboard(DependencyObject obj)
            {
                return (object)obj.GetValue(VirtualKeyboardProperty);
            }
    
            public static void SetVirtualKeyboard(DependencyObject obj, object value)
            {
                obj.SetValue(VirtualKeyboardProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for VirtualKeyboard.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty VirtualKeyboardProperty =
                DependencyProperty.RegisterAttached("VirtualKeyboard", typeof(object), typeof(InputProvider), new PropertyMetadata(null, OnVirtualKeyboardChanged));
    
            private static void OnVirtualKeyboardChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                TextBox tb = d as TextBox;
                tb.GotFocus += (sender, ee) => OpenPopup(sender as TextBox);
                tb.LostFocus += (sender, ee) => ((Popup)tb.GetValue(PopupProperty)).IsOpen = false;
            }
    
            private static void OpenPopup(TextBox textBox)
            {
                Popup popup = new Popup() { Child = new ContentControl() { Content = GetVirtualKeyboard(textBox), Focusable = false } };
                FocusManager.SetIsFocusScope(popup.Child, true);
                popup.PlacementTarget = textBox;
                popup.AllowsTransparency = true;
                popup.PopupAnimation = PopupAnimation.Slide;
                textBox.SetValue(PopupProperty, popup);
                popup.IsOpen = true;
            }
    
            // Using a DependencyProperty as the backing store for Popup.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty PopupProperty =
                DependencyProperty.RegisterAttached("Popup", typeof(Popup), typeof(InputProvider), new PropertyMetadata(null));
        }
    }
    

    这个类定义了一个对象类型的附加属性VirtualKeyboard,因此允许您根据需要定义数据模板

    所以这个类监听文本框的 GotFocus 和 LostFocus 事件,并在滑动动画中显示或隐藏虚拟键盘。

    试一试,我希望这可以帮助你达到预期。

    请注意,虚拟键盘实际上是虚拟的,它不会进行任何打字,您需要在项目中实现相同的虚拟键盘时将其替换为实际工作的虚拟键盘。

    【讨论】:

    • 还没有尝试过,但我很好奇这是否负责为键盘后面的页面设置动画,以便移动键盘输入指向的文本块,使其位于弹出式键盘?你看,这实际上就是问题发生的地方。通过将承载文本块的 Grid 的 RenderTransform 属性设置为 TranslateTransform 以便它可以移动,Grid 现在有一个不可变的子级,它会在退出应用程序时导致崩溃。所以,键盘本身不是问题。
    • 通过设置PlacementTarget,Popup 将确保将自己保持在目标控件附近(默认情况下它位于目标控件的底部)。通过设置Placement 属性,我们可以进一步定义弹出位置,即。上、下、右、左等等,包括自定义放置。如果需要,我们可以进一步编写代码来拉取控件以查看。
    猜你喜欢
    • 2012-10-21
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 2021-12-09
    • 1970-01-01
    相关资源
    最近更新 更多