有没有办法阻止 WPF 弹出窗口在超出屏幕时重新定位?

13

有没有办法停止WPF Popup控件在离开屏幕时重新定位?

我发现这个旧问题,但它并没有得到一个适当的回答。有没有办法做到这一点?如果必要的话,我愿意对其进行子类化。谢谢。

5个回答

6
如Andrei所指出的那样,这种行为深深地嵌入在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>

并且 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 允许的话应该出现的位置。

这个方法需要扩展以应对屏幕的四个边缘和多屏幕的可能性,但它表明了这种方法是可行的。


2

我认为没有一种干净的方式来实现这个。如果你使用反射查看Popup类,你会发现一个UpdatePosition方法,它将调整弹出窗口的位置,使其保持在屏幕边界之间。这个方法从几个地方调用,从On***Changed回调函数(OnPlacementChangedOnVerticalOffsetChanged等)。

如果你真的非常想要这个功能,你可能有一点机会扩展Popup类,覆盖一些注册了On***Changed回调的属性元数据,以便UpdatePosition永远不会被调用,但大多数人都认为这样做是纯粹的邪恶,我也不建议这样做。


2

在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);

            }


        }

0
你可以使用我开源的JungleControls库中的PrecisePopup控件。即使这意味着弹出窗口部分超出屏幕,它也会将弹出窗口定位到指定位置。
它可以自动从你定义的放置方式中选择最合适的放置方式,但如果你只需要一个与内置弹出窗口的“底部”放置相对应的确切弹出窗口放置方式,你可以这样做:
<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
                 IsOpen="{Binding IsOpen}">
    <jc:PrecisePopup.Placements>
        <jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
    </jc:PrecisePopup.Placements>
    <!-- Popup content here... -->
</jc:PrecisePopup>

-1

您需要将弹出窗口及其目标放置在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>

由于整个单元位于画布面板内,且放置矩形已设置,因此弹出窗口将显示在矩形下方。


这个解决方案对我不起作用。当我将弹出窗口移动到屏幕边缘附近时,它会相应地重新定位,而不考虑画布容器。我尝试了同级放置目标以及使用默认父级。 - Brannon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接