为什么我的用户控件会导致 Visual Studio 崩溃? [英] Why does my user control crash Visual Studio?

查看:27
本文介绍了为什么我的用户控件会导致 Visual Studio 崩溃?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了一整天试图弄清楚为什么这个用户控件会导致 VS2010(Windows Phone 7.1 开发)崩溃.应用程序运行此控件没有问题,但是当我在 MainPage.xaml 中进入设计模式时 - VS 崩溃.

I've spent all day trying to figure out why this user control crashes VS2010 (Windows Phone 7.1 development). Application runs this control with no problem, but when I go to design mode in MainPage.xaml - VS crash.

<UserControl x:Class="blabla.Controls.Tile"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}">

    <UserControl.Resources>
        <Storyboard x:Name="SwitchSidesAnimation">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="FrontSide">
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="90"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="90"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause1" KeyTime="0:0:6" Value="-90"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause2" KeyTime="0:0:6.2" Value="-90"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause3" KeyTime="0:0:6.4" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause4" KeyTime="0:0:12" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="BackSide">
                <EasingDoubleKeyFrame KeyTime="0" Value="-90"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-90"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause5" KeyTime="0:0:6" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause6" KeyTime="0:0:6.2" Value="90"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause7" KeyTime="0:0:6.4" Value="90"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause8" KeyTime="0:0:12" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="FrontSide">
                <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
                <DiscreteObjectKeyFrame KeyTime="0:0:0.4">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Collapsed</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
                <DiscreteObjectKeyFrame x:Name="MoveThisForPause9" KeyTime="0:0:6">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
                <DiscreteObjectKeyFrame x:Name="MoveThisForPause10" KeyTime="0:0:6.4">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
                <DiscreteObjectKeyFrame x:Name="MoveThisForPause11" KeyTime="0:0:12">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
            </ObjectAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="FrontSide">
                <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause12" KeyTime="0:0:6" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause13" KeyTime="0:0:6.2" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause14" KeyTime="0:0:6.4" Value="0"/>
                <EasingDoubleKeyFrame x:Name="MoveThisForPause15" KeyTime="0:0:12" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>

        <!-- Front side -->
        <Grid x:Name="FrontSide" Grid.Column="0" Grid.Row="0"
              Background="{Binding FrontBackground}">
            <Image Source="{Binding FrontImage}" />
        </Grid>
        <!-- /Front side -->

        <!-- Back side -->
        <Grid x:Name="BackSide" Grid.Column="0" Grid.Row="0"
              Background="{Binding BackBackground}">
            <Grid.Projection>
                <PlaneProjection RotationX="-90" />
            </Grid.Projection>
            <Image Source="{Binding BackImage}" />
        </Grid>
        <!-- /Back side -->
    </Grid>
</UserControl>

现在是代码.

namespace blabla.Controls
{
    public partial class Tile : UserControl
    {
        /// <summary>
        /// Defines if the tile has two sides.
        /// </summary>
        public bool IsTwoSided
        {
            get { return (bool)GetValue(IsTwoSidedProperty); }
            set
            {
                SetValue(IsTwoSidedProperty, value);

                this.startAnimations();
            }
        }

        /// <summary>
        /// Image that will be displayed on front side.
        /// </summary>
        public BitmapImage FrontImage
        {
            get { return (BitmapImage)GetValue(FrontImageProperty); }
            set { SetValue(FrontImageProperty, value); }
        }

        /// <summary>
        /// Image that ill be displayed on back side.
        /// </summary>
        public BitmapImage BackImage
        {
            get { return (BitmapImage)GetValue(BackImageProperty); }
            set { SetValue(BackImageProperty, value); }
        }

        /// <summary>
        /// Brush that will be used as background for front side.
        /// </summary>
        public Brush FrontBackground
        {
            get { return (Brush)GetValue(FrontBackgroundProperty); }
            set { SetValue(FrontBackgroundProperty, value); }
        }

