Есть ли способ остановить всплывающее окно 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
должны были позволить это.
Это должно быть расширено, чтобы иметь дело со всеми четырьмя краями экрана и возможностью нескольких экранов, но это демонстрирует, что подход работоспособен.
Я не думаю, что есть способ сделать это. По крайней мере, чисто. Если вы заглядываете в всплывающий класс с отражателем, вы найдете UpdatePosition
метод, который отрегулирует местоположение всплывающего окна, чтобы оно оставалось между границами экрана. Этот метод вызывается из нескольких мест, из On***Changed
обратные вызовы (OnPlacementChanged
, OnVerticalOffsetChanged
и т. д.).
если вы действительно хотите получить эту функциональность, у вас может быть небольшая возможность расширить класс Popup, переопределить некоторые метаданные на свойства, зарегистрированные в On***Changed
обратные вызовы, так что UpdatePosition
никогда не вызывается, однако большинство людей считают, что это чистое зло, я тоже не рекомендую.
в WPF framework нет способа вывести всплывающее окно из экрана, но вы можете заставить всплывающее положение, вызвав "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>
<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 и установлен прямоугольник размещения, всплывающее окно будет отображаться ниже прямоугольника.