I have a dll which adds/removes Canvases, and deals with drawing on them.
I wish to "register" each Canvas I add to MouseMove, MouseDown and MouseUp events.
I know I can add those 3 event functions and do it the traditional way, but I wanted to know if there is a more elegant way of doing that in MVVM?
I tried to search for it, but found only implementation of it in the xaml using Interactivity.
There are a couple of ways of doing this but in my experience you'll save yourself a lot of work by creating a Blend behavior. Start by declaring an interface you can use to communicate with your view model, here's a very simple one:
public interface IMouseCaptureProxy
{
event EventHandler Capture;
event EventHandler Release;
void OnMouseDown(object sender, MouseCaptureArgs e);
void OnMouseMove(object sender, MouseCaptureArgs e);
void OnMouseUp(object sender, MouseCaptureArgs e);
}
public class MouseCaptureArgs
{
public double X {get; set;}
public double Y { get; set; }
public bool LeftButton { get; set; }
public bool RightButton { get; set; }
}
Then the behavior itself:
public class MouseCaptureBehavior : Behavior<FrameworkElement>
{
public static readonly DependencyProperty ProxyProperty = DependencyProperty.RegisterAttached(
"Proxy",
typeof(IMouseCaptureProxy),
typeof(MouseCaptureBehavior),
new PropertyMetadata(null, OnProxyChanged));
public static void SetProxy(DependencyObject source, IMouseCaptureProxy value)
{
source.SetValue(ProxyProperty, value);
}
public static IMouseCaptureProxy GetProxy(DependencyObject source)
{
return (IMouseCaptureProxy)source.GetValue(ProxyProperty);
}
private static void OnProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is IMouseCaptureProxy)
{
(e.OldValue as IMouseCaptureProxy).Capture -= OnCapture;
(e.OldValue as IMouseCaptureProxy).Release -= OnRelease;
}
if (e.NewValue is IMouseCaptureProxy)
{
(e.NewValue as IMouseCaptureProxy).Capture += OnCapture;
(e.NewValue as IMouseCaptureProxy).Release += OnRelease;
}
}
static void OnCapture(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.CaptureMouse();
}
static void OnRelease(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.ReleaseMouseCapture();
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreviewMouseDown += OnMouseDown;
this.AssociatedObject.PreviewMouseMove += OnMouseMove;
this.AssociatedObject.PreviewMouseUp += OnMouseUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.PreviewMouseDown -= OnMouseDown;
this.AssociatedObject.PreviewMouseMove -= OnMouseMove;
this.AssociatedObject.PreviewMouseUp -= OnMouseUp;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs {
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseDown(this, args);
}
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs {
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseMove(this, args);
}
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs
{
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseUp(this, args);
}
}
}
Then you just add it to whichever control you want mouse event notification on:
<Canvas>
<i:Interaction.Behaviors>
<behaviors:MouseCaptureBehavior Proxy="{Binding}" />
</i:Interaction.Behaviors>
In this case I'm binding Proxy to the DataContext, which happens to be a MainViewModel, so make sure MainViewModel inherits IMouseCaptureProxy. Then it's a simple matter of implementing the various handlers and the Capture/Release handlers which you can then raise to capture and release the mouse, should you need to:
public event EventHandler Capture;
public event EventHandler Release;
public void OnMouseDownobject sender, MouseCaptureArgs e)
{
this.Capture(sender, null);
// etc
}
Some would argue that passing sender into the handlers is technically a violation of MVVM, and personally I'd say I probably agree with this, but this should at least be enough to get you started. If you're an MVVM purist then you'll want to modify the behaviour to instead create intermediate objects that track that kind of thing internally.
Related
Is there any way to store the code below in another class called RecordAddControl?
Code from "RecordAdd.cs"
private void txtEilesNum_Enter(object sender, EventArgs e)
{
txtEilesNum.Clear();
txtEilesNum.ForeColor = SystemColors.Desktop;
}
private void txtEilesNum_Leave(object sender, EventArgs e)
{
if (txtEilesNum.Text == "")
{
txtEilesNum.ForeColor = SystemColors.InactiveCaption;
txtEilesNum.Text = "Eil Num";
}
}
Things I've tried like RecordAdd recordAdd = new RecordAdd(); doesn't seem to work when trying to get the class to recognise things like txtEilesNum.
RecordAdd is a form, and txtEilesNum is taken from "RecordAdd.Designer.cs"
it makes more sense to tailor helper class to work with TextBox directly (any TextBox, not only the one in RecordAdd)
public static class TexBoxDecorator
{
public static void UsePlaceholder(this TextBox tb)
{
tb.Enter += tb_Enter;
tb.Leave += tb_Leave;
}
private static void tb_Enter(object sender, EventArgs e)
{
var tb = (TextBox)sender;
tb.Clear();
tb.ForeColor = SystemColors.Desktop;
}
private static void tb_Leave(object sender, EventArgs e)
{
var tb = (TextBox)sender;
if (tb.Text == "")
{
tb.ForeColor = SystemColors.InactiveCaption;
tb.Text = "Eil Num";
}
}
}
txtEilesNum in a known member in RecordAdd, so it can be accessed to add event handlers:
txtEilesNum.UsePlaceholder();
Sure, you can store event handlers in another class, you'll need to make them public, and you'll need to cast the sender to the correct type:
public static class EventHandlers
{
public void EilesNum_Enter(object sender, EventArgs e)
{
var txtEilesNum = (TextBox)sender; // Assumed textbox
txtEilesNum.Clear();
txtEilesNum.ForeColor = SystemColors.Desktop;
}
public void EilesNum_Leave(object sender, EventArgs e)
{
var txtEilesNum = (TextBox)sender; // Also assumed textbox
if(txtEilesNum.Text == "")
{
txtEilesNum.ForeColor = SystemColors.InactiveCaption;
txtEilesNum.Text = "Eil Num";
}
}
}
In your form, you'll still need to wire up the events as before
txtEilesNum.Enter += EventHandlers.EilesNum_Enter;
txtEilesNum.Leave += EventHandlers.EilesNum_Leave;
I want to drag the listbox selected item. Dragging function is working fine. My requirement is dragging should not happen while drag started from any other location in listbox. I have did like this, but that is not working. Please anyone suggest me to achieve this,
private bool IsDragging { get; set; }
private Point _startPoint { get; set; }
protected override void OnAttached()
{
this.AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
this.AssociatedObject.PreviewMouseMove += AssociatedObject_PreviewMouseMove;
}
private void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) <= SystemParameters.MinimumHorizontalDragDistance &&
Math.Abs(position.Y - _startPoint.Y) <= SystemParameters.MinimumVerticalDragDistance)
{
StartDrag(sender);
}
}
}
private void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
private void StartDrag(object sender)
{
IsDragging = true;
if (sender is ListBox)
{
var listBox = (sender as ListBox);
if (listBox != null)
{
var selectedMember = listBox.SelectedItem;
if (selectedMember != null)
{
DragDrop.DoDragDrop(listBox, selectedMember, DragDropEffects.Copy);
}
}
}
IsDragging = false;
}
Please refer the screenshot
Do like this, By using System.Windows.Media.VisualTreeHelper.HitTest(this, point) it gives the current element under the mouse. I think it will help you.
private bool IsDrag { get; set; }
protected override void OnAttached()
{
this.AssociatedObject.Drop += AssociatedObject_Drop;
this.AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
this.AssociatedObject.PreviewMouseMove += AssociatedObject_PreviewMouseMove;
}
private void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if(IsDrag)
{
StartDrag(sender);
}
}
}
private void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IsDrag = false;
if(sender is ListBox)
{
Point initialPoint = e.GetPosition((UIElement)sender);
var histPoint = VisualTreeHelper.HitTest(sender as ListBox, initialPoint);
if (histPoint.VisualHit != null)
{
if (histPoint.VisualHit is TextBlock || histPoint.VisualHit is Border)
{
IsDrag = true;
}
}
}
}
private void StartDrag(object sender)
{
if (sender is ListBox)
{
var listBox = (sender as ListBox);
if (listBox != null)
{
var selectedMember = listBox.SelectedItem;
if (selectedMember != null)
{
DragDrop.DoDragDrop(listBox, selectedMember, DragDropEffects.Copy);
}
}
}
}
I want to send Keys, from a WPF, to another Window (e.g. editor)
I found this Methode
public static class SendKeys
{
public static void Send(Key key)
{
if (Keyboard.PrimaryDevice != null)
{
if (Keyboard.PrimaryDevice.ActiveSource != null)
{
var e1 = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Down) { RoutedEvent = Keyboard.KeyDownEvent };
InputManager.Current.ProcessInput(e1);
}
}
}
}
, but it doesn't work :/
so I call it from my main program
SendKeys.Send(Key.D7);
In this case is WindowsForms not an alternative :/
Use the key parameter passed instead of Key.Down.
This worked fine:
public partial class MainWindow : Window
{
public MainWindow()
{
this.Loaded += OnLoaded;
this.KeyDown += OnKeyDown;
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
SendKeys.Send(Key.A);
SendKeys.Send(Key.B);
SendKeys.Send(Key.C);
SendKeys.Send(Key.D);
SendKeys.Send(Key.LeftCtrl);
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
MessageBox.Show("Key pressed: " + e.Key);
}
}
public static class SendKeys
{
public static void Send(Key key)
{
if (Keyboard.PrimaryDevice != null)
{
if (Keyboard.PrimaryDevice.ActiveSource != null)
{
var e1 = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key) { RoutedEvent = Keyboard.PreviewKeyDownEvent };
InputManager.Current.ProcessInput(e1);
}
}
}
}
I keep seeing posts about adding Events to be able to drag around the panel. But how would i achieve this through a dynamically created Panel?
Panel pn = wch.GenerateWorkspaceControl(space.Name, space.Name, p);
PanelTest.Controls.Add(pn);
public Panel GenerateWorkspaceControl(string gbTitle, string gbName, Point gbPos)
{
Panel pnl = GeneratePanelContainer(gbPos, new Size(300, 200));
pnl.Controls.Add(GenerateLabel(gbName,new Point(100,1),new Size(135,115)));
return pnl;
}
private Panel GeneratePanelContainer(Point loc, Size size)
{
return new Panel() { BackColor = Color.Transparent, BorderStyle = BorderStyle.FixedSingle, Location = loc, Size = size };
}
Do i add an event handler in the generate panel container?
to summarize i have a panel that is holding multiple panels that are dynamically created. I want to be able to move around the dynmaically created panels INSIDE the main panel.
Any ideas?
Here is a class that lets you make any control movable.
Simply register it:
MoveController.RegisterCtl( button1 );
Now you can move the control..
When done you can also unregister a control:
MoveController.UnRegisterCtl( button1 );
Here is the controller class:
static class MoveController
{
static List<Control> Controls = new List<Control>();
static Control curCtl = null;
static Point curStart = Point.Empty;
static public void RegisterCtl(Control ctl)
{
Controls.Add(ctl);
ctl.MouseDown += ctl_MouseDown;
ctl.MouseMove += ctl_MouseMove;
ctl.MouseUp += ctl_MouseUp;
}
static public void UnRegisterCtl(Control ctl)
{
if (Controls != null && Controls.Contains(ctl) )
{
Controls.Remove(ctl);
ctl.MouseDown -= ctl_MouseDown;
ctl.MouseMove -= ctl_MouseMove;
ctl.MouseUp -= ctl_MouseUp;
}
}
static void ctl_MouseDown(object sender, MouseEventArgs e)
{
curCtl = (Control)sender;
curStart = curCtl.Location;
}
static void ctl_MouseMove(object sender, MouseEventArgs e)
{
if (curCtl != null)
{
curCtl.Left += e.Location.X - curCtl.Width / 2;
curCtl.Top += e.Location.Y - curCtl.Height / 2;
}
}
static void ctl_MouseUp(object sender, MouseEventArgs e)
{
curCtl = null;
}
}
Update
Here is a more involved version that allows
to set a Tag value to restrict movement to vertical or horizontal
adding Actions for Moving and Moved events..
class MoveController
{
static List<Control> Controls = new List<Control>();
static Control curCtl = null;
static Point curStart = Point.Empty;
static Dictionary<Control, Tuple<Action, Action>>
actions = new Dictionary<Control, Tuple<Action, Action>>();
static public void RegisterCtl(Control ctl)
{
RegisterCtl(ctl, null, null);
}
static public void RegisterCtl(Control ctl, Action moveAction, Action movedAction)
{
Controls.Add(ctl);
ctl.MouseEnter += Ctl_MouseEnter;
ctl.MouseLeave += Ctl_MouseLeave;
ctl.MouseDown += ctl_MouseDown;
ctl.MouseMove += ctl_MouseMove;
ctl.MouseUp += ctl_MouseUp;
if (moveAction != null)
if (actions.Keys.Contains(ctl)) actions[ctl] = new Tuple<Action, Action>(moveAction, movedAction);
else actions.Add(ctl, new Tuple<Action, Action>(moveAction, movedAction));
}
private static void Ctl_MouseEnter(object sender, EventArgs e)
{
((Control)sender).Cursor = Cursors.Hand;
}
private static void Ctl_MouseLeave(object sender, EventArgs e)
{
((Control)sender).Cursor = Cursors.Default;
}
public static void UnRegisterCtl(Control ctl)
{
if (Controls != null && Controls.Contains(ctl) )
{
Controls.Remove(ctl);
ctl.MouseDown -= ctl_MouseDown;
ctl.MouseMove -= ctl_MouseMove;
ctl.MouseUp -= ctl_MouseUp;
}
if (actions.ContainsKey(ctl)) actions.Remove(ctl);
}
static public void RegisterMovingAction(Control ctl, Action action)
{
}
static void ctl_MouseDown(object sender, MouseEventArgs e)
{
curCtl = (Control)sender;
curStart = curCtl.Location;
}
static void ctl_MouseMove(object sender, MouseEventArgs e)
{
int t = 0;
if (curCtl != null)
{
if (curCtl.Tag != null) t = Convert.ToInt32(curCtl.Tag);
if ((t&1) != 1) curCtl.Left += e.Location.X - curCtl.Width / 2;
if ((t&2) != 2) curCtl.Top += e.Location.Y - curCtl.Height / 2;
if (actions.ContainsKey(curCtl) && actions[curCtl] != null && actions[curCtl].Item1 != null)
actions[curCtl].Item1();
}
}
static void ctl_MouseUp(object sender, MouseEventArgs e)
{
if (curCtl == null) return; ///
if (actions.ContainsKey(curCtl) && actions[curCtl] != null && actions[curCtl].Item2 != null)
actions[curCtl].Item2();
curCtl = null;
}
}
I need to get the sender of the mouseDown event from within the event and set it as a global variable to use in a dragDrop event, so that it calls a method depending on what picturebox was dragged. I need the control name or something. My attempt:
Global variable "dragSource":
public partial class MapDesignerView : Form
{
public Map myMap { get; set; }
public MapController myMapController { get; set; }
public MapConstructor myMapConstructor { get; set; }
public MouseEventHandler myDetectMouse { get; set; }
object dragSource = null;
mouseDown
private void pbxMinotaur_MouseDown(object sender, MouseEventArgs e)
{
pbxMap.AllowDrop = true;
pbxMinotaur.DoDragDrop(pbxMinotaur.Name, DragDropEffects.Copy |
DragDropEffects.Move);
dragSource = sender;
}
DragDrop
private void pbxMap_DragDrop(object sender, DragEventArgs e)
{
{
if (dragSource == pbxMinotaur)
{
myDetectMouse.setMinotaur(e, myMap.myCells);
}
So what exactly isn't working... the only thing I can think that might be causing the problem is that you're storing the reference to the entire control in your drag source.
A better idea might be to just story the Id. and then test it based on the Id further down.
string dragSourceName = null;
private void pbxMinotaur_MouseDown(object sender, MouseEventArgs e)
{
pbxMap.AllowDrop = true;
pbxMinotaur.DoDragDrop(pbxMinotaur.Name, DragDropEffects.Copy |
DragDropEffects.Move);
Control c = (sender as Control);
if(c != null)
dragSourceName = c.Name;
}
private void pbxMap_DragDrop(object sender, DragEventArgs e)
{
if (dragSourceName == pbxMinotaur.Name)
{
myDetectMouse.setMinotaur(e, myMap.myCells);
}