直接在一个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>