有什么方法可以阻止 WPF 弹出窗口在离开屏幕时重新定位自身吗?

发布于 2024-10-10 19:37:57 字数 335 浏览 10 评论 0原文

有没有办法阻止 WPF 弹出窗口< /a> 当它离开屏幕时不会重新定位自己?

我发现这个​​老问题,但没有得到适当的解决回答它。有什么办法可以做到这一点吗?如果有必要,我愿意将其子类化。谢谢。

Is there any way to stop a WPF Popup from repositioning itself when it goes off-screen?

I found this old question, but it didn't get a proper answer to it. Is there any way to do this? I'm willing to subclass it if necessary. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

悍妇囚夫 2024-10-17 19:37:57

正如 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>

和像这样的代码隐藏:

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 允许的话,屏幕上的部分会被剪切到显示的内容。

这必须扩展以处理屏幕的所有四个边缘以及多个屏幕的可能性,但它表明该方法是可行的。

As Andrei points out, this behavior is deep inside the Popup control and hard to overcome. If you are willing to do some work it can be done by resizing and translating the content of the popup when it reaches the screen edges. For the purposes of the demonstration, we'll focus on the left edge of the screen.

If we have some XAML like this:

<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>

and code-behind like this:

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;
}

then by calculating that the Popup would be off-screen, we can reduce the width of the content and give it a negative margin so that the portion that is on-screen is clipped to what would have appeared if the Popup were to allow this.

This would have to be extended to deal with all four edges of the screen and the possibility of multiple screens, but it demonstrates that the approach is workable.

情感失落者 2024-10-17 19:37:57

我认为没有办法做到这一点。至少是一种干净的方式。如果您使用 Reflector 查看 Popup 类,您会发现一个 UpdatePosition 方法,该方法将调整弹出窗口的位置,使其保持在屏幕边界之间。此方法是从多个位置调用的,例如 On***Changed 回调(OnPlacementChangedOnVerticalOffsetChanged 等)。

如果您真的很想获得此功能,您可能有机会扩展 Popup 类,覆盖使用 On***Changed 回调注册的属性上的一些元数据,以便UpdatePosition 永远不会被调用,但是大多数人会认为这样做是纯粹的邪恶,我也不推荐它。

I don't think there is a way to do this. At least a clean way. If you peek to the Popup class with the Reflector, you'll find an UpdatePosition method that will adjust the location of the popup so that it stays between screen bounds. This method is called from several places, from On***Changed callbacks (OnPlacementChanged, OnVerticalOffsetChanged, etc).

If you're really really want to get this functionality, you might have a small chance to extend the Popup class, override some of the metadata on the properties that registered with the On***Changed callbacks so that UpdatePosition never gets called, however most people will consider doing this to be pure evil, I don't recommend it either.

开始看清了 2024-10-17 19:37:57

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

            }


        }

there is no way on wpf framework to put Popup out of screen , but you can force popup position by calling "SetWindowPos" via p/invoke:

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

            }


        }
回忆躺在深渊里 2024-10-17 19:37:57

您可以使用 PrecisePopup 控件来自我的开源 JungleControls 库。它将按照指定的方式准确定位弹出窗口,即使这意味着弹出窗口部分位于屏幕外。

它可以自动从您定义的展示位置中选择最合适的展示位置,但如果您只需要一个与内置弹出窗口的“底部”位置相对应的精确弹出位置,您可以这样做:

<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
                 IsOpen="{Binding IsOpen}">
    <jc:PrecisePopup.Placements>
        <jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
    </jc:PrecisePopup.Placements>
    <!-- Popup content here... -->
</jc:PrecisePopup>

You can use PrecisePopup control from my opensource JungleControls library. It will position the popup exactly as specified even if that means the popup is partially off-screen.

It can automatically select the most suitable placement from among the placements you define, but if you just need one exact popup placement that corresponds to "Bottom" placement of the built-in Popup, you can do it like this:

<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
                 IsOpen="{Binding IsOpen}">
    <jc:PrecisePopup.Placements>
        <jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
    </jc:PrecisePopup.Placements>
    <!-- Popup content here... -->
</jc:PrecisePopup>
梦幻的心爱 2024-10-17 19:37:57

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

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

You have to place the popup and its target inside the Canvas panel so that it wont reposition due to the screen edge problem in popup control.

          <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>

Since the whole unit is inside canvas panel and placement rectangle is set the popup will show below the rectangle.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文