Detect Drag'n'Drop file in WPF? - c#

Is it possible to have a WPF window/element detect the drag'n'dropping of a file from windows explorer in C# .Net 3.5? I've found solutions for WinForms, but none for WPF.

Try the following :
private void MessageTextBox_Drop(object sender, DragEventArgs e)
{
if (e.Data is DataObject && ((DataObject)e.Data).ContainsFileDropList())
{
foreach (string filePath in ((DataObject)e.Data).GetFileDropList())
{
// Processing here
}
}
}
private void MessageTextBox_PreviewDragEnter(object sender, DragEventArgs e)
{
var dropPossible = e.Data != null && ((DataObject)e.Data).ContainsFileDropList();
if (dropPossible)
{
e.Effects = DragDropEffects.Copy;
}
}
private void MessageTextBox_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}

Unfortunately, TextBox, RichTextBox, and FlowDocument viewers always mark drag-and-drop events as handled, which prevents them from bubbling up to your handlers. You can restore drag-and-drop events being intercepted by these controls by force-handling the drag-and-drop events (use UIElement.AddHandler and set handledEventsToo to true) and setting e.Handled to false in your handler.

Turns out I couldn't drop onto my TextBox for some reason, but dropping onto buttons works fine. Got it working by adding 'AllowDrop="True"' to my window and adding drop event handler to button consisting of:
private void btnFindType_Drop(object sender, DragEventArgs e)
{
if (e.Data is System.Windows.DataObject &&
((System.Windows.DataObject)e.Data).ContainsFileDropList())
{
foreach (string filePath in ((System.Windows.DataObject)e.Data).GetFileDropList())
{
// Processing here
}
}
}

I had similar Issue, The drop events and drag enter events were not fired. The issue was with the windows User Account Settings. Set it to least secure setting and try the same code it works.

Related

How to make an application DragDrop aware?

I'm trying to make an application where I want to drag and drop files from explorer directly to application and a DataGridView updates its entries with the information about the file dropped.
Doing that wasn't an issue but what exactly I wanted is to update the form's UI and display some sort of message like 'Drop files here to update' just when I am over the application holding the file (and not dropped yet).
I found Control.GiveFeedback can be used to achieve what I want but somehow even after subscribing to the event, Its not being called.
Here is how I'm handling DragDrop event:
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
FileInfo[] DroppedFiles = ((string[])e.Data.GetData(DataFormats.FileDrop)).Select(x => new FileInfo(x)).ToArray();
foreach (FileInfo File in DroppedFiles)
{
TrackInfo Track = new TrackInfo(File.FullName);
PlaylistSource.Add(Track);
}
}
Thank you in advanced.
DragOver and DragLeave were the events I had to subscribe in order to let the application aware about a potential DragDrop event.
I handled the event like this:
private void label1_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Link;
//Updating UI Controls when file dragged over
label1.BackColor = Color.Green;
label1.ForeColor = Color.White;
}
}
private void label1_DragLeave(object sender, EventArgs e)
{
//Updating UI Controls when file no longer being dragged over
label1.BackColor = SystemColors.Control;
label1.ForeColor = Color.Black;
}
Of course, you must not forget to set the control's AllowDrop property to actually let the control become "DragDrop Aware".

Prevent selecting text in a TextBox

