Wpf Thumb Drag not firing drag enter on other component - c#

I have a wpf application with some Thumb controls which move items around a canvs. I want to detect when these thumbs are dragged over another element. However, the drag enter on the element which is dragged over is not fired. I know this code works as drag items that are not thumbs fire the event.
Is the drag event on the thumb not a drag event that other components listen to?
Any idea how to get this to work?

Apparently no. The only Thumb related Events are DragStarted, DragCompleted and DragDelta. The other events are for Drag and drop, like your DragEnter Event. These Events are especially for the Drag and drop, like dragging a file from the explorer into your application which has nothing to do with the Thumb. The names are similar but in fact very different.
One thing you could try is to use HitTesting while dragging, but remember that drag and drop and dragging a thumb takes the mouse capture, which disables input events on the other classes.

To achieve the simulation of a drag enter event when a thumb object is moved over another component I've had to do this:
Register event handler for the thumb drag delta
EventManager.RegisterClassHandler(typeof(Thumb), Thumb.DragDeltaEvent, new RoutedEventHandler(Thumb_DragDeltaEvent), true);
Then in the event handler see if the dragged element is over the element that is listening to the moving component
void Thumb_DragDeltaEvent(object sender, RoutedEventArgs e)
{
UIElement src = e.Source as UIElement ;
if (src != null)
{
Point srcPositionTopLeft = new Point(Canvas.GetLeft(src), Canvas.GetTop(src));
Point srcPositionBottomRight = new Point(srcPositionTopLeft.X + src.ActualWidth, srcPositionTopLeft.Y + ActualHeight);
Rect srcRect = new Rect(srcPositionTopLeft, srcPositionBottomRight);
Rect transformedSrcRect = src.TransformToAncestor(this.Parent).TransformBounds(srcRect);
Point trgPositionTopLeft = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
Point trgPositionBottomRight = new Point(trgPositionTopLeft.X + this.ActualWidth, trgPositionTopLeft.Y + this.ActualHeight);
Rect trgRect = new Rect(srcPositionTopLeft, srcPositionBottomRight);
Rect transformedTrgRect = this.TransformToAncestor(this.Parent).TransformBounds(trgRect);
if (transformedSrcRect.Contains(transformedTrgRect) ||
transformedSrcRect.IntersectsWith(transformedTrgRect))
{
//drag is over element
}
}
}
Remember to removed event handlers etc later.
Hope this helps someone in the future.

Related

ContextMenuStrip location is not good because of PointToScreen

I have a Button in a SplitContainer, on panel2.
I have created dynamically a ContextMenuStrip which I have attached to this Button. I want to positioning the context menu under the button like in this image
But what I got is not like that.
This is what I tried:
private void SelectContentGroup_Click(object sender, EventArgs e) {
ContextMenuStrip x = selectContentGroup.ContextMenuStrip;
if (x is null) return;
// this will show contextmenu near the mouse arrow
//x.Show(Control.MousePosition);
// I have tried to get MousePosition and to compare to my button location and Y is a lot of difference, about 200 pixels
//Console.WriteLine("MousePosition: {0}, ButtonLocation: {1}", Control.MousePosition, PointToScreen(selectContentGroup.Location));
x.Show(PointToScreen(selectContentGroup.Location));
// I tried with e.Location also, but none of those points will give the button Left-Bottom position for contextmenu
}
Use the method that includes the control, and adjust by the height of that control to have the menu show below it:
x.Show(SelectContentGroup, new Point(0, SelectContentGroup.Height));
I'm assuming SelectContentGroup is the name of the button.

Drag and drop a popup text box in WPF

I have created a little program in WPF where I click on a button and a popup text box arrive. I would like to make this movable - drag and drop.
In the code I have created an object for a textbox named x, and used the command x.AllowDrop = true;, but without success.
I have tried MSN, Youtube and other sources, but without success.
private void button1_Click_1(object sender, RoutedEventArgs e) {
TextBox x = new TextBox();
x.Name = "new_textboxqq";
x.TextWrapping = TextWrapping.Wrap;
x.Text = "asfsadfasfsadfasff";
x.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
x.Background = Brushes.Yellow;
x.AcceptsReturn = true
x.Margin = new Thickness(5, 10, 0, 0);
x.AllowDrop = true;
HouseCanvas.Children.Add(x);
this.AllowDrop = true;
Canvas.SetLeft(x, 20);
Canvas.SetTop(x, 20);
}
Drag and Drop is a data transfer technique. From one control or files to another control or window.
If you need to move your control inside window, you need using mouse events: MouseDown, MouseUp, MouseMove. Look this.
You want to drag a TextBox and move it around on a Canvas, but AllowDrop property is for Drag-and-Drop operation. Drag-and-Move and Drag-and-Drop are different operations.
This is an example to do what you want.
The idea is handling the MouseMove event of the Canvas, calculating the position of the mouse cursor, and by setting the position of the TextBox to that position, you can make the TextBox move following the mouse cursor.

