The interface control is composed of a Grid and several controls, using an Expander to allow panel content to be expanded.
<Grid>
<Grid MinWidth="100" Height="50"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Background="Transparent" Margin="0,0,0,0"
PreviewMouseDown="Button_MouseDown"
PreviewMouseMove="Button_MouseMove"
PreviewMouseUp="Button_MouseUp"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="播放" Grid.Column="0" HorizontalAlignment="Left" />
<Expander VerticalAlignment="Center" Grid.Column="1" HorizontalAlignment="Left" ExpandDirection="Right" IsExpanded="True">
<TextBlock TextWrapping="Wrap" FontSize="18">
扩展面板
</TextBlock>
</Expander>
</Grid>
</Grid>
Three events have been bound to enable dragging:
PreviewMouseDown="Button_MouseDown"
PreviewMouseMove="Button_MouseMove"
PreviewMouseUp="Button_MouseUp"
The background color of the container must be set. If no color is needed, it can be set to transparent, but must not be left unset. Otherwise, clicking on a blank area within the container will not trigger the events.
Background="Transparent"
Additionally, the container needs to use the specified relative position of the top left corner; otherwise, it will be difficult to determine if the container is outside the window:
VerticalAlignment="Top"
HorizontalAlignment="Left"
Then, three events are used to implement the dragging functionality:
// Whether the mouse is down
bool _isMouseDown = false;
// The position when the mouse is down
Point _mouseDownPosition;
// The Margin of the control when the mouse is down
Thickness _mouseDownMargin;
// Mouse down event
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
var c = sender as Panel;
_mouseDownPosition = e.GetPosition(this);
var hitTestResult = VisualTreeHelper.HitTest(this, _mouseDownPosition);
if (hitTestResult != null && hitTestResult.VisualHit != c)
{
return;
}
_isMouseDown = true;
_mouseDownMargin = c.Margin;
c.CaptureMouse();
}
private void Button_MouseMove(object sender, MouseEventArgs e)
{
var window = this;
if (_isMouseDown)
{
var panel = sender as Panel;
// Current mouse position
var pos = e.GetPosition(this);
// Movement distance
var dp = pos - _mouseDownPosition;
double left;
double top;
const double right = 0;
const double bottom = 0;
// Left cannot be less than 0
left = _mouseDownMargin.Left + dp.X;
if (left < 0)
{
left = 0;
}
var windowWidth = window.ActualWidth - SystemParameters.WindowNonClientFrameThickness.Left - SystemParameters.WindowNonClientFrameThickness.Right;
if (left + panel.ActualWidth > windowWidth)
{
left = windowWidth - panel.ActualWidth - SystemParameters.WindowNonClientFrameThickness.Left - SystemParameters.WindowNonClientFrameThickness.Right;
}
// Top cannot be less than 0
top = _mouseDownMargin.Top + dp.Y;
if (top < 0)
{
top = 0;
}
// Height of the window excluding the title bar and bottom border
var windowHeight = window.ActualHeight - SystemParameters.WindowNonClientFrameThickness.Top - SystemParameters.WindowNonClientFrameThickness.Bottom;
// Height should also account for the height occupied by the title bar
if (top + panel.ActualHeight > windowHeight)
{
top = windowHeight - panel.ActualHeight - SystemParameters.WindowNonClientFrameThickness.Bottom * 2;
}
panel.Margin = new Thickness(left, top, right, bottom);
}
}
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
var c = sender as Panel;
_isMouseDown = false;
c.ReleaseMouseCapture();
}
e.GetPosition(this)
indicates that we are obtaining the position of the current user click.
e.GetPosition(this)
In addition, to identify elements within the container, it is necessary to check whether there are other child elements at the position clicked by the user. This step is crucial; otherwise, when a user clicks on buttons or other elements within the small panel, it will not work as the panel's drag event will take precedence.
At this time, it is necessary to check if there are child elements at the clicked position; if so, do not use the dragging event, but use the events of the child elements.
var hitTestResult = VisualTreeHelper.HitTest(this, _mouseDownPosition);
if (hitTestResult != null && hitTestResult.VisualHit != c)
{
return;
}
If this check is not set, it will result in user clicks on elements in the container being ineffective, and dragging will occur directly, while clicking buttons on child elements will not trigger their events.
The code in Button_MouseMove is extensive and restricts the dragging such that elements cannot be dragged out of the window.
When using a custom title bar or a third-party UI framework, if the border size is 0, you need to manually determine whether to add the height of the title bar and whether to calculate the border values. Here is an example:
var window = this;
if (_isPlayMouseDown)
{
var panel = sender as Panel;
// Current mouse position
var pos = e.GetPosition(this);
// Movement distance
var dp = pos - _playMouseDownPosition;
double left;
double top;
const double right = 0;
const double bottom = 0;
// Left cannot be less than 0
left = _playMouseDownMargin.Left + dp.X;
if (left < 0)
{
left = 0;
}
var windowWidth = window.ActualWidth;
if (left + panel.ActualWidth > windowWidth)
{
left = windowWidth - panel.ActualWidth;
}
// Top cannot be less than 0
top = _playMouseDownMargin.Top + dp.Y;
if (top < 0)
{
top = 0;
}
// Avoid covering the title bar; if it's a custom title bar, the height should be replaced with SystemParameters.WindowNonClientFrameThickness.Top
if (top < SystemParameters.WindowNonClientFrameThickness.Top)
{
top = SystemParameters.WindowNonClientFrameThickness.Top;
}
else
{
// Window excluding the height of the title bar and bottom border
var windowHeight = window.ActualHeight;
// Height should also account for the height occupied by the title bar
if (top + panel.ActualHeight > windowHeight)
{
top = windowHeight - panel.ActualHeight;
}
}
panel.Margin = new Thickness(left, top, right, bottom);
}
文章评论