Similar questions have been already asked (e.g., here), however I've not found an answer for my specific case. I'm building a custom control based on a DevExpress control, which in turns is based on standard TextBox and I've a flickering problem that seems due to the base TextBox component, which tries to update selection.
Without explaining all the details of my custom control, to reproduce the problem you just need to place a TextBox inside a Form and then use this code:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
textBox1.MouseMove += TextBox1_MouseMove;
}
private void TextBox1_MouseMove(object sender, MouseEventArgs e) {
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
}
}
If you launch it, you click on the TextBox and then you move the cursor toward right you will notice the flickering problem (see video here). For my custom control I would need to avoid this flickering. I'm bound to use a TextBox (so no RichTextBox). Any idea?
The solution has been provided in the meantime by Reza Aghaei by overriding WndProc and intercepting WM_SETFOCUS messages. See here
Depending on what u want to do there are several solutions:
If you want to prevent the selection it would be:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
(sender as TextBox).SelectionLength = 0;
}
Or for selecting all:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
(sender as TextBox).SelectAll();
}
And besides that u also could specify the conditions for selecting, for example:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
if (MouseButtons == MouseButtons.Left) (sender as TextBox).SelectAll();
else (sender as TextBox).SelectionLength = 0;
}
But as long as you want to select the text you always will get some flickering, because a normal Textbox has not the possibility to use things like BeginEdit and EndEdit and so it will change the text first and then select it.
Looking at the video it looks like your textbox is calling WM_ERASEBKGND unnecessarily. In order to remedy this problem you can subclass the textbox class and intercept these messages. Below is sample code which should do the trick (untested) Disclaimer: I have used this technique for other WinForm controls that had the type of flicker shown in your video but not TextBox. If it does work for you, please let me know. Good luck!
// textbox no flicker
public partial class TexttBoxNF : TextBox
{
public TexttBoxNF()
{
}
public TexttBoxNF(IContainer container)
{
container.Add(this);
InitializeComponent();
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
//http://stackoverflow.com/questions/442817/c-sharp-flickering-listview-on-update
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if (m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}

Cleared Clipboard is not null

I want to check if the Clipboard consists of a data and if not, let the "Paste" Button be enabled. But unfortunately, even after I clear the Clipboard it still doesn't show it's null. I am working with Windows Forms.
I manually clear the clipboard:
private void button2_Click(object sender, EventArgs e)
{
Clipboard.Clear();
}
and then I add the following code to the Form LoadEvent:
if (Clipboard.GetDataObject() != null)
{
this.pn1_BtnPaste.Enabled = true;
}
And it makes a button enabled which is weird to me. Can anybody explain why is that happening?
EDIT:
Because I got understood wrong, let me change the code to make it more clear:
private void button2_Click(object sender, EventArgs e)
{
Clipboard.Clear();
if (Clipboard.GetDataObject() != null)
{
this.pn1_BtnPaste.Enabled = true;
}
else
this.pn1_BtnPaste.Enabled = false;
}
I click the "button2" and the "pn1_BtnPaste" is enabled anyway.
Data can appear on the clipboard at any time. The Application.Idle event is a decent way to update the button state:
public Form1() {
InitializeComponent();
Application.Idle += Application_Idle;
}
You have to unsubscribe it again when the window closes to be on the safe side:
protected override void OnFormClosed(FormClosedEventArgs e) {
Application.Idle -= Application_Idle;
base.OnFormClosed(e);
}
Clipboard.GetDataObject() does not work the way you think it does, it never returns null. If you want to handle any data then you can write the event handler like this:
private void Application_Idle(object sender, EventArgs e) {
PasteButton.Enabled = Clipboard.GetDataObject().GetFormats().Length > 0;
}
But it is pretty likely you'll find out that handling every possible format is lot let practical than you assumed.

Allow certain keyboard strokes / events for disabled control

I have a disabled control. I still want the key down and key up events to get fired off, because I would like to be aware of a certain key.
When I tested this out, it seems like none of the control's events get fired. Any ideas on how to get around this?
Lack of interaction is basic idea of disabling control, it is working as intended.
You might use KeyPreview property of Form and then launch event handlers of your disabled control from Form events.
As usually, there is n+1 ways to achieve what you seek for. So I will present only some of them.
1 - Manipulate Focus. Although easy to implement, it is also volatile as user can manipulate focus as well.
private void button_KeyDown(object sender, KeyEventArgs e)
{
((Control)sender).Controls.Add(helpBox);
((Control)sender).Focus(); // Set focus back to control so its KeyUp will be called
}
private void button_KeyUp(object sender, KeyEventArgs e)
{
((Control)sender).Controls.Remove(helpBox);
}
2 - Multiple calls to same event handler. Yes, it is possible. A bit more complicated and might depend on helpBox control class and/or its events. Possible issues may include multiply fired up event handler, however presented code is safe to use even if such situation will occur.
private void button_KeyDown(object sender, KeyEventArgs e)
{
((Control)sender).Controls.Add(helpBox);
helpBox.KeyUp += button_KeyUp; //This will add KeyUp of our hosting button to hosted control, which means that said KeyUp handler can be raised by both helpBox and button
}
private void button_KeyUp(object sender, KeyEventArgs e)
{
((Control)sender).Controls.Remove(helpBox);
helpBox.KeyUp -= button1_KeyUp; // to avoid unnecessary calls event handler can be easily "subtracted" from helpBox
}
3 - Master control by Form itself by use of KeyPreview set to true, shows controls with "help_" as helpBoxes on controls named .
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
foreach (Control c in this.Controls)
{
string hName = "help_" + c.Name;
Control[] help = this.Controls.Find(hName, true);
if (help.Length>0)
{
c.Controls.Add(help[0]);
}
}
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F1)
{
foreach (Control c in this.Controls)
{
string hName = "help_" + c.Name;
Control[] help = c.Controls.Find(hName, true);
if (help.Length > 0)
{
c.Controls.Remove(help[0]);
}
}
}
}
Note: Control on-screen position depends on its parent position (relative to top-left corner of parent), in other words, by changing parent of control (which happens during Controls.Add) control will change its position.
4 - Draw it by yourself! Manipulating controls is not always best solution, instead you might want to just draw something on control.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
button1.Tag = true;
button1.Update();
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
button1.Tag = false;
button1.Update();
}
// there is no reason to re-draw it constantly, Paint event will be fired when needed
private void button1_Paint(object sender, PaintEventArgs e)
{
// remember that all Tag's start as null
if (((Control)sender).Tag != null && (bool)((Control)sender).Tag == true)
{
// and when needed just paint on control drawing area (also known as Canvas)
e.Graphics.FillRectangle(Brushes.Red, 0, 0, 100, 100);
e.Graphics.DrawString("Foo!", ((Control)sender).Font, Brushes.Beige, new PointF(5, 5));
}
}
Note: Every Control have Tag property, due to it being of object type you can store in it anything you want. Which makes it universal custom property. In case you wonder about meaning of this property: it have no meaning, it is made to be user-defined.

