【问题标题】:Silverlight Popup UserControl - Works once, then crashesSilverlight Popup UserControl - 工作一次,然后崩溃
【发布时间】:2019-03-07 10:02:10
【问题描述】:

我正在尝试制作 Silverlight2 UserControl 来模拟 WinForms MessageBox 的基本功能。我在关注Shawn WildermuthJohn Papa's 的例子。

编译正常,我将用户控件添加到我的应用程序中,并带有一个按钮以显示弹出窗口。这有效 - 出现弹出窗口,您可以单击按钮将其关闭并关闭。您可以多次执行此操作。

如果包含“MessageBox”的 UserControl 已关闭并创建了一个新的,则会出现此问题。如果您尝试在新实例上打开弹出窗口,则会引发异常:

Unhandled Error in Silverlight 2 Application
Code: 4004
Category: ManagedRuntimeError
Message: System.ArgumentException: Value does not fall within the expected range.
  at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
  at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize)
  at System.Windows.FrameworkElement.MeasureOverride(Size availableSize)
  ...
  (stack trace doesnt go as far as any of my code)

控件的代码在帖子的底部。如果你想运行它,我已经做了一个testbench project to demonstrate the problem. 测试台有屏幕上的说明告诉你如何重现问题。

这一直让我发疯,所以任何帮助或指点将不胜感激。

这里是 PopupMessage 用户控件的代码:

<UserControl x:Class="SilverlightPopupControlTest.PopupMessage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" 
    >
    <Grid x:Name="LayoutRoot">

        <vsm:VisualStateManager.VisualStateGroups>
            <vsm:VisualStateGroup x:Name="PopupState" CurrentStateChanged="PopupState_CurrentStateChanged">
                <vsm:VisualStateGroup.Transitions>
                    <vsm:VisualTransition GeneratedDuration="00:00:00.2000000"/>
                    <vsm:VisualTransition GeneratedDuration="00:00:00.2000000" To="PopupStateOpened"/>
                    <vsm:VisualTransition GeneratedDuration="00:00:00.2000000" To="PopupStateClosed"/>
                </vsm:VisualStateGroup.Transitions>
                <vsm:VisualState x:Name="PopupStateClosed">
                    <Storyboard>
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="PopupFillGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#00FFFFFF"/>
                        </ColorAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>
                        </DoubleAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>
                        </DoubleAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.Opacity)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </vsm:VisualState>
                <vsm:VisualState x:Name="PopupStateOpened">
                    <Storyboard>
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="PopupFillGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#BFFFFFFF"/>
                        </ColorAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                        </DoubleAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                        </DoubleAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.Opacity)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </vsm:VisualState>
            </vsm:VisualStateGroup>
        </vsm:VisualStateManager.VisualStateGroups>

        <Popup x:Name="PopupControl">
            <Grid x:Name="PopupFillGrid">
                <Grid.Background>
                    <SolidColorBrush Color="#00ffffff"/>
                </Grid.Background>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <Border Grid.Row="1" Grid.Column="1" RenderTransformOrigin="0.5,0.5" x:Name="border" Opacity="0" >
                    <Border.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </Border.RenderTransform>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="40"/>
                        </Grid.RowDefinitions>

                        <ContentPresenter x:Name="PopupContentPresenter"/>

                        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
                            <Button x:Name="Button1" Click="Button1_Click" Margin="4" MinWidth="80">
                                <ContentPresenter x:Name="Button1ContentPresenter"/>
                            </Button>
                            <Button x:Name="Button2" Click="Button2_Click" Margin="4" MinWidth="80">
                                <ContentPresenter x:Name="Button2ContentPresenter"/>
                            </Button>
                            <Button x:Name="Button3" Click="Button3_Click"  Margin="4" MinWidth="80">
                                <ContentPresenter x:Name="Button3ContentPresenter"/>
                            </Button>
                        </StackPanel>
                    </Grid>
                </Border>
            </Grid>
        </Popup>
    </Grid>
</UserControl>

