关于在WPF xaml中包含另一个window的方法

直接在一个window窗口的xaml中包含另一个Window对象,在运行时会直接报错
如:

<Window x:Class="Test.TestWin"
        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"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d" Height="300" Width="300">
    <Grid>
        <Window></Window>
    </Grid>
</Window>

为了解决该问题,可以使用Popup替代Window对象,比如:

<Window x:Class="Test.TestWin"
        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"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d" Height="300" Width="300">
    <Grid>
        <TextBlock Name="textBlock" Text="test"/>
        <Popup Placement="Bottom" PlacementTarget="{Binding ElementName=textBlock}" AllowsTransparency="True">
            <Grid>
                <TextBlock Text="popup"/>
            </Grid>
        </Popup>
    </Grid>
</Window>

Popup与Window属性相似,多出来的几个属性可以用来定位Popup的显示,如PlacementTarget用来确定相对于某个控件来计算位置,Placement用来确定位于PlacementTarget的什么位置(Left、Right、Center、Top、Bottom等),详细信息参考:windows官方文档
但是使用该方法有一个弊端,比如窗口移动时还是要再次更新Popup的位置,及Popup不会跟随父window移动,并且Popup无法移出屏幕,及Popup对象Left或Top不能为负、不能大于屏幕大小,即使PlacementTarget对象已经被移动到屏幕外

另一种解决思路是继承Decorator对象,在Decorator对象的子类作为载体在子类中动态创建Window实例,示例如下:


    public class CustomPopup : Decorator
    {
        public Window contentWindow = null;
        public Window parentWindow = null;

        public CustomPopup()
        {
            CreateContentWindow();
        }

        public object ContentChild
        {
            get { return contentWindow.Content; }
            set { contentWindow.Content = value; }
        }

        private void CreateContentWindow()
        {
            if (contentWindow == null)
            {
                contentWindow.Background = Brushes.Transparent;
                contentWindow.AllowsTransparency = true;
                contentWindow.WindowStyle = WindowStyle.None;

                contentWindow.ShowInTaskbar = false;

                contentWindow.Focusable = false;
            }
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            UpdateChildSize();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            if (contentWindow.Visibility != Visibility.Visible)
            {
                UpdateChildSize();
                contentWindow.Show();
                parentWindow = GetParentWindow(this);
                contentWindow.Owner = parentWindow;
                parentWindow.LocationChanged += ParentWindow_LocationChanged;
                parentWindow.SizeChanged += ParentWindow_SizeChanged;
            }
        }

        private static Window GetParentWindow(DependencyObject o)
        {
            var parent = VisualTreeHelper.GetParent(o);
            if (parent != null)
                return GetParentWindow(parent);
            var fe = o as FrameworkElement;
            if (fe is Window)
                return fe as Window;
            if (fe != null && fe.Parent != null)
                return GetParentWindow(fe.Parent);
            throw new ApplicationException("A window parent could not be found for " + o);
        }

        private void ParentWindow_LocationChanged(object sender, EventArgs e)
        {
            UpdateChildSize();
        }

        private void ParentWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateChildSize();
        }

        private void UpdateChildSize()
        {
            if (parentWindow != null)
            {
                Point hostTopLeft = this.TransformToAncestor(parentWindow).Transform(new Point(0, 0));
                if (parentWindow.WindowState == WindowState.Normal)
                {
                    contentWindow.Left = parentWindow.Left + hostTopLeft.X;
                    contentWindow.Top = parentWindow.Top + hostTopLeft.Y;
                }
                else
                {
                    contentWindow.Left = 0 + hostTopLeft.X;
                    contentWindow.Top = 0 + hostTopLeft.Y;
                }
                contentWindow.Width = ActualWidth;
                contentWindow.Height = ActualHeight;
            }
        }
    }

以上代码中CustomPopup会铺满父窗口,可以根据需求增加类似Popup中的PlacementTarget属性来动态确定相对于父窗口中某一控件的位置,用法与其他控件一样直接在xaml中添加即可,如:

<Window x:Class="Test.TestWin"
        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"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d" Height="300" Width="300">
    <Grid>
         <Test:CustomPopup>
             <Test:CustomPopup.ContentChild >
                 <TextBlock Text="test"/>
             </Test:CustomPopup.ContentChild>
         </Test:CustomPopup>
    </Grid>
</Window>

你可能感兴趣的:(wpf)