题
WPF Popup 控件很好,但在我看来有些局限性。有没有办法在打开时“拖动”弹出窗口(就像使用 Windows 的 DragMove() 方法一样)?
这可以在没有大问题的情况下完成吗?还是我必须自己编写弹出类的替代品?谢谢
解决方案
这是使用 Thumb 的简单解决方案。
- XAML 和代码隐藏中的 Popup 子类
- 添加宽度/高度设置为 0 的 Thumb(这也可以在 XAML 中完成)
- 侦听 Popup 上的 MouseDown 事件并在 Thumb 上引发相同的事件
- 在 DragDelta 上移动弹出窗口
XAML:
<Popup x:Class="PopupTest.DraggablePopup" ...>
<Canvas x:Name="ContentCanvas">
</Canvas>
</Popup>
C#:
public partial class DraggablePopup : Popup
{
public DraggablePopup()
{
var thumb = new Thumb
{
Width = 0,
Height = 0,
};
ContentCanvas.Children.Add(thumb);
MouseDown += (sender, e) =>
{
thumb.RaiseEvent(e);
};
thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
}
其他提示
PopUp没有DragMove。只是一个小小的工作,你可以添加很多改进。
<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint" Width="200">
<Rectangle Stretch="Fill" Fill="Red"/>
</Popup>
在后面的代码中,添加此mousemove事件
pop.MouseMove += new MouseEventHandler(pop_MouseMove);
void pop_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
e.GetPosition(this).Y),new Point(200,200));
}
}
实现此目的的另一种方法是将弹出窗口的位置设置为 MousePoint。这使得弹出窗口最初出现在鼠标光标的位置。
然后您可以使用 Thumb 或 MouseMove 事件来设置 Popup 的 HorizontalOffset 和 VerticalOffset。当用户拖动 Popup 时,这些属性会将 Popup 移离其原始位置。
请记住将 HorizontalOffset 和 VerticalOffset 重置为零,以便下次使用弹出窗口!
移动太快时松动鼠标的问题可以解决
这取自msdn:
新窗口包含Popup的Child内容。
Popup控件将其Child内容的引用维护为逻辑子项。创建新窗口时,Popup的内容将成为窗口的可视子项,并仍然是Popup的逻辑子项。相反,Popup仍然是其子内容的逻辑父母。
换句话说,弹出窗口的子窗口显示在独立窗口中。
所以当尝试以下内容时:
Popup.CaptureMouse()
正在捕获包装器窗口,而不是弹出窗口本身。而是使用 Popup.Child.CaptureMouse()
捕获实际的弹出窗口。
所有其他事件都应使用 Popup.Child
注册。
喜欢 Popup.Child.MouseMove
, Popup.Child.LostCapture
等等
这已经过测试,效果非常好
Private Point startPoint;
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(null);
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point relative = e.GetPosition(null);
Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
this.Top = AbsolutePos.Y - startPoint.Y;
this.Left = AbsolutePos.X - startPoint.X;
}
}
这适用于拖动我的窗口,但就像它被告知如果我将鼠标移动到快速,它将离开窗口并停止提升事件。没有提到拖拽根本不顺利。有没有人知道如何正确地做,漂亮和平滑的拖动,而不是在拖得太快时失去它???如果可能的话,发布一个简单的例子,除了整个教程,让像我这样的初学者在代码中丢失。谢谢!
建立 Jobi Joy 的答案后,我找到了一个可重复使用的解决方案,可让您添加为在现有控件/页面的xaml内控制。由于它具有不同的范围,因此无法使用名称添加为Xaml。
[ContentProperty("Child")]
[DefaultEvent("Opened")]
[DefaultProperty("Child")]
[Localizability(LocalizationCategory.None)]
public class DraggablePopup : Popup
{
public DraggablePopup()
{
MouseDown += (sender, e) =>
{
Thumb.RaiseEvent(e);
};
Thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
/// <summary>
/// The original child added via Xaml
/// </summary>
public UIElement TrueChild { get; private set; }
public Thumb Thumb { get; private set; } = new Thumb
{
Width = 0,
Height = 0,
};
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
TrueChild = Child;
var surrogateChild = new StackPanel();
RemoveLogicalChild(TrueChild);
surrogateChild.Children.Add(Thumb);
surrogateChild.Children.Add(TrueChild);
AddLogicalChild(surrogateChild);
Child = surrogateChild;
}
}