及其代码隐藏

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightPopupControlTest
{
    public partial class PopupMessage : UserControl
    {
        #region PopupContent Dependency Property
        public UIElement PopupContent
        {
            get { return (UIElement)GetValue(PopupContentProperty); }
            set { SetValue(PopupContentProperty, value); }
        }
        public static readonly DependencyProperty PopupContentProperty = DependencyProperty.Register(
            "PopupContent", typeof(UIElement), typeof(PopupMessage), new PropertyMetadata(PopupContentChanged));

        private static void PopupContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnPopupContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue);
        }

        private void OnPopupContentChanged(UIElement oldValue, UIElement newValue)
        {
            this.PopupContentPresenter.Content = newValue;
        }
        #endregion
        #region Button1Visibility Dependency Property
        public Visibility Button1Visibility
        {
            get { return (Visibility)GetValue(Button1VisibilityProperty); }
            set { SetValue(Button1VisibilityProperty, value); }
        }
        public static readonly DependencyProperty Button1VisibilityProperty = DependencyProperty.Register(
            "Button1Visibility", typeof(Visibility), typeof(PopupMessage), new PropertyMetadata(Button1VisibilityChanged));


        private static void Button1VisibilityChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton1VisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
        }

        private void OnButton1VisibilityChanged(Visibility oldValue, Visibility newValue)
        {
            this.Button1.Visibility = newValue;
        }
        #endregion
        #region Button2Visibility Dependency Property
        public Visibility Button2Visibility
        {
            get { return (Visibility)GetValue(Button2VisibilityProperty); }
            set { SetValue(Button2VisibilityProperty, value); }
        }
        public static readonly DependencyProperty Button2VisibilityProperty = DependencyProperty.Register(
            "Button2Visibility", typeof(Visibility), typeof(PopupMessage), new PropertyMetadata(Button2VisibilityChanged));

        private static void Button2VisibilityChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton2VisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
        }

        private void OnButton2VisibilityChanged(Visibility oldValue, Visibility newValue)
        {
            this.Button2.Visibility = newValue;
        }
        #endregion
        #region Button3Visibility Dependency Property
        public Visibility Button3Visibility
        {
            get { return (Visibility)GetValue(Button3VisibilityProperty); }
            set { SetValue(Button3VisibilityProperty, value); }
        }
        public static readonly DependencyProperty Button3VisibilityProperty = DependencyProperty.Register(
            "Button3Visibility", typeof(Visibility), typeof(PopupMessage), new PropertyMetadata(Button3VisibilityChanged));


        private static void Button3VisibilityChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton3VisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
        }

        private void OnButton3VisibilityChanged(Visibility oldValue, Visibility newValue)
        {
            this.Button3.Visibility = newValue;
        }
        #endregion
        #region Button1Content Dependency Property
        public UIElement Button1Content
        {
            get { return (UIElement)GetValue(Button1ContentProperty); }
            set { SetValue(Button1ContentProperty, value); }
        }
        public static readonly DependencyProperty Button1ContentProperty = DependencyProperty.Register(
            "Button1Content", typeof(UIElement), typeof(PopupMessage), new PropertyMetadata(Button1ContentChanged));

        private static void Button1ContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton1ContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue);
        }

        private void OnButton1ContentChanged(UIElement oldValue, UIElement newValue)
        {
            this.Button1ContentPresenter.Content = newValue;
        }
        #endregion
        #region Button2Content Dependency Property
        public UIElement Button2Content
        {
            get { return (UIElement)GetValue(Button2ContentProperty); }
            set { SetValue(Button2ContentProperty, value); }
        }
        public static readonly DependencyProperty Button2ContentProperty = DependencyProperty.Register(
            "Button2Content", typeof(UIElement), typeof(PopupMessage), new PropertyMetadata(Button2ContentChanged));


        private static void Button2ContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton2ContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue);
        }

        private void OnButton2ContentChanged(UIElement oldValue, UIElement newValue)
        {
            this.Button2ContentPresenter.Content = newValue;
        }
        #endregion
        #region Button3Content Dependency Property
        public UIElement Button3Content
        {
            get { return (UIElement)GetValue(Button3ContentProperty); }
            set { SetValue(Button3ContentProperty, value); }
        }
        public static readonly DependencyProperty Button3ContentProperty = DependencyProperty.Register(
            "Button3Content", typeof(UIElement), typeof(PopupMessage), new PropertyMetadata(Button3ContentChanged));

        private static void Button3ContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((PopupMessage)o).OnButton3ContentChanged((UIElement)e.OldValue, (UIElement)e.NewValue);
        }

        private void OnButton3ContentChanged(UIElement oldValue, UIElement newValue)
        {
            this.Button3ContentPresenter.Content = newValue;
        }
        #endregion

        public enum PopupMessageResult
        {
            Unknown=0,
            Button1=1,
            Button2,
            Button3
        }

        #region PopupMessageClosed Event
        public class PopupMessageClosedEventArgs : EventArgs
        {
            public PopupMessage.PopupMessageResult Result { get; set; }

            public PopupMessageClosedEventArgs()
            {
            }

            public PopupMessageClosedEventArgs(PopupMessage.PopupMessageResult _Result)
            {
                this.Result = _Result;
            }

        }

        public delegate void PopupMessageClosedEventHandler(object sender, PopupMessageClosedEventArgs e);

        public event PopupMessageClosedEventHandler PopupMessageClosed;

        protected virtual void OnPopupMessageClosed(PopupMessageClosedEventArgs e)
        {
            if (PopupMessageClosed != null)
            {
                PopupMessageClosed(this, e);
            }
        }
        #endregion

        private PopupMessageResult m_Result = PopupMessageResult.Unknown;
        public PopupMessageResult Result
        {
            get { return m_Result; }
            set { m_Result = value; }
        }

        private FrameworkElement m_HostControl = null;
        public FrameworkElement HostControl
        {
            get { return m_HostControl; }
            set { m_HostControl = value; }
        }

        public PopupMessage()
        {
            InitializeComponent();

            this.Button1Visibility = Visibility.Collapsed;
            this.Button2Visibility = Visibility.Collapsed;
            this.Button3Visibility = Visibility.Collapsed;

            VisualStateManager.GoToState(this, "PopupStateClosed", false);
        }

        private void Close()
        {
            VisualStateManager.GoToState(this, "PopupStateClosed", true);
        }

        public void Show()
        {

            this.PopupControl.IsOpen = true;
            this.Visibility = Visibility.Visible;
            this.TabNavigation = KeyboardNavigationMode.Cycle;
            this.Button1.Focus();

            VisualStateManager.GoToState(this, "PopupStateOpened", true);
        }

        void PopupState_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
        {
            if (e.NewState.Name == "PopupStateClosed")
            {
                this.PopupControl.IsOpen = false;
                this.Visibility = Visibility.Collapsed;

                this.OnPopupMessageClosed(new PopupMessageClosedEventArgs(this.Result));
            }
        }

        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            this.Result = PopupMessageResult.Button1;
            this.Close();
        }

        private void Button2_Click(object sender, RoutedEventArgs e)
        {
            this.Result = PopupMessageResult.Button2;
            this.Close();
        }

        private void Button3_Click(object sender, RoutedEventArgs e)
        {
            this.Result = PopupMessageResult.Button3;
            this.Close();
        }
    }
}

