Можно ли как-нибудь остановить всплывающее окно WPF, когда оно выходит за пределы экрана?

Можно ли как-нибудь остановить всплывающее окно WPF, когда оно выходит за пределы экрана?

Я нашел этот старый вопрос, но он не получил правильного ответа на него. Есть какой-либо способ сделать это? Я готов подкласс его, если это необходимо. Благодарю.

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 должны были позволить это.

Это должно быть расширено, чтобы охватить все четыре края экрана и возможность использования нескольких экранов, но это демонстрирует, что подход является работоспособным.

Я не думаю, что есть способ сделать это. По крайней мере, чистый способ. Если вы загляните в класс Popup с помощью Reflector, вы найдете UpdatePosition метод, который настроит местоположение всплывающего окна так, чтобы оно оставалось между границами экрана. Этот метод вызывается из нескольких мест, из On***Changed обратные вызовы (OnPlacementChanged, OnVerticalOffsetChanged, так далее).

Если вы действительно хотите получить эту функциональность, у вас может быть небольшой шанс расширить класс Popup, переопределить некоторые метаданные в свойствах, зарегистрированных в On***Changed обратные вызовы, так что UpdatePosition никогда не вызывается, однако большинство людей посчитают это чистым злом, я тоже не рекомендую.

В среде wpf нет способа убрать всплывающее окно за пределы экрана, но вы можете принудительно установить позицию всплывающего окна, вызвав "SetWindowPos" через 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);

            }


        }

Вы можете использовать элемент управления 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>

Вы должны поместить всплывающее окно и его цель внутри панели 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>

Поскольку весь блок находится внутри панели холста и прямоугольник размещения установлен, всплывающее окно будет отображаться под прямоугольником.

Другие вопросы по тегам