需求:系统有多个页面,第一个页面上有若干个自定义控件,当点击控件时在适当位置弹出弹窗显示其详细参数。弹窗具有以下功能:
①默认点击某个控件则显示某个控件的弹窗,当点击下一个控件时当前控件的弹窗自动关闭。
②有个固定弹窗功能,用户可以选择将其固定住即使点击下一个控件,下一个控件弹窗显示时,上一个控件的弹窗也不会关闭。
③有个可以自由拖动的按钮可以将弹窗放在任意位置。
④有个关闭按钮,关闭弹窗(考虑到当弹窗固定时不会自动关闭则可以手动关闭)。
⑤当切换至别的页面再切换至第一页时,它还保留了上次固定并处于打开状态的弹窗(位置和状态)
一 。使用Popup控件作弹窗
Popup或许是窗体的一种。
Popup控件和需要弹窗功能的控件在同一个xaml中设计 。
AllowsTransparency = true 可以将Popup自带的黑色背景去掉
IsOpen 控制其打开和关闭配和一个是否固定的标志变量(IsFixed)可以实现固定和打开和关闭功能
StaysOpen = true 避免失去焦点时关闭
当控件获得焦点和失去焦点时对IsOpen进行控制
this.GotFocus += ComponentBase_GotFocus;
this.LostFocus += ComponentBase_LostFocus;
private void ComponentBase_LostFocus(object sender, RoutedEventArgs e)
{
if (DetailPopup!=null)
{
if (DetailPopup.IsFixed)
{
IsExpandDetail = true;
}
else
{
IsExpandDetail = false;
}
}
else
{
IsExpandDetail = false;
}
e.Handled = true;
}
private void ComponentBase_GotFocus(object sender, RoutedEventArgs e)
{
IsExpandDetail = true;
e.Handled = true;
}
移动代码:
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(POINT Point);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(out POINT lpPoint);
[DllImportAttribute("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
private void MoveButton_MouseDown(object sender, MouseButtonEventArgs e)
{
POINT curPos;
IntPtr hWndPopup;
GetCursorPos(out curPos);
hWndPopup = WindowFromPoint(curPos);
ReleaseCapture();
SendMessage(hWndPopup, WM_NCLBUTTONDOWN, new IntPtr(HT_CAPTION), IntPtr.Zero);
e.Handled = true;
}
出现的问题:如果页面是叠加并通过控制Visibility来实现轮流显示的话会导致未关闭的Popup显示在下个页面。
如果页面是通过反射加载的话会导致从本页切换到其他页面再切回来的时候原本固定且没有关闭的Popup会关闭,不会显示。
二。使用Window作弹窗
window本身有个DragMove()方法可以用来实现拖动。
但window不能作为子级,即window不能和需要弹窗的控件在同一xaml中设计。需要通过控件获得或失去焦点时进行创建并弹出。如果用ShowDialog()则不能点击其他控件,只能等此窗体关闭。而用Show()可以点击其他的控件,但切换页面时并再次切回来时弹窗会置底(弹窗会是一个独立于主窗体的窗体)。
三。使用UserControl作弹窗
可以和需要弹窗的控件在同xaml中设计,且控件需要用到Canvas。
通过Visibility控制其打开和关闭。
移动:
1,通过方法一的代码并不能移动UserControl而是移动窗体。
2,通过鼠标的按下,移动,松开事件来实现移动,但并不灵敏,且它的父级必须是Canvas(注意:必须手动标注Canvas.Top=“0”,Canvas.Left=“0”,不然它不会有效果)
bool _isMoving = false;
Point _downPoint = new Point(0, 0);
double left = 0, top = 0;
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isMoving = true;
_downPoint = e.GetPosition(Parent);
var ob = Parent.GetValue(PopupEx.MarginProperty);
left = double.Parse(Parent.GetValue(Canvas.LeftProperty).ToString());
top = double.Parse(Parent.GetValue(Canvas.TopProperty).ToString());
Parent.CaptureMouse();
e.Handled = true;
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isMoving = false;
Parent.ReleaseMouseCapture();
e.Handled = true;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (_isMoving)
{
Point currentPoint = e.GetPosition(Parent);
Parent.SetValue(Canvas.LeftProperty, left + (currentPoint.X - _downPoint.X));
Parent.SetValue(Canvas.TopProperty, top + (currentPoint.Y - _downPoint.Y));
e.Handled = true;
}
}
3.通过Thumb的DragDelta来实现移动
在界面上放一个Thumb并实现其DragDelta事件
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
double nTop = Canvas.GetTop(Parent) + e.VerticalChange;
double nLeft = Canvas.GetLeft(Parent) + e.HorizontalChange;
//防止Thumb控件被拖出容器。
//....
Canvas.SetTop(Parent, nTop);
Canvas.SetLeft(Parent, nLeft);
e.Handled = true;
}
2022-7-16 16:44:14 记录一下怕忘了
更新:使用UserControl作自定义弹窗但不能和需要弹窗的控件放在同一xaml中设计。因为有多个控件时,前面先布局的控件的弹窗会在后布局控件的下面(即挡住了先布局的控件的弹窗)
因为在容器中控件先设计的控件在底部,后设计的控件在顶部,弹窗和控件是一体的故会挡住。
解决办法:控件和控件所需的弹窗分开设计 ,布局时将所有控件放在Canvas1中,所有弹窗放在另一Canvas2中并让弹窗通过依赖属性绑定至控件。