        /// <summary>
        /// Brush that will be used as background for back side.
        /// </summary>
        public Brush BackBackground
        {
            get { return (Brush)GetValue(BackBackgroundProperty); }
            set { SetValue(BackBackgroundProperty, value); }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////

        public static readonly DependencyProperty IsTwoSidedProperty =
            DependencyProperty.Register("IsTwoSided", typeof(bool), typeof(Tile), new PropertyMetadata(false));

        public static readonly DependencyProperty FrontImageProperty =
            DependencyProperty.Register("FrontImage", typeof(BitmapImage), typeof(Tile), new PropertyMetadata(null));

        public static readonly DependencyProperty BackImageProperty =
            DependencyProperty.Register("BackImage", typeof(BitmapImage), typeof(Tile), new PropertyMetadata(null));

        public static readonly DependencyProperty FrontBackgroundProperty =
            DependencyProperty.Register("FrontBackground", typeof(Brush), typeof(Tile),
            new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"])));

        public static readonly DependencyProperty BackBackgroundProperty =
            DependencyProperty.Register("BackBackground", typeof(Brush), typeof(Tile),
            new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"])));

        ///////////////////////////////////////////////////////////////////////////////////////////////////////

        public Tile()
        {
            InitializeComponent();
            this.LayoutRoot.DataContext = this;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Modifies animation frames' KeyTime to adjust time for new timing.
        /// <param name="pauseTime">Lenght of the pause.</param>
        /// </summary>
        private void setPauses(TimeSpan pauseTime)
        {
            // Sets pauses.
            EasingDoubleKeyFrame frameToModify;

            for(int i = 0; true; i++)
            {
                if(this.FindName("MoveThisForPause" + i) != null)
                {
                    frameToModify = (EasingDoubleKeyFrame)this.FindName("MoveThisForPause" + i);
                    frameToModify.KeyTime = KeyTime.FromTimeSpan(frameToModify.KeyTime.TimeSpan - TimeSpan.FromSeconds(5) + pauseTime);
                }
                else
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Starts animations.
        /// </summary>
        /// <param name="beginTime">Usually delay before first-time animation.</param>
        private void startAnimations()
        {
            // We start animations only if the tile is two sided.
            if(this.IsTwoSided)
            {
                // Stopping previous animation.
                this.SwitchSidesAnimation.Stop();

                // Sets correct pauses.
                this.setPauses(TimeSpan.FromSeconds(new Random().Next(5, 10)));

                // Starts animation.
                this.SwitchSidesAnimation.BeginTime = TimeSpan.FromSeconds(new Random().Next(2, 15));
                this.SwitchSidesAnimation.RepeatBehavior = RepeatBehavior.Forever;
                this.SwitchSidesAnimation.Begin();
            }
            else
            {
                this.SwitchSidesAnimation.Stop();
            }
        }
    }
}

推荐答案

无可否认,我使用 Silverlight 的非电话版本和 Visual Web Dev Express 而不是完整版的 VS 能够重现此崩溃.

I have been able to reproduce this crash, admittedly using the non-Phone version of Silverlight, and in Visual Web Dev Express as opposed to the full version of VS.

问题最终归结为这两个依赖属性声明中指定的默认值:

The problem ultimately comes down to the default values specified in these two dependency property declarations:

    public static readonly DependencyProperty FrontBackgroundProperty =
        DependencyProperty.Register("FrontBackground", typeof(Brush), typeof(Tile),
        new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"])));

    public static readonly DependencyProperty BackBackgroundProperty =
        DependencyProperty.Register("BackBackground", typeof(Brush), typeof(Tile),
        new PropertyMetadata(new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"])));

在我用 null 替换默认值后崩溃消失了(使用 Notepad++ 因为 Visual Web Dev Express 崩溃了),删除了项目的 binobj 文件夹并重新启动 Visual Web Dev Express.当我重新启动 VWDX 时,它抱怨找不到 Tile 类型,但那是因为我删除了 binobj 文件夹.重建解决了这个问题.

The crash went away after I replaced the default values with null (using Notepad++ as Visual Web Dev Express was crashing), deleted the project's bin and obj folders and restarted Visual Web Dev Express. When I restarted VWDX, it complained that it couldn't find the type Tile, but that was because I had deleted the bin and obj folders. A rebuild sorted that out.

我只能猜测到底是什么问题.在 Tile 类被静态初始化时,Application.Current 可能是 null, Application.Current.Resources> 可能是 null,或者 Application.Current.Resources["PhoneAccentColor"] 可能是 null(这会导致转换为 Color 失败,因为 Color 是一个 struct).也许 VS 设计器没有很好地处理类型静态初始化期间抛出的异常?

I can only guess at exactly what the problem is. At the point the Tile class is being statically initialized, Application.Current might be null, Application.Current.Resources might be null, or Application.Current.Resources["PhoneAccentColor"] might be null (which would cause the cast to Color to fail, as Color is a struct). Perhaps the VS designer doesn't handle very well exceptions thrown during static initialization of types?

顺便说一下,我还想指出另外几个潜在的问题.首先,这是您的 IsTwoSided 属性:

Incidentally, I'd also like to point out another couple of potential problems. Firstly, this is your IsTwoSided property:

    /// <summary>
    /// Defines if the tile has two sides.
    /// </summary>
    public bool IsTwoSided
    {
        get { return (bool)GetValue(IsTwoSidedProperty); }
        set
        {
            SetValue(IsTwoSidedProperty, value);

            this.startAnimations();
        }
    }

看起来您希望在 IsTwoSided 依赖项属性更改时调用 startAnimations 方法.您在上面编写的代码无法实现这一点.

It looks like you want the startAnimations method to be called whenever your IsTwoSided dependency property changes. The code you've written above will not achieve that.

当 Silverlight 更改依赖属性的值时,它不会调用您的属性设置器来执行此操作.如果您希望在依赖项属性的值更改时发生事情,请使用 属性更改回调.

When Silverlight changes the value of a dependency property, it doesn't call your the property setter to do this. If you want things to happen when a dependency property's value changes, use a property-changed callback instead.

其次,在 Tile.xaml 中,您在 中声明 Storyboard 如下:

Secondly, in Tile.xaml, you declare the Storyboard in <UserControl.Resources> as follows:

    <Storyboard x:Name="SwitchSidesAnimation">

我建议使用 x:Key 而不是 x:Name,原因有两个:

I would recommend using x:Key instead of x:Name, for two reasons:

  • 资源字典中的所有项目(隐式样式除外)都必须有一个 x:Key 或一个 x:Name 来标识它们.VS 支持使用 x:Name 代替 x:Key,但它作为 仅旧支持机制.

  • all items within resource dictionaries (except implicit styles) must have an x:Key or an x:Name to identify them. VS supports using x:Name in place of x:Key, but that exists as a legacy support mechanism only.

在用户控件 XAML 的元素中使用 x:Name 会导致 VS 在自动-生成了 Tile 类的一部分(在 obj\Debug 中的 Tile.g.cs 中).但是,仅仅因为您可以将 x:Name 粘贴到元素上并不一定意味着您将能够访问生成字段中的相应对象.由于您的 Tile.xaml 中没有名为 SwitchSidesAnimation 的 UIElement(故事板不是 UIElements),SwitchSidesAnimation 字段将始终为 null.

using x:Name in an element in a user-control XAML causes VS to create a field with that name in InitializeComponent() in the auto-generated part of your Tile class (in Tile.g.cs somewhere within obj\Debug). However, just because you can stick x:Name on an element doesn't necessarily mean you'll be able to access the corresponding object in the generated field. Because there is no UIElement named SwitchSidesAnimation in your Tile.xaml (Storyboards are not UIElements), the SwitchSidesAnimation field will always be null.

确实,x 的 MSDN 文档:关键属性(也链接到上面)提到

Indeed, the MSDN documentation for the x:Key attribute (also linked to above) mentions that

使用键值的 FindName 调用不会检索键控资源

A FindName call using a key value will not retrieve a keyed resource

(FindName 是用于按名称查找控件的方法.如果您查看 Tile.g.cs,您会看到它在那里使用.)

(FindName is the method used to look up a control by name. If you look in Tile.g.cs you'll see it used there.)

我建议始终在资源字典中使用 x:Key,这样您就不会相信可以在代码隐藏中直接访问此 Storyboard.

I'd recommend always using x:Key within resource dictionaries so you're not led to believe that you can access this Storyboard directly in code-behind.

要在代码隐藏中访问故事板,请使用

To access the storyboard in code-behind, use

    this.Resources["SwitchSidesAnimation"] as Storyboard

事实上,如果您添加以下属性,您将不必更改您的 startAnimations 方法:

In fact, if you add the following property, you won't have to change your startAnimations method:

    private Storyboard SwitchSidesAnimation
    {
        get { return this.Resources["SwitchSidesAnimation"] as Storyboard; }
    }

这篇关于为什么我的用户控件会导致 Visual Studio 崩溃?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