C#: Drag and Drop in WPF (Richtextbox) - wpf

i want to implement a Drag/Drop mechanic in WPF, but it didn't work...
With Windows-Forms it worked, ...
First i set AllowDrop to True. In windows-forms you can already drag items into the richtextbox and the cursor changes.
With WPF .... nothing happens.
The nexT point: Implement DragEnter and DragDrop Methodes.
I did it like the online-manuals says. (ok i had to try out something more, because DragDrop doesn't exists in WPF)
I think all tutorials for drag/drop is only for Windowsforms, nothing for WPF...
Is there a problem with the richtextbox? If i change it to "allowDrop" - nothing happens. The cursor is still a not-allowed-symbol.
Hope someone can help :)
Examplecode from tutorials i read:
richTextBox1.AllowDrop = true;
void richTextBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.None;
if (e.Data.GetDataPresent(DataFormats.XXX))
{
e.Effect = DragDropEffects.Copy;
}
}
void richTextBox1_DragDrop(object sender, DragEventArgs e)
{
//intert in richtextbox ...
richTextBox1.methodeXY();
}

I was curious as to why so I had a little play and eventually got it to work. Essentially, I started by binding to the PreviewXXX events like so:
AllowDrop="True" PreviewDragEnter="RichTextBox_DragEnter" PreviewDragOver="RichTextBox_DragEnter" PreviewDrop="RichTextBox_Drop"
Even still, they STILL didn't work. But the answer came here, in the form of elevated permissions. I usually run VS2010 with admin elevation. Because I was dropping some files from Explorer onto my RichTextBox, it was essentially banning the operation because Explorer runs in a non-elevated user mode. Running the VS2010 in a non-elevated mode fixed the problem. The article linked does suggest some workarounds, but I haven't tried them yet.

Related

WPF Routed Command only fires sometimes

I have some RoutedCommands for commands like control-A, copy paste and they all work fine.
Then I added 4 more routedcommands to move object up down left and right in the canvas using arrowkeys, they sometimes works and sometime doesn't. At first I thought it was a Focus issue on the Canvas but I just found out that at the same time, all the other routedcommands like control-A works but arrowkeys doesn't.
I really have no idea what's going on here, they are identical routedcommands with different variable names, how come one works 100% of time and one only work 50% of time?
Working RoutedCommand:
_bindings.Add(new CommandBinding(DesignerCanvas.SelectAll, SelectAll_Executed));
SelectAll.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));
private void SelectAll_Executed(object sender, ExecutedRoutedEventArgs e)
{
SelectionService.SelectAll();
}
Malfunctioning RoutedCommand:
_bindings.Add(new CommandBinding(DesignerCanvas.MoveDown, MoveDown_Executed));
MoveDown.InputGestures.Add(new KeyGesture(Key.Down));
private void MoveDown_Executed(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
select item;
if (selectedItems.Count() > 0)
{
for (int i = 0; i < selectedItems.Count(); i++)
selectedItems.ElementAt(i).Top += Option.OptionSingleton.Sensitivity;
}
}
The malfunctioning RoutedCommand is just not firing sometimes, especially after I open some other window and come back to the canvas, then it will stop firing while other routedcommands are unaffected. Any ideas what's causing this weird behavior?
You can sometiems use very inclusive class event handlers to trace the route of an event:
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.CanExecuteEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("CanExecute: " + s)), true);
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.ExecutedEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("Executed:" + s)), true);
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.ExecutedEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("KeyDown:" + s)), true);
In your case the KeyDown may be handled before it reaches the command binding or the CanExecute event may not reach it for some other reason.
Hopefully this will help you debug the problem
This may be due to the fact that the key you are using is the "Down" key. I suspect that if you used a different key, it would work.
Some controls consume the arrow keys and pageup/pagedown keys. For example, TextBox does this. If your Canvas is in a scrollviewer, the scrollviewer might be eating it.
There are two workarounds for this:
Add a binding to the control that is eating the key gesture.
Handle KeyPreview for the Canvas (or any parent of the control that is eating the keystroke) and execute the command from there.
The answer to this question shows how you can do #2 without writing specific code in the KeyPreview handler for each command.
It turns out that it was a focus issue, I just set the focus to the canvas whenever mouse enters, now it's sort of fixed. Thanks everybody for answering.

WPF 4 multi-touch drag and drop

