是否有任何方法可以阻止WPF Popup在屏幕外重新定位?

时间:2011-01-08 04:24:19

标签: c# wpf popup multiple-monitors

有什么方法可以阻止WPF Popup在屏幕外重新定位吗?

我发现了这个old question,但它没有得到正确答案。有没有办法做到这一点?如果有必要,我愿意将其子类化。感谢。

5 个答案:

答案 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回调(OnPlacementChangedOnVerticalOffsetChanged等多个地方调用此方法。

如果你真的想要获得这个功能,你可能很少有机会扩展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>

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