Windows C# CheckedListBox Checked Item Event Handling

I'm currently developing a Window app that uses CheckedListBoxes for certain aspects of the program. A problem I've encountered is that I have been trying to find which event is triggered when an item is checked so that I can enable a form button when any list item is checked.
Problem is that I tried using the following;
private void clbAvailMods_ItemCheck(object sender, ItemCheckEventArgs e)
{
if(e.NewValue == CheckState.Checked)
{
btnInstall.Enabled = true;
}
}
but when I set a breakpoint on the if statement, it never fires upon checking an item in the listbox.
Am I doing something wrong here?
A standard Windows Forms trick is to delay running code until all event side-effects have been completed. You delay running code with the Control.BeginInvoke() method. This will fix your problem:
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e) {
this.BeginInvoke(new MethodInvoker(evalList), null);
}
private void evalList() {
bool any = false;
for (int ix = 0; ix < checkedListBox1.Items.Count; ++ix) {
if (checkedListBox1.GetItemChecked(ix)) {
any = true;
break;
}
}
btnInstall.Enabled = any;
}
You can use the NewValue property to manually update CheckedItems.Count. This is the code I use to only enable a button when there's at least one item checked:
private void checkedListBoxProds_ItemCheck(object sender, ItemCheckEventArgs e)
{
this.buttonGenerar.Enabled = ((this.checkedListBoxProds.CheckedItems.Count + (e.NewValue == CheckState.Checked ? 1 : -1)) > 0);
}
A couple of potential gotchas. Presumably you've added the event through the VS.Net GUI to ensure that it gets plumbed into the control. Try clicking on an item twice - once to give the item focus and again to toggle the check state - if you want an item to have its check state toggled on first click then set the "CheckOnClick" property to true.
I think it is the SelectedIndexChanged event but I will confirm right now.
EDIT: SelectedIndexChanged event does work. But that is firing regardless of whether the checkbox was checked. So I would then check the checked state if you want to do that.
But as an aside when I did use the ItemCheck event it did fire when I actually checked the checkbox and not just the text.
I know this has been answered long ago, but I found it easier to just handle the MouseUp and KeyUp events. The CheckedItems.Count property is accurate when those events are fired. Since they both do the same thing, I created a method to to the work and called that method from both event handlers.
private void clbFolders_KeyUp(object sender, KeyEventArgs e) { Update(); }
private void clbFolders_MouseUp(object sender, MouseEventArgs e) { Update(); }
private void Update()
{
btnDelete.Enabled = clbFolders.CheckedItems.Count > 0;
}

Categories

Resources