Вопрос по xaml, windows-phone-7, user-controls, c# – Почему мой пользовательский элемент управления аварийно завершает работу Visual Studio?

2

Я провел весь день, пытаясь выяснить, почему этот пользовательский элемент управления дает сбой VS2010 (разработка для Windows Phone 7.1). Приложение запускает этот элемент управления без проблем, но когда я перехожу в режим проектирования в MainPage.xaml - VS сбой.

<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();
            }
        }
    }
}
@HenkHolterman Нет, Люк Вудворд нашел проблему. Andrzej
Не могли бы вы удалить эту строку и посмотреть, что произойдет:this.LayoutRoot.DataContext = this;? Erno de Weerd
Есть какие-либо сообщения об ошибках или другие симптомы? Henk Holterman
@ Ничего, просто привязка не работает. ;-) В конце концов, кажется, что это не проблема. Andrzej

Ваш Ответ

2   ответа
0

на борьбу которой у меня ушло почти 3 дня, и, конечно, больше, но, к счастью, здесь я нашел решение (Люк Вудворд).

В моем случае я использовал:

    public static readonly DependencyProperty MyStyleProperty = 
        DependencyProperty.Register(
            "MyStyle",
            typeof(Style), 
            typeof(MainButton), 
            new PropertyMetadata(
                    // using this construct as a default value 
                    // makes VS 2010 SP1 to crush!
                    Application.Current.Resources["SomeStyle"] as Style, 
                    OnPropertyChanged
            )
        );

Таким образом, общая проблема в обеих проблемах заключается в использовании некоторого значения ресурса в качестве значения по умолчанию для DependencyProperty.

Но что еще более трагично, эта проблема возникла только после того, как я применил SP1 для VS 2010 (поскольку я хотел разработать Silverlight 5, для которого требовался SP1 для VS 2010).

Это принесло мне головную боль и много времени для поиска.

К счастью, теперь это решено, спасибо!

4

по общему признанию, используя не-Phone версию Silverlight и в Visual Web Dev Express, в отличие от полной версии VS.

В конечном итоге проблема сводится к значениям по умолчанию, указанным в этих двух объявлениях свойств зависимостей:

    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 терпел крах), удалил проектbin а такжеobj папки и перезапустил Visual Web Dev Express. Когда я перезапустил VWDX, он пожаловался, что не может найти типTile, но это потому, что я удалилbin а такжеobj папки. Перестройка разобралась в этом.

Я могу только догадываться, в чем именно заключается проблема. На данный моментTile класс статически инициализируется,Application.Current возможноnull, Application.Current.Resources возможноnull, или жеApplication.Current.Resources["PhoneAccentColor"] возможноnull (что приведет к приведениюColor потерпеть неудачу, какColor этоstruct). Возможно, конструктор VS не очень хорошо обрабатывает исключения, возникающие при статической инициализации типов?

Между прочим, я также хотел бы указать на еще одну пару потенциальных проблем. Во-первых, это вашIsTwoSided имущество:

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

            this.startAnimations();
        }
    }

Похоже, вы хотитеstartAnimations метод, который будет вызываться всякий раз, когда вашIsTwoSided изменения свойств зависимостей. Код, который вы написали выше, не достигнет этого.

Когда Silverlight изменяет значение свойства зависимостей, он не вызывает вашего установщика свойств, чтобы сделать это. Если вы хотите, чтобы что-то происходило при изменении значения свойства зависимостей, используйтесвойство-измененный обратный вызов вместо.

Во-вторых, вTile.xamlВы объявляетеStoryboard в<UserControl.Resources> следующее:

    <Storyboard x:Name="SwitchSidesAnimation">

Я бы порекомендовал использоватьx:Key вместоx:Nameпо двум причинам:

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.

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.

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

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

(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 в словарях ресурсов, так что вы не можете поверить, что вы можете получить доступ к этой раскадровке непосредственно в коде позади.

Чтобы получить доступ к раскадровке в коде позади, используйте

    this.Resources["SwitchSidesAnimation"] as Storyboard

Фактически, если вы добавите следующее свойство, вам не придется менятьstartAnimations метод:

    private Storyboard SwitchSidesAnimation
    {
        get { return this.Resources["SwitchSidesAnimation"] as Storyboard; }
    }
Неважно, я должен привести аргумент DependencyObject к Tile, и у меня есть экземпляр моего элемента управления. Andrzej
Ладно, кажется, вы правильно разобрались, где проблема. Но после использования обратного вызова с измененным свойством возникает новая проблема. Метод для обратного вызова должен быть статическим, верно? (в этом случае это startAnimations ()). Поэтому я должен сделать startAnimations () статическим. Andrzej
В startAnimations () я использую SwitchSidesAnimation (я создал свойство, как вы предложили), и для доступа к нему я должен сделать SwitchSidesAnimation статическим. Когда это свойство является статическим, я не могу получить доступ к ресурсам управления пользователями через "this.Resources [xxx]". То же самое и с setPauses () (используется в startAnimations ()) - если я сделаю его статичным, я не смогу получить доступ к кадрам анимации через "this.Findname (" MoveThisForPause9 ") & quot; Andrzej

Похожие вопросы