有什么方法可以阻止WPF Popup在屏幕外重新定位吗?
我发现了这个old question,但它没有得到正确答案。有没有办法做到这一点?如果有必要,我愿意将其子类化。感谢。
答案 0 :(得分:5)
正如安德烈所指出的,这种行为深入Popup
控制范围,难以克服。如果您愿意做一些工作,可以通过调整弹出窗口内容到达屏幕边缘时进行调整和翻译。出于演示的目的,我们将重点关注屏幕的左边缘。
如果我们有这样的XAML:
<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>
和代码隐藏这样:
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
允许这样做。
这必须扩展到处理屏幕的所有四个边缘和多个屏幕的可能性,但它表明该方法是可行的。
答案 1 :(得分:1)
我认为没有办法做到这一点。至少一个干净的方式。如果您使用Reflector查看Popup类,您将找到一个UpdatePosition
方法,该方法将调整弹出窗口的位置,使其保持在屏幕边界之间。从On***Changed
回调(OnPlacementChanged
,OnVerticalOffsetChanged
等多个地方调用此方法。
如果你真的想要获得这个功能,你可能很少有机会扩展Popup类,覆盖使用On***Changed
回调注册的属性上的一些元数据,以便{{1永远不会被召唤,但大多数人会认为这是纯粹的邪恶,我也不推荐它。
答案 2 :(得分:1)
在wpf框架上没有办法将Popup放在屏幕外,但你可以通过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);
}
}
答案 3 :(得分:0)
您可以在我的开源PrecisePopup库中使用JungleControls控件。它将完全按指定的方式定位弹出窗口,即使这意味着弹出窗口部分在屏幕外。
它可以从您定义的展示位置中自动选择最合适的展示位置,但如果您只需要一个与&#34;底部&#34;相对应的精确弹出展示位置。放置内置的Popup,您可以这样做:
<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
IsOpen="{Binding IsOpen}">
<jc:PrecisePopup.Placements>
<jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
</jc:PrecisePopup.Placements>
<!-- Popup content here... -->
</jc:PrecisePopup>
答案 4 :(得分:-1)
您必须将弹出窗口及其目标放在“画布”面板中,以便由于弹出控件中的屏幕边缘问题而无法重新定位。
<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>
由于整个单位位于画布面板内,并且设置了放置矩形,弹出窗口将显示在矩形下方。