How to use ctrl key + mouse click to select multiple controls?

Probably this question has already an answer here but I was not able to find it..
I have a tabControl with a flowlayoutpanel in each tab page where I can add controls at run time. I can rearrange them, move them across tab pages.. How can I select multiple controls to be able to move them around using ctrl key + mouse click?
This is my drag event so far:
private void control_DragDrop(object sender, DragEventArgs e)
{
Control target = new Control();
target.Parent = sender as Control;
if (target != null)
{
int targetIndex = FindCSTIndex(target.Parent);
if (targetIndex != -1)
{
string cst_ctrl = typeof(CustomControl).FullName;
if (e.Data.GetDataPresent(cst_ctrl))
{
Button source = new Button();
source.Parent = e.Data.GetData(cst_ctrl) as CustomControl;
if (targetIndex != -1)
fl_panel = (FlowLayoutPanel)tabControl1.SelectedTab.Controls[0];
if (source.Parent.Parent.Name == target.Parent.Parent.Parent.Name)
{
this.fl_panel.Controls.SetChildIndex(source.Parent, targetIndex);
}
else
{
target.Parent.Parent.Parent.Controls.Add(source.Parent);
this.fl_panel.Controls.SetChildIndex(source.Parent, targetIndex);
}
}
}
}
}
private int FindCSTIndex(Control cst_ctr)
{
fl_panel = (FlowLayoutPanel)tabControl1.SelectedTab.Controls[0];
for (int i = 0; i < this.fl_panel.Controls.Count; i++)
{
CustomControl target = this.fl_panel.Controls[i] as CustomControl;
if (cst_ctr.Parent == target)
return i;
}
return -1;
}
This is not an easy, nor a common task. But surely doable and depending on preconditions could become trivial without need to spend multi-man-year effort on it ^^.
You have many options:
controls support selection;
container control support children controls selection;
overlay.
Handling selection is pretty easy: have a dictionary (or a control property, possibly using Tag) to store if control is selected or not, show selection somehow, when control is Ctrl-clicked invert selection. You can even provide Shift-key selection.
As #Hans Passant commented, you can use overlay window (invisible window on top of everything) to draw selection reticle there as well as handle selection and dragging itself. Or it could be a custom control with property IsSelected, setting which will draw something (border?) to indicate selection.
Easiest option would be to create SelectionPanel control, which can host any other controls inside, has IsSelected indication and is draggable. When children is added subscribe to MouseUp/MouseDown events or you can only allow to drag if special area of SelectionPanel is clicked. To example, you could have option Enable dragging in your software, when set all SelectionPanels will display special area (header?) which you can drag or Ctrl-click.

how to make the cursor lines to follow the mouse in charts using C#

The picture below shows a chart in my project. As you can see there are two dotted crossing lines. I’m asked to make it to follow the mouse, but now only if I click on the chart it moves. I tried to use CursorPositionChanging but it didn’t work.
CursorEventHandler also is not shown in the command below:
this.chart1.CursorPositionChanging += new System.Windows.Forms.DataVisualization.Charting.Chart.CursorEventHandler(this.chart1_CursorPositionChanging);
do we need to add extra lib for that?
So I have two problems now:
1. Make the lines to follow the mouse
2. Missing CursorEventHandler
the project is window form application with C#
private void chData_MouseMove(object sender, MouseEventArgs e)
{
Point mousePoint = new Point(e.X, e.Y);
Chart.ChartAreas[0].CursorX.SetCursorPixelPosition(mousePoint, true);
Chart.ChartAreas[0].CursorY.SetCursorPixelPosition(mousePoint, true);
// ...
}
The chart supports a 'MouseMove' event which is fired each time the mouse is moved inside the chart. The MouseEventArgs contain the position of the mouse so u can move the dotted lines based on that data each time the event fires.
A more generalized form to synchronized all areas without any additional logic is as follows:
var mousePoint = new Point(e.X, e.Y);
var chart = (Chart)sender;
//foreach child
foreach (var ca in chart.ChartAreas)
{
ca.CursorX.SetCursorPixelPosition(mousePoint, true);
ca.CursorY.SetCursorPixelPosition(mousePoint, true);
}