【问题讨论】:

    标签: silverlight user-controls


    【解决方案1】:

    你得到的实际错误信息如下:

    如果您将 Page.xaml.cs 重构为如下所示,它将修复您的错误:

    public partial class Page : UserControl
    {
        private PopupHost popupHost1 = new PopupHost();
    
        public Page()
        {
            InitializeComponent();
        }
    
        private void OpenButton_Click(object sender, RoutedEventArgs e)
        {
            this.PageContent.Children.Add(popupHost1);
        }
    
        private void CloseButton_Click(object sender, RoutedEventArgs e)
        {
            this.PageContent.Children.Remove(popupHost1);
        }
    }
    

    【讨论】:

    • 非常感谢您的帮助。突出显示我在我的主应用程序中做坏事。
    • 所以问题是 popupHost 没有从可视化树中删除?
    • 是的 - Button1ContentPresenter 没有在调用 Children.Clear 时被删除。查看 Reflector 中 UIElementCollection 的 Silverlight 版本(子元素的类型),您会看到 Clear 不会在每个元素上调用 Remove,而 Remove 会调用 _visualChildren.Remove(element)
    • 感谢您的提示。在我的应用程序中,托管弹出窗口的页面位于 ViewBox 中,该 ViewBox 有 Child 而不是 Children,除了设置为 null 之外,我看不到执行 Remove()。
    • 我最终从按钮中删除了 ContentPresenters 并直接设置了它们的内容。这似乎奏效了,但是我不确定幕后是否发生了令人讨厌的事情。我在玩测试台时发现了一些有趣的东西。
    【解决方案2】:

    仅供参考,作为 2009 年 3 月新版 Silverlight Toolkit 的一部分,我们提供了一个 Picker 基类。 该 Picker 类提供弹出功能,您无需手动定位和显示弹出窗口。

    如果您有兴趣,请给我留言,我会尽力在此处发布有关如何使用 Picker 的快速教程。 基本上:

    1. 创建一个继承自 Picker 的类
    2. 在 Blend 中,编辑控件的模板
      1. 在模板的弹出窗口中,在弹出窗口中放置您想要的任何内容(如按钮或其他东西)
      2. 在模板中的切换按钮旁边放置您想要展示弹出窗口关闭时的值的任何可视化。
    3. 为您在 2.1 和 2.2 中创建的元素公开 TemplateParts。
    4. 在 OnApplyTemplate 中使用 GetTemplatePart 抓取那些 TemplatePart 的实例
    5. 注册这些控件上的事件以响应其更改并更新控件。

    【讨论】:

    • 使用这个基类并不是那么简单。对此有进一步的指导吗?我在 msdn 论坛上也看到有人问你,但我没有看到任何实现,除了工具包中非常特定于 Datetime 的那些。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    相关资源
    最近更新 更多