I have a WPF 4 application where I have implemented Drag and Drop using the standard DragDrop.DoDragDrop approach, but Im doing it using touch instead of Mouse events.
My XAML for my Grid (that Im dragging) is as follows:
<Grid x:Name="LayoutRoot"
ManipulationStarting="ManipulationStarting"
ManipulationDelta="ManipulationDelta"
ManipulationCompleted="ManipulationCompleted"
IsManipulationEnabled="True">
<!-- children in here -->
</Grid>
Now the code behind is like this:
private void ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
e.ManipulationContainer = this;
e.Handled = true;
}
private void ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
e.Handled = true;
DragDrop.DoDragDrop(this, new DataObject(GetType(), this), DragDropEffects.Move);
}
private void ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
//completed stuff
}
BUT when I try to drag with one finger while already dragging with another finger (different hands for example, which would simulate two people) the second touches don't seem to register properly, infact, it seems that windows thinks that my two fingers are trying to scale (like a pinch gesture)...
Does anyone know a way to get around this?
Thanks a lot
Mark
For multi-touch, you're going to want to use the Surface Toolkit for Windows Touch. It includes a drag-and-drop framework suitable for multi-touch scenarios. It includes a drag-and-drop framework. That link includes several how-to scenarios.
Although this is just an educated guess, I would say that DragDrop.DoDragDrop() is not capable to handle multiple drag guestures in parallel.
Indices:
There's no possibility to pass a touch ID to the method (which would be neccessary for differentiating the ongoing drag gestures)
The implementation of DoDragDrop() is static
The call of DoDragDrop() is blocking until the drop occured or was canceled
It is based upon the OLE version of drag and drop (which was not updated for Win7)
However, I would be glad if someone could correct me in this matter, because I'm currently also searching an DND API suitable for touch support.
Regards,
Seven

WPF ComboBox DropDown part appears in the wrong place

I put several ComboBoxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.
I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).
It seems to be a bug.
Workaround:
Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.
This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).
Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.
Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.
I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.
In any event, I hope someone finds this of use.
I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.
I had the same problem on Visual Studio 2019.
Using window.Show() can help but it can ruin your design.
The solution is to open the window asynchronously.
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
You can also create a more elegant solution by making the extension method:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
And you can use it like this:
await dlgReview.ShowDialogAsync();
It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
I already found a workabout.

How to update the position of a drag adorner during WPF drag-n-drop?

I'm using an adorner to show a 'ghost' of the element being dragged...
var adornerLayer = AdornerLayer.GetAdornerLayer(topLevelGrid);
dragAdorner = new DragAdorner(topLevelGrid, itemToDrag);
adornerLayer.Add(dragAdorner);
dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
adornerLayer.Remove(dragAdorner);
itemToDrag = null;
...but I can't find a nice way to update the position of the adorner during the drag. The closest I've got is by setting AllowDrop="true" on the top level grid and giving it a DragOver handler...
private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
{
dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
}
But this means I don't get the proper DragDropEffects feedback on the cursor, i.e., it always shows the DragDropEffects.Move cursor instead of DragDropEffects.None until I'm over an actual drop target.
Does anyone know a better way to update the adorner position?
There's this (unfortunately only available as a cached version) pretty old blog post from Bea Stollnitz, which pretty much covers your question. It has a nice implementation of drag n drop with an adorner showing a "ghost image".
Basically drag and drop in WPF is quite the complicate procedure that - if you want some custom DragAdorners - involves adding a bunch of attached dependency properties for handling setup of all the events involved and especially for displaying the adorner in a way that doesn't interfere with the dropping code.
Bea's code works by having a helper class that sets the owning Window's DragOver event handler and AllowDrop right before the actual drag drop operation, that way you can control all the moving in between the actual drag source and the drop target.
So, looking closer at Bea's code that redoced was referring to...
I still set AllowDrop="true" on the top level grid and give it a DragOver handler where I can update the adorner position, but I also set the DragDropEffects to None here. Then I just need to add a DragOver handler to the actual drop target to also update the adorner position...and making sure to set e.Handled = true so that the top level grid's handler doesn't just set the effects back to None when I'm over a drop target...
private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
{
UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
e.Effects = DragDropEffects.None;
e.Handled = true;
}
private void DropTarget_OnDragOver(object sender, DragEventArgs e)
{
UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
e.Handled = true;
}
I know this is an old question, but I ended up asking the same thing recently and then had to answer it myself. I used hooks via p/invoke to get at the native window messages before they were consumed by the drag and drop operation. This let me track the mouse even during drag and drop and without having to set AllowDrop where I didn't want it.
For the full answer (including most of the code I used) you can check out my question:
WPF - Track mouse during Drag & Drop while AllowDrop = False

Is it possible to avoid Focus on SplitContainer?

The WinForm SplitContainer gets the focus when it's dragged or clicked, while the Splitter does not.
The side-effect of this, is that dragging a SplitContainer bar fires Leave/Validate on other controls, and I need to avoid this.
I already tried setting TabStop and CausesValidation to False, but with no success.
Is there a way to stop the SplitContainer from getting focused? (not a big deal, I can still use the old Splitter, but I lose some nice VS properties...)
Remove the SplitContainer control and replace it manually with Panel and Splitter controls. A little more effort, but a much cleaner outcome.
Try with this code:
//This code will move the focus from the splitContainer to TreeView shortly after moved.
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) {
if(this.splitContainer1.CanFocus) {
this.splitContainer1.ActiveControl = this.treeView1;
}
}
Filini,
The only time that the splitcontainer would have focus is when you are actually moving the splitter. So I would so something like this in your validating and leave events.
private void Button_Leave(object sender, EventArgs e)
{
if(SplitContainer.ContainsFocus)
return;
}
I reproduced your issue and when I added the above it still calls the event of course, but the code execution doesn't occur because the SplitContainer has focus while you are moving the splitter.
Hope that helps.

Resources