Drag WPF Popup control

the WPF Popup control is nice, but somewhat limited in my opinion. is there a way to "drag" a popup around when it is opened (like with the DragMove() method of windows)?
can this be done without big problems or do i have to write a substitute for the popup class myself?
thanks
Here's a simple solution using a Thumb.
Subclass Popup in XAML and codebehind
Add a Thumb with width/height set to 0 (this could also be done in XAML)
Listen for MouseDown events on the Popup and raise the same event on the Thumb
Move popup on DragDelta
XAML:
<Popup x:Class="PopupTest.DraggablePopup" ...>
<Canvas x:Name="ContentCanvas">
</Canvas>
</Popup>
C#:
public partial class DraggablePopup : Popup
{
public DraggablePopup()
{
var thumb = new Thumb
{
Width = 0,
Height = 0,
};
ContentCanvas.Children.Add(thumb);
MouseDown += (sender, e) =>
{
thumb.RaiseEvent(e);
};
thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
}
There is no DragMove for PopUp. Just a small work around, there is lot of improvements you can add to this.
<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint" Width="200">
<Rectangle Stretch="Fill" Fill="Red"/>
</Popup>
In the code behind , add this mousemove event
pop.MouseMove += new MouseEventHandler(pop_MouseMove);
void pop_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
e.GetPosition(this).Y),new Point(200,200));
}
}
Building off of Jobi Joy's answer, I found a re-useable solution that allows you to add as a control within xaml of an existing control/page. Which was not possible adding as Xaml with a Name since it has a different scope.
[ContentProperty("Child")]
[DefaultEvent("Opened")]
[DefaultProperty("Child")]
[Localizability(LocalizationCategory.None)]
public class DraggablePopup : Popup
{
public DraggablePopup()
{
MouseDown += (sender, e) =>
{
Thumb.RaiseEvent(e);
};
Thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
/// <summary>
/// The original child added via Xaml
/// </summary>
public UIElement TrueChild { get; private set; }
public Thumb Thumb { get; private set; } = new Thumb
{
Width = 0,
Height = 0,
};
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
TrueChild = Child;
var surrogateChild = new StackPanel();
RemoveLogicalChild(TrueChild);
surrogateChild.Children.Add(Thumb);
surrogateChild.Children.Add(TrueChild);
AddLogicalChild(surrogateChild);
Child = surrogateChild;
}
}
Another way of achieving this is to set your Popup's placement to MousePoint. This makes the popup initially appear at the position of the mouse cursor.
Then you can either use a Thumb or MouseMove event to set the Popup's HorizontalOffset & VerticalOffset. These properties shift the Popup away from its original position as the user drags it.
Remember to reset HorizontalOffset and VerticalOffset back to zero for the next use of the popup!
The issue with loosing the mouse when moving too fast, could be resolved
This is taken from msdn:
The new window contains the Child content of Popup.
The Popup control maintains a reference to its Child content as a logical child. When the new window is created, the content of Popup becomes a visual child of the window and remains the logical child of Popup. Conversely, Popup remains the logical parent of its Child content.
In the other words, the child of the popup is displayed in standalone window.
So when trying to the following:
Popup.CaptureMouse() is capturing the wrapper window and not the popup itself. Instead using Popup.Child.CaptureMouse() captures the actual popup.
And all other events should be registered using Popup.Child.
Like Popup.Child.MouseMove, Popup.Child.LostCapture and so on
This has been tested and works perfectly fine
Contrary to what others have stated about this, I agree 100% with Jobi Joy's answer (which should honestly be the accepted answer). I saw a comment stating that the solution in the answer would cause memory fragmentation. This is not possible as creating new structs cannot cause memory fragmentation at all; in fact, using structs saves memory because they are stack-allocated. Furthermore, I think that this is actually the correct way to reposition a popup (after all, Microsoft added the PlacementRectangle property for a reason), so it is not a hack. Appending Thumbs and expecting a user to always place a Popup onto a canvas, however, is incredibly hacky and is not always a practical solution.
Private Point startPoint;
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(null);
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point relative = e.GetPosition(null);
Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
this.Top = AbsolutePos.Y - startPoint.Y;
this.Left = AbsolutePos.X - startPoint.X;
}
}
This works for dragging my window, but like it was told if i move the mouse to fast, it would get out of window and stop raising the event. Without mentioning the dragging is not smooth at all. Does anyone knows how to do it properly, nice and smooth dragging, without loosing it when dragged too fast??? Post a simple example if possible, other than a whole tutorial that would get beginners like me lost in code. Thanks!

Categories

Resources