Silverlight ChildWindow for WPF
Asked Answered
P

4

11

is it possible to make a ChildWindow like ChildWindow in Silverlight, but for WPF? I tried to adapt the Silverlight ChildWindow to WPF but ran into issues with Transformations and not being able to set the Popup's Parent. I'm trying to make something that works simular so I do not have to add code to the XAML for popups. Any ideas?

Prefix answered 8/2, 2010 at 23:23 Comment(0)
O
9

This class should do what you want to do:

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;
    }
}

Just set the Parent property to any Framework element in the parent window (it'll find the Window to block it with the mask), and set the content to whatever you want to be popped up (and call the Show method when you want it to be shown, of course). You'll have to come up with the pop-up wrapper (i.e. something with a border and a close button that calls the close method) on your own, but it shouldn't be difficult, and obviously remove the placeholder button in the constructor (it's just there to show you how it it will look).

The only problem with this is it will only work on windows that have their content (i.e. the thing that is named "LayoutRoot" in Silverlight) is a grid (the default when you create a new WPF/Silverlight Window/Page). I had it set to work for all panels, but it looks weird when used with a StackPanel or a DockPanel (as expected). If that doesn't work for you, let me know and we'll figure something out.

If you play with it, you can probably get the animation to look closer to the original pop-up (perhaps using some easing). There might also be a better way to find the root, I just came up with that method on the fly, but I think it'll work (though again, only with a Contentcontrol with its content set to a grid).

Let me know if you have any questions/problems, and I hope this solves your problem.

Ornamented answered 9/2, 2010 at 21:44 Comment(1)
can't you inject a Grid parent before the root dynamically and remove it when done?Haroldson
V
2

Have a look at the BubbleBurst Sourcecode. The GameOverView does exactly what you are looking for.

Vacuum answered 24/3, 2010 at 8:36 Comment(0)
O
1

Just derive from Window and call ShowDialog from the parent window.

Ornamented answered 9/2, 2010 at 0:38 Comment(3)
Thats all fine and dandy but I do not want a new window. I want to be able to show a veil over the current window, and have the new ChildWindow shown inside the current. I'm completely familair with how windows works and how to make windows and dialogs, but that is not my goal here. My goal is as I said in the post above, the same feel as silverlight's ChildWindow but in WPF.Prefix
Oddly enough this was what I was looking for, but it hadn't been upvoted yet so I didn't read it. I eventually realized that WPF's Window class is what I needed for a modal dialog in WPF. True, it's not exactly like Silverlight's ChildWindow. But it is modal, as long as you call ShowDialog. All of my Silverlight code that uses ChildWindow ports over easily, if I just change the constructor to Window, and the Show to ShowDialog.Inferno
I think Show of Silverlight's ChildWindow doesn't block the current thread, whereas ShowDialog of WPF blocks it, so you have to be careful your code's logic doesn't breakHaroldson
P
1

See the ChildWindow control thats available in the Extended WPF Toolkit here http://wpftoolkit.codeplex.com/wikipage?title=ChildWindow&referringTitle=Home

Pedicure answered 15/8, 2011 at 15:53 Comment(2)
they've changed the ChildWindows and MessageBox classes there - "Starting with version 2.0, MessageBox (and ChildWindow) is derived from WindowControl and no longer manages its parent’s background or the positioning of itself based on its parent’s size. A WindowContainer should now be used to contain these controls."Haroldson
btw, they say the older version will remain available, but not sure where it is in the latest version of the repositoryHaroldson

© 2022 - 2024 — McMap. All rights reserved.