WPF中的Silverlight ChildWindow

11

有没有可能在WPF中制作类似Silverlight的ChildWindow?我试图将Silverlight的ChildWindow适应于WPF,但遇到了转换问题和无法设置Popup的父级的问题。我正在尝试制作与之类似的东西,以便不必在XAML中添加弹出窗口的代码。你有什么想法吗?

4个回答

9
这个类应该可以实现你想要的功能:
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 元素(它会找到要使用蒙板阻止的窗口),并将内容设置为您想要弹出的内容(当然,在需要显示时调用 Show 方法)。您需要自己设计弹出层包装器(例如,带有边框和关闭按钮的内容,可以调用关闭方法),但这不应该很难,并且显然在构造函数中删除占位符按钮(它只是为了向您展示它的外观)。
唯一的问题是,它只适用于其内容(即 Silverlight 中命名为“LayoutRoot”的内容)是网格(在创建新的 WPF/Silverlight 窗口/页面时默认为网格)的窗口。我曾经设置成适用于所有面板,但与 StackPanel 或 DockPanel 一起使用时看起来很奇怪(意料之中)。如果这对您不起作用,请告诉我,我们会想出其他办法。
如果您尝试一下,可能可以使动画看起来更接近原始弹出窗口(也许使用一些缓和方式)。可能还有更好的方法来查找根节点,我只是临时想出了那种方法,但我认为它会起作用(尽管仅适用于其内容已设置为网格的 ContentControl)。
如果您有任何疑问/问题,请告诉我,希望这可以解决您的问题。

你不能在根节点之前动态注入一个Grid父级,完成后再将其删除吗? - George Birbilis

2

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

这些都很好,但我不想要一个新窗口。我想能够在当前窗口上显示一个遮罩,并在其中显示新的ChildWindow。我完全熟悉窗口如何工作以及如何制作窗口和对话框,但这不是我的目标。正如我在上面的帖子中所说,我的目标是在WPF中实现与Silverlight的ChildWindow相同的感觉。 - Jeff
很奇怪,这正是我在寻找的,但它还没有被点赞,所以我没有阅读它。最终我意识到,在WPF中,Window类是我需要的模态对话框。确实,它并不完全像Silverlight的ChildWindow。但只要调用ShowDialog,它就是模态的。如果我只是将构造函数更改为Window,并将Show更改为ShowDialog,则我所有使用ChildWindow的Silverlight代码都可以轻松移植。 - Gabe Halsmer
我认为Silverlight的ChildWindow的Show方法不会阻塞当前线程,而WPF的ShowDialog方法会阻塞它,因此您必须小心,以免破坏代码逻辑。 - George Birbilis

1

他们已经更改了 ChildWindows 和 MessageBox 类 - 自2.0版本开始,MessageBox(和ChildWindow)从WindowControl派生,并不再管理其父窗口的背景或基于其父窗口大小的自身定位。现在应该使用 WindowContainer 来容纳这些控件。 - George Birbilis
顺便说一句,他们说旧版本将继续保留,但不确定在存储库的最新版本中它在哪里。 - George Birbilis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接