【问题标题】:Is there any way to stop a WPF Popup from repositioning itself when it goes off-screen?有什么方法可以阻止 WPF 弹出窗口在离开屏幕时重新定位?
【发布时间】:2011-01-08 04:24:19
【问题描述】:

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

我找到了这个old question,但没有得到正确的答案。有没有办法做到这一点?如有必要,我愿意对其进行子类化。谢谢。

【问题讨论】:

    标签: c# wpf popup multiple-monitors


    【解决方案1】:

    正如 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 会在屏幕外,我们可以减小内容的宽度并给它一个负边距,以便屏幕上的部分被剪裁到如果@ 987654325@ 允许这样做。

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

          您可以使用我的开源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>
          

          【讨论】:

            【解决方案5】:

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

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

            【讨论】:

            • 这个解决方案对我不起作用。当将我的弹出窗口移动到屏幕边缘附近时,它会相应地重新定位自己,而不管 Canvas 容器如何。我尝试了对等放置目标以及使用默认父级。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-05
            • 2011-10-31
            • 1970-01-01
            • 2022-01-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多