How can the initiator determine that the drag has ended? - c#

Let's assume the following Situation:
a Control (e.g. a Button) has an attached behavior to enable a Drag&Drop-Operation
<Button Content="test">
<i:Interaction.Behaviors>
<SimpleDragBehavior/>
</i:Interaction.Behaviors>
</Button>
And the SimpleDragBehavior
public class SimpleDragBehavior: Behavior<Button>
{
protected override void OnAttached ()
{
AssociatedObject.MouseLeftButtonDown += OnAssociatedObjectMouseLeftButtonDown;
AssociatedObject.MouseLeftButtonUp += OnAssociatedObjectMouseLeftButtonUp;
AssociatedObject.MouseMove += OnAssociatedObjectMouseMove;
mouseIsDown = false;
}
private bool mouseIsDown;
private void OnAssociatedObjectMouseMove (object sender, MouseEventArgs e)
{
if (mouseIsDown)
{
AssociatedObject.Background = new SolidColorBrush(Colors.Red);
DragDrop.DoDragDrop((DependencyObject)sender,
AssociatedObject.Content,
DragDropEffects.Link);
}
}
private void OnAssociatedObjectMouseLeftButtonUp (object sender, MouseButtonEventArgs e)
{
mouseIsDown = false;
}
private void OnAssociatedObjectMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
{
mouseIsDown = true;
}
}
The task now is to determine when the drag ends, to restore the orignal backgound of the button.
This is no problem when droped on an drop-target. But how do i recognize a drop on something which isn't a drop-target? In the worst case: outside the window?

DragDrop.DoDragDrop returns after drag-and-drop operation is completed.
Yes, "Initiates a drag-and-drop operation" is confusing, since it could be read as "start drag-and-drop and return":
private void OnAssociatedObjectMouseMove (object sender, MouseEventArgs e)
{
if (mouseIsDown)
{
AssociatedObject.Background = new SolidColorBrush(Colors.Red);
var effects = DragDrop.DoDragDrop((DependencyObject)sender,
AssociatedObject.Content,
DragDropEffects.Link);
// this line will be executed, when drag/drop will complete:
AssociatedObject.Background = //restore color here;
if (effects == DragDropEffects.None)
{
// nothing was dragged
}
else
{
// inspect operation result here
}
}
}

Related

Richtextbox drag drop different than form drag drop

so I have a richtextbox and a form, and have attached a drag enter and drop event to them. The code for both of them are EXACTLY the same, I've made sure of it, but for some reason, the richtextbox event activates the Text Changed event. The form would activate the text changed event aswell, but I have a variable in the method that I call from the drag event that blocks it. I've tried to reproduce it on a different form, but the main kicker is that the code for the forms drag and drop events are EXACTLY the same as the richtextbox drag drop events. How could they possibly be different? Here is the drop drop code:
private void TestBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
private async void TestBox_DragDrop(object sender, DragEventArgs e)
{
string filePath = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
await AsyncStuff();
}
private async Task AsyncStuff()
{
block = true;
await OtherAsyncMethodThatActivatesTextChanged();
block = false;
}
private bool block;
private void TestBox_TextChanged(object sender, EventArgs e)
{
if (block) return;
MessageBox.Show("Text Changed called"); //Activates on the richtextbox drag, doesn't on the form
}
I am able to reproduce it now, here is the code. I attached the form drag drop and enter events through the designer.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
richTextBox1.AllowDrop = true;
richTextBox1.TextChanged += TestBox_TextChanged;
richTextBox1.DragDrop += TestBox_DragDrop;
richTextBox1.DragEnter += TestBox_DragEnter;
AllowDrop = true;
}
private void TestBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
private async void TestBox_DragDrop(object sender, DragEventArgs e)
{
string filePath = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
await AsyncStuff();
}
private async Task AsyncStuff()
{
block = true;
richTextBox1.AppendText("asd");
block = false;
}
private bool block;
private void TestBox_TextChanged(object sender, EventArgs e)
{
if (block) return;
MessageBox.Show("Text Changed called"); //Activates on the richtextbox drag, doesn't on the form
}
private async void Form1_DragDrop(object sender, DragEventArgs e)
{
string filePath = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
await AsyncStuff();
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
}

How to handle horizontal swipe events in Windows Phone 8.1?

