问题 用于WPF的Silverlight ChildWindow


是否有可能在Silverlight中制作像ChildWindow一样的ChildWindow,但对于WPF?我尝试将Silverlight ChildWindow改编为WPF,但遇到了转换问题而无法设置Popup的Parent。我正在尝试制作一些有效的东西,所以我不必为弹出窗口添加代码到XAML。有任何想法吗?


4636
2018-02-08 23:23


起源



答案:


这个课应该做你想做的事:

public class SilverlightishPopup
{
    private Rectangle maskRectangle = new Rectangle { Fill = new SolidColorBrush(Colors.DarkGray), Opacity = 0.0 };

    public FrameworkElement Parent
    {
        get;
        set;
    }

    public FrameworkElement Content
    {
        get;
        set;
    }

    public SilverlightishPopup()
    {
        Button button = new Button();
        button.Width = 100;
        button.Height = 200;
        button.Content = "I am the popup!";

        button.Click += delegate { Close(); };

        Content = button;
    }

    public void Show()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, new Duration(TimeSpan.FromSeconds(0.5)));

            Storyboard opacityBoard = new Storyboard();
            opacityBoard.Children.Add(opacityAnimation);

            Storyboard.SetTarget(opacityAnimation, maskRectangle);
            Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

            opacityBoard.Completed += delegate
            {
                ScaleTransform scaleTransform = new ScaleTransform(0.0, 0.0, Content.Width / 2.0, Content.Height / 2.0);
                Content.RenderTransform = scaleTransform;

                grid.Children.Add(Content);

                Storyboard scaleBoard = new Storyboard();

                DoubleAnimation scaleXAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleXAnimation);

                Storyboard.SetTarget(scaleXAnimation, Content);
                Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

                DoubleAnimation scaleYAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleYAnimation);

                Storyboard.SetTarget(scaleYAnimation, Content);
                Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

                scaleBoard.Begin();
            };

            opacityBoard.Begin();

            grid.Children.Add(maskRectangle);
        }
    }

    public void Close()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            ScaleTransform scaleTransform = new ScaleTransform(1.0, 1.0, Content.Width / 2.0, Content.Height / 2.0);
            Content.RenderTransform = scaleTransform;

            Storyboard scaleBoard = new Storyboard();

            DoubleAnimation scaleXAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleXAnimation);

            Storyboard.SetTarget(scaleXAnimation, Content);
            Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

            DoubleAnimation scaleYAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleYAnimation);

            Storyboard.SetTarget(scaleYAnimation, Content);
            Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

            scaleBoard.Completed += delegate
            {
                DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, 0.0, new Duration(TimeSpan.FromSeconds(0.5)));

                Storyboard opacityBoard = new Storyboard();
                opacityBoard.Children.Add(opacityAnimation);

                Storyboard.SetTarget(opacityAnimation, maskRectangle);
                Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

                opacityBoard.Completed += delegate
                {
                    grid.Children.Remove(maskRectangle);
                    grid.Children.Remove(Content);
                };

                opacityBoard.Begin();
            };

            scaleBoard.Begin();
        }
    }

    private Grid GetRootGrid()
    {
        FrameworkElement root = Parent;

        while (root is FrameworkElement && root.Parent != null)
        {
            FrameworkElement rootElement = root as FrameworkElement;

            if (rootElement.Parent is FrameworkElement)
            {
                root = rootElement.Parent as FrameworkElement;
            }
        }

        ContentControl contentControl = root as ContentControl;

        return contentControl.Content as Grid;
    }
}

只需将Parent属性设置为父窗口中的任何Framework元素(它将找到使用掩码阻止它的Window),并将内容设置为您想要弹出的任何内容(并在需要时调用Show方法)当然要显示)。你必须自己想出弹出包装器(即带边框的东西和调用close方法的关闭按钮),但这应该不难,显然在构造函数中删除占位符按钮(它就在那里向你展示它的外观)。

唯一的问题是它只适用于具有内容的窗口(即Silverlight中名为“LayoutRoot”的东西)是网格(创建新的WPF / Silverlight窗口/页面时的默认值)。我已将它设置为适用于所有面板,但与StackPanel或DockPanel(如预期)一起使用时看起来很奇怪。如果这对您不起作用,请告诉我,我们会解决一些问题。

如果你玩它,你可能会让动画看起来更接近原始弹出窗口(可能使用一些缓动)。可能还有更好的方法来查找根,我只是想出了那个方法,但我认为它会起作用(尽管再次,只有Contentcontrol的内容设置为网格)。

如果您有任何疑问/问题,请告诉我,我希望这能解决您的问题。


9
2018-02-09 21:44



你不能动态地在根之前注入Grid父级并在完成后删除它吗? - George Birbilis
哇没有xaml的例子??? - user1034912


答案:


这个课应该做你想做的事:

public class SilverlightishPopup
{
    private Rectangle maskRectangle = new Rectangle { Fill = new SolidColorBrush(Colors.DarkGray), Opacity = 0.0 };

    public FrameworkElement Parent
    {
        get;
        set;
    }

    public FrameworkElement Content
    {
        get;
        set;
    }

    public SilverlightishPopup()
    {
        Button button = new Button();
        button.Width = 100;
        button.Height = 200;
        button.Content = "I am the popup!";

        button.Click += delegate { Close(); };

        Content = button;
    }

