Introduction
Have you ever wanted to bind something to the mouse's x and y coordinates? Ever get frustrated by the times WPF consumes mouse events? Then the MouseTracker
class will probably be helpful!
Background
Tying directly into the Windows API allows us to bypass the .NET Framework. However, this also introduces a few issues you will have to consider before doing so. There is a note in the MouseTracker.cs file!
The three big down sides about WPF's mouse implementation are: that you cannot make global mouse handlers, you must attach mouse handlers to IInputElement
objects, and that there is no mouse DependencyObject that can be used for binding. That being said there's still a way to achieve almost any objective with the mouse implementation provided by Microsoft. Easily being able to make a popup follow the mouse seems to be one of the biggest problems people face with this, myself included. Although, getting this to work right is very helpful for aesthetics in drag 'n' drop scenarios.
Using the code
In the MouseTracker
class I did not implement all mouse events possible by the Windows API. You can always add more if you want, just look at the mouse values in WINUSER.H. You can Google where to get a copy. When you have it, look for the value WM_MOUSEMOVE
in the file. The values that immediately follow are the mouse events possible.
Here is a basic implementation of the MouseTracker
...
MT = new MouseTracker();
MT.MouseLeftButtonDown += MT_MouseLeftButtonDown;
MT.MouseLeftButtonUp += MT_MouseLeftButtonUp;
MT.MouseRightButtonDown += MT_MouseRightButtonDown;
MT.MouseRightButtonUp += MT_MouseRightButtonUp;
MT.MouseXChanged += MT_MouseXChanged;
MT.MouseYChanged += MT_MouseYChanged;
private void MT_MouseLeftButtonDown(object sender, EventArgs e)
{
}
private void MT_MouseLeftButtonUp(object sender, EventArgs e)
{
}
private void MT_MouseRightButtonDown(object sender, EventArgs e)
{
}
private void MT_MouseRightButtonUp(object sender, EventArgs e)
{
}
private void MT_MouseXChanged(object sender, EventArgs e)
{
}
private void MT_MouseYChanged(object sender, EventArgs e)
{
}
As you can see it is similar to what you are used to. Let's look at the part I like. Binding a popup's location to the mouse for a drag 'n' drop setup. Let's say you want to "rip" out a TreeViewItem
from a TreeView
and display it in a popup so it can be dropped into another TreeView
. Or maybe, have the TreeViewItem
s' content display in a Window on LeftMouseButtonUp
(...tabs to windows...hmmmm). Here is a slimmed down version of that, minus drag 'n' drop...and the other TreeView
. :)
<TreeView Name="treeView" MouseLeave="treeView_MouseLeave">
<TreeViewItem Header="One" />
<TreeViewItem Header="Two">
<TreeViewItem Header="Two-One" />
</TreeViewItem>
<TreeViewItem Header="Three" />
<TreeViewItem Header="Four" />
<TreeViewItem Header="Five">
<TreeViewItem Header="Five-One" />
</TreeViewItem>
</TreeView>
private MouseTracker MT { get; set; }
private Popup DragDisplay { get; set; }
public MainWindow()
{
InitializeComponent();
MT = new MouseTracker();
MT.MouseLeftButtonUp += MT_MouseLeftButtonUp;
}
private void MT_MouseLeftButtonUp(object sender, EventArgs e)
{
if (DragDisplay != null)
{
DragDisplay.IsOpen = false;
DragDisplay = null;
}
}
private void treeView_MouseLeave(object sender, MouseEventArgs e)
{
if (treeView.SelectedItem != null && e.LeftButton == MouseButtonState.Pressed)
{
DragDisplay = new Popup();
DragDisplay.AllowsTransparency = true;
DragDisplay.Placement = PlacementMode.Absolute;
DragDisplay.StaysOpen = true;
Border border = new Border()
{
Background = Brushes.LightGray,
BorderBrush = Brushes.Gray,
BorderThickness = new Thickness(1),
Child = new ContentPresenter()
{
Content = treeView.SelectedItem.ToString(),
Margin = new Thickness(5, 10, 5, 10)
}
};
DragDisplay.Child = border;
Binding leftBinding = new Binding("MouseX");
leftBinding.Mode = BindingMode.OneWay;
leftBinding.Source = MT;
Binding topBinding = new Binding("MouseY");
topBinding.Mode = BindingMode.OneWay;
topBinding.Source = MT;
DragDisplay.SetBinding(Popup.HorizontalOffsetProperty, leftBinding);
DragDisplay.SetBinding(Popup.VerticalOffsetProperty, topBinding);
DragDisplay.IsOpen = true;
}
}
Run your main window and "rip" out TreeViewItem
s and see their ToString
representation. Download the source and demo project for the MouseTracker
DependencyObject and a better example.