I am trying to implement a horizontal swipe event handler in the following app. However, the gr_CrossSliding cross sliding event handler never fires.
What do I need to do to fire gr_CrossSliding?
public sealed partial class MainPage : Page
{
private GestureRecognizer gr;
public MainPage()
{
this.InitializeComponent();
gr = new GestureRecognizer();
gr.GestureSettings = GestureSettings.CrossSlide;
gr.CrossSlideHorizontally = true;
gr.CrossSliding += gr_CrossSliding;
}
void gr_CrossSliding(GestureRecognizer sender, CrossSlidingEventArgs args)
{
// handle swipe event
}
}
You need to set the GestureRecognizer on the handlers of the UI Element that is getting the gestures.
In this case, I'm using a Grid (GrdFoto).
public MainPage()
{
this.InitializeComponent();
gestureRecognizer.GestureSettings = Windows.UI.Input.GestureSettings.Drag;
gestureRecognizer.Dragging += gestureRecognizer_Dragging;
GrdFoto.PointerPressed += GrdFoto_PointerPressed;
GrdFoto.PointerMoved += GrdFoto_PointerMoved;
GrdFoto.PointerReleased += GrdFoto_PointerReleased;
GrdFoto.PointerCanceled += GrdFoto_PointerCanceled;
}
void GrdFoto_PointerPressed(object sender, PointerRoutedEventArgs e)
{
this.gestureRecognizer.ProcessDownEvent(e.GetCurrentPoint(this.GrdFoto));
this.GrdFoto.CapturePointer(e.Pointer);
e.Handled = true;
}
void GrdFoto_PointerMoved(object sender, PointerRoutedEventArgs e)
{
this.gestureRecognizer.ProcessMoveEvents(e.GetIntermediatePoints(this.GrdFoto));
}
void GrdFoto_PointerReleased(object sender, PointerRoutedEventArgs e)
{
this.gestureRecognizer.ProcessUpEvent(e.GetCurrentPoint(this.GrdFoto));
e.Handled = true;
}
void GrdFoto_PointerCanceled(object sender, PointerRoutedEventArgs e)
{
this.gestureRecognizer.CompleteGesture();
e.Handled = true;
}
void gestureRecognizer_Dragging(GestureRecognizer sender, DraggingEventArgs args)
{
// Drag completed.
}

Raising an event from child control to parent control

I have a class (which extends Framework Element) which contains within it a number of other Elements.
// Click event coverage area
private Rectangle connectorRectangle;
These shapes all have their event handlers, and when the user clicks on them its working well. Now what I want is to be able to 'handle' a right-click on my class from outside the scope of the class.
So I figured the best way to do it is to handle the event internally, and somehow bubble it to the top
private void connectorRectangle_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
MouseButtonEventArgs args = new MouseButtonEventArgs();
//???
e.Handled = true;
}
The problem is that I have no idea how to raise the event. this.OnMouseRightButtonUp doesn't exist, and all the tutorials I'm finding are for raising custom events.
I'm pretty new to silverlight, so bear with me if I missed something obvious.
Try it :
public Rectangle
{
this.Click += new System.EventHandler(Function);
}
private void Function(object sender, System.EventArgs e)
{
if (((MouseEventArgs)e).Button == MouseButtons.Right)
{
//Your code
}
}
Your "exteded Framework Element class" shouldn't handel the mouse event (or if they handel them, set e.Handled to false). Then the event should bubble up automatically (without reraise the event).
EDIT
public class ExtendedFrameworkElement : Grid
{
public ExtendedFrameworkElement()
{
Border b1 = new Border();
b1.Padding = new Thickness(20);
b1.Background = Brushes.Red;
b1.MouseRightButtonUp += b1_MouseRightButtonUp;
Border b2 = new Border();
b2.Padding = new Thickness(20);
b2.Background = Brushes.Green;
b2.MouseRightButtonUp += b2_MouseRightButtonUp;
b1.Child = b2;
this.Children.Add(b1);
}
private void b1_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//DoSomeThing
e.Handled = false;
}
private void b2_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//DoSomeThing
e.Handled = false;
}
}
Xaml:
<Window x:Class="WpfApplicationTest.MainWindow">
<wpfApplicationTest:ExtendedFrameworkElement MouseRightButtonUp="UIElement_OnMouseRightButtonUp"/>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void UIElement_OnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
//DoSomeThing
}
}

How to select ListViewItem with left mouse button only?

How can i prevent the right click from selecting an item in my listview both in click and double click?
You could use this code, I think it should do the work. You need to set some bool variable to indicate that the right mouse has been clicked in your MouseDown, then Clear selected items, if SelectedIndexChanged event handler fired because of the right click and then reset the indicator on MouseUp event. Check the code:
bool rightClicked = false;
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
rightClicked = true;
}
else
{
rightClicked = false;
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (rightClicked)
{
listView1.SelectedItems.Clear();
}
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
rightClicked = false;
}
EDIT: This is the best I could do, it preserves the selection but flickers. the solution could be implemented using some custom drawing of the items but that requires too much time. I leave that to you.
bool rightClicked = false;
int [] lviListIndex = null;
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
rightClicked = true;
lviListIndex = new int[listView1.SelectedItems.Count];
listView1.SelectedIndices.CopyTo(lviListIndex, 0);
}
else
{
rightClicked = false;
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (rightClicked)
{
listView1.SelectedIndices.Clear();
}
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
if (rightClicked)
{
listView1.SelectedIndexChanged -= new System.EventHandler(listView1_SelectedIndexChanged);
if (lviListIndex != null)
{
foreach (int index in lviListIndex)
{
listView1.SelectedIndices.Add(index);
}
}
lviListIndex = null;
listView1.SelectedIndexChanged += new System.EventHandler(listView1_SelectedIndexChanged);
}
rightClicked = false;
}
Subclass the listview and add this override:
protected override void WndProc(ref Message m)
{
const int WM_RBUTTONUP = 0x0205;
const int WM_RBUTTONDOWN = 0x0204;
if ((m.Msg != WM_RBUTTONDOWN) && (m.Msg != WM_RBUTTONUP))
{
base.WndProc(ref m);
}
}
This works by ignoring messages from the right button of the mouse.
you can check like this if (e.Button == MouseButtons.Left)

