Popup
控件中,很难克服。如果您愿意付出一些努力,可以通过调整弹出窗口的大小和位置来解决这个问题。为了演示的目的,我们将重点放在屏幕左侧。<Window ...
LocationChanged="Window_LocationChanged"
SizeChanged="Window_SizeChanged"
>
<Grid>
<Rectangle Name="rectangle1" Width="100" Height="100" Fill="Blue"/>
<Popup Name="popup1" PlacementTarget="{Binding ElementName=rectangle1}" IsOpen="True" Width="100" Height="100">
<TextBlock Background="White" TextWrapping="Wrap" Width="100" Height="100">
<TextBlock.Text>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</TextBlock.Text>
</TextBlock>
</Popup>
</Grid>
</Window>
并且 code-behind 如下:
private void Window_LocationChanged(object sender, EventArgs e)
{
RefreshPopupPosition();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
RefreshPopupPosition();
}
private void RefreshPopupPosition()
{
var upperLeft = rectangle1.PointToScreen(new Point(0, 100));
var xOffset = Math.Min(0, upperLeft.X);
popup1.Width = xOffset + 100;
(popup1.Child as FrameworkElement).Margin = new Thickness(xOffset, 0, 0, 0);
popup1.HorizontalOffset += 1;
popup1.HorizontalOffset -= 1;
}
计算出 Popup
将会超出屏幕后,我们可以减少内容的宽度并赋予负边距,这样在屏幕上显示的部分就被剪切到如果 Popup
允许的话应该出现的位置。
这个方法需要扩展以应对屏幕的四个边缘和多屏幕的可能性,但它表明了这种方法是可行的。
我认为没有一种干净的方式来实现这个。如果你使用反射查看Popup类,你会发现一个UpdatePosition
方法,它将调整弹出窗口的位置,使其保持在屏幕边界之间。这个方法从几个地方调用,从On***Changed
回调函数(OnPlacementChanged
,OnVerticalOffsetChanged
等)。
如果你真的非常想要这个功能,你可能有一点机会扩展Popup类,覆盖一些注册了On***Changed
回调的属性元数据,以便UpdatePosition
永远不会被调用,但大多数人都认为这样做是纯粹的邪恶,我也不建议这样做。
在WPF框架中无法将弹出窗口放置在屏幕之外,但是您可以通过调用"P/invoke"中的"SetWindowPos"来强制设置弹出窗口位置:
#region P/Invoke imports & definitions
private const int HWND_TOPMOST = -1;
private const int HWND_NOTOPMOST = -2;
private const int HWND_BOTTOM = 1;
private const int SWP_NOMOVE = 0x0002;
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOACTIVATE = 0x0010;
private const int GW_HWNDPREV = 3;
private const int GW_HWNDLAST = 1;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags);
#endregion
private void updateposition(Item item)
{
if (item.IsOpen)
{
IntPtr hwnd = ((HwndSource)PresentationSource.FromVisual(item.popup.Child)).Handle;
SetWindowPos(hwnd, 0, (int)item.WorkPoint.X, (int)item.WorkPoint.Y, (int)item.popup.Width, (int)item.popup.Height, SWP_NOSIZE | SWP_NOACTIVATE);
}
}
<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
IsOpen="{Binding IsOpen}">
<jc:PrecisePopup.Placements>
<jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
</jc:PrecisePopup.Placements>
<!-- Popup content here... -->
</jc:PrecisePopup>
您需要将弹出窗口及其目标放置在Canvas面板中,以避免在弹出控件中由于屏幕边缘问题而重新定位。
<Canvas>
<Rectangle Name="rectPopUp" Width="30" Height="30"/>
<Button Width="20" Height="20" Name="btnAppLauncher" Click="btnAppLauncher_Click" Margin="-5,-2,0,0">
<Button.Content>
<Path Fill="White" Stretch="Uniform" Data="M16,20H20V16H16M16,14H20V10H16M10,8H14V4H10M16,8H20V4H16M10,14H14V10H10M4,14H8V10H4M4,20H8V16H4M10,20H14V16H10M4,8H8V4H4V8Z"></Path>
</Button.Content>
</Button>
<Popup Name="pnlAppLauncherPopup" PlacementRectangle="0,25,0,0" PlacementTarget="{Binding ElementName=btnAppLauncher}" Placement="Bottom" AllowsTransparency="True" PopupAnimation="Slide">
<UniformGrid Name="pnlAppLauncher">
</UniformGrid>
</Popup>
</Canvas>
由于整个单元位于画布面板内,且放置矩形已设置,因此弹出窗口将显示在矩形下方。