    public void Show()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, new Duration(TimeSpan.FromSeconds(0.5)));

            Storyboard opacityBoard = new Storyboard();
            opacityBoard.Children.Add(opacityAnimation);

            Storyboard.SetTarget(opacityAnimation, maskRectangle);
            Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

            opacityBoard.Completed += delegate
            {
                ScaleTransform scaleTransform = new ScaleTransform(0.0, 0.0, Content.Width / 2.0, Content.Height / 2.0);
                Content.RenderTransform = scaleTransform;

                grid.Children.Add(Content);

                Storyboard scaleBoard = new Storyboard();

                DoubleAnimation scaleXAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleXAnimation);

                Storyboard.SetTarget(scaleXAnimation, Content);
                Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

                DoubleAnimation scaleYAnimation = new DoubleAnimation(1.0, TimeSpan.FromSeconds(0.5));

                scaleBoard.Children.Add(scaleYAnimation);

                Storyboard.SetTarget(scaleYAnimation, Content);
                Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

                scaleBoard.Begin();
            };

            opacityBoard.Begin();

            grid.Children.Add(maskRectangle);
        }
    }

    public void Close()
    {
        Grid grid = GetRootGrid();

        if (grid != null)
        {
            ScaleTransform scaleTransform = new ScaleTransform(1.0, 1.0, Content.Width / 2.0, Content.Height / 2.0);
            Content.RenderTransform = scaleTransform;

            Storyboard scaleBoard = new Storyboard();

            DoubleAnimation scaleXAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleXAnimation);

            Storyboard.SetTarget(scaleXAnimation, Content);
            Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));

            DoubleAnimation scaleYAnimation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(0.5));

            scaleBoard.Children.Add(scaleYAnimation);

            Storyboard.SetTarget(scaleYAnimation, Content);
            Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

            scaleBoard.Completed += delegate
            {
                DoubleAnimation opacityAnimation = new DoubleAnimation(0.5, 0.0, new Duration(TimeSpan.FromSeconds(0.5)));

                Storyboard opacityBoard = new Storyboard();
                opacityBoard.Children.Add(opacityAnimation);

                Storyboard.SetTarget(opacityAnimation, maskRectangle);
                Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath("(Opacity)"));

                opacityBoard.Completed += delegate
                {
                    grid.Children.Remove(maskRectangle);
                    grid.Children.Remove(Content);
                };

                opacityBoard.Begin();
            };

            scaleBoard.Begin();
        }
    }

    private Grid GetRootGrid()
    {
        FrameworkElement root = Parent;

        while (root is FrameworkElement && root.Parent != null)
        {
            FrameworkElement rootElement = root as FrameworkElement;

            if (rootElement.Parent is FrameworkElement)
            {
                root = rootElement.Parent as FrameworkElement;
            }
        }

        ContentControl contentControl = root as ContentControl;

        return contentControl.Content as Grid;
    }
}

只需将Parent属性设置为父窗口中的任何Framework元素(它将找到使用掩码阻止它的Window),并将内容设置为您想要弹出的任何内容(并在需要时调用Show方法)当然要显示)。你必须自己想出弹出包装器(即带边框的东西和调用close方法的关闭按钮),但这应该不难,显然在构造函数中删除占位符按钮(它就在那里向你展示它的外观)。

唯一的问题是它只适用于具有内容的窗口(即Silverlight中名为“LayoutRoot”的东西)是网格(创建新的WPF / Silverlight窗口/页面时的默认值)。我已将它设置为适用于所有面板,但与StackPanel或DockPanel(如预期)一起使用时看起来很奇怪。如果这对您不起作用,请告诉我,我们会解决一些问题。

如果你玩它,你可能会让动画看起来更接近原始弹出窗口(可能使用一些缓动)。可能还有更好的方法来查找根,我只是想出了那个方法,但我认为它会起作用(尽管再次,只有Contentcontrol的内容设置为网格)。

如果您有任何疑问/问题,请告诉我,我希望这能解决您的问题。


9
2018-02-09 21:44



你不能动态地在根之前注入Grid父级并在完成后删除它吗? - George Birbilis
哇没有xaml的例子??? - user1034912


请参阅此处的Extended WPF Toolkit中提供的ChildWindow控件 http://wpftoolkit.codeplex.com/wikipage?title=ChildWindow&referringTitle=Home


2
2017-08-15 15:53



他们在那里更改了ChildWindows和MessageBox类 - “从版本2.0开始,MessageBox(和ChildWindow)从WindowControl派生,不再根据父级的大小管理其父级的背景或自身的位置。现在应该使用WindowContainer包含这些控件。“ - George Birbilis
顺便说一句,他们说旧版本仍然可用,但不确定它在最新版本的存储库中的位置 - George Birbilis


看看吧 BubbleBurst源代码。 GameOverView完全符合您的要求。


2
2018-03-24 08:36





只需从Window派生并从父窗口调用ShowDialog。


1
2018-02-09 00:38



多数民众赞成,但我不想要一个新窗口。我希望能够在当前窗口上显示面纱,并在当前窗口中显示新的ChildWindow。我完全熟悉Windows如何工作以及如何制作窗口和对话框,但这不是我的目标。正如我在上面的帖子中所说,我的目标与Silverlight的ChildWindow一样,但在WPF中。 - Jeff
奇怪的是,这正是我所寻找的,但它还没有被投票,所以我没有读过它。我最终意识到WPF的Window类是我在WPF中进行模态对话所需要的。没错,它与Silverlight的ChildWindow不完全相同。但它是模态的,只要你调用ShowDialog。如果我只是将构造函数更改为Window,则显示使用ChildWindow端口的所有Silverlight代码,以及Show to ShowDialog。 - Gabe Halsmer
我认为Show of Silverlight的ChildWindow不阻止当前线程,而WPF的ShowDialog会阻止它,所以你必须小心你的代码的逻辑不会破坏 - George Birbilis