Is there a way to catch when ContainsFocus changes?

I need to be able to determine when ContainsFocus changes on a Control (specifically a windows form). Overriding OnGotFocus is not the answer. When I bring the form to the foreground, ContainsFocus is true and Focused is false. So is there an OnGotFocus equivalent for ContainsFocus? Or any other way?
Note: GotFocus events of the child controls are fired if you have a child control. Otherwise OnGotFocus of the form is called.
If I understood the question correctly, then this should work:
bool lastNotificationWasGotFocus = false;
protected override void OnControlAdded(ControlEventArgs e)
{
SubscribeEvents(e.Control);
base.OnControlAdded(e);
}
protected override void OnControlRemoved(ControlEventArgs e)
{
UnsubscribeEvents(e.Control);
base.OnControlRemoved(e);
}
private void SubscribeEvents(Control control)
{
control.GotFocus += new EventHandler(control_GotFocus);
control.LostFocus += new EventHandler(control_LostFocus);
control.ControlAdded += new ControlEventHandler(control_ControlAdded);
control.ControlRemoved += new ControlEventHandler(control_ControlRemoved);
foreach (Control innerControl in control.Controls)
{
SubscribeEvents(innerControl);
}
}
private void UnsubscribeEvents(Control control)
{
control.GotFocus -= new EventHandler(control_GotFocus);
control.LostFocus -= new EventHandler(control_LostFocus);
control.ControlAdded -= new ControlEventHandler(control_ControlAdded);
control.ControlRemoved -= new ControlEventHandler(control_ControlRemoved);
foreach (Control innerControl in control.Controls)
{
UnsubscribeEvents(innerControl);
}
}
private void control_ControlAdded(object sender, ControlEventArgs e)
{
SubscribeEvents(e.Control);
}
private void control_ControlRemoved(object sender, ControlEventArgs e)
{
UnsubscribeEvents(e.Control);
}
protected override void OnGotFocus(EventArgs e)
{
CheckContainsFocus();
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
CheckLostFocus();
base.OnLostFocus(e);
}
private void control_GotFocus(object sender, EventArgs e)
{
CheckContainsFocus();
}
private void control_LostFocus(object sender, EventArgs e)
{
CheckLostFocus();
}
private void CheckContainsFocus()
{
if (lastNotificationWasGotFocus == false)
{
lastNotificationWasGotFocus = true;
OnContainsFocus();
}
}
private void CheckLostFocus()
{
if (ContainsFocus == false)
{
lastNotificationWasGotFocus = false;
OnLostFocus();
}
}
private void OnContainsFocus()
{
Console.WriteLine("I have the power of focus!");
}
private void OnLostFocus()
{
Console.WriteLine("I lost my power...");
}
One way to solve this is to use a Timer. It's definitely brute force, but it gets the job done:
private Timer m_checkContainsFocusTimer = new Timer();
private bool m_containsFocus = true;
m_checkContainsFocusTimer.Interval = 1000; // every second is good enough
m_checkContainsFocusTimer.Tick += new EventHandler(CheckContainsFocusTimer_Tick);
m_checkContainsFocusTimer.Start();
private void CheckContainsFocusTimer_Tick(object sender, EventArgs e)
{
if (!m_containsFocus && ContainsFocus)
OnAppGotFocus();
m_containsFocus = ContainsFocus;
}
But is there an easier way?
Handling the GotFocus and LostFocus events should do it.
Another thing to note... the SDK says this about the ContainsFocus property:
You can use this property to determine
whether a control or any of the
controls contained within it has the
input focus. To determine whether the
control has focus, regardless of
whether any of its child controls have
focus, use the Focused property.
EDIT:
When handling the GotFocus event, you may still have to check the Focused/ContainsFocus property depending on how the hierarchy of your controls is set up.
ContainsFocus will be true if the control or any of its children have focus.
Focus will only be true if the specific control itself has focus, regardless of its children.

Categories

Resources