Drag and Drop from Outlook into Winforms - c#

When dragging items from Outlook email into a Winforms app (Control is a GalleryControl by DevExpress, the DragDrop event is not firing, even though i manually set 'DragDropEffects.Move` in the DragEnter event handler. (have confirmed that this is firing)
However DragDrop event does fire just when dragging normal files from windows explorer.
private async void gcImages_DragDrop(object sender, DragEventArgs e)
{
string[] fileNames = null;
if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
{
fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
OutlookDataObject dataObject = new OutlookDataObject(e.Data);
string[] filenames = (string[])dataObject.GetData("FileGroupDescriptor");
}
// do stuff async with file names
}
private void gcImages_DragEnter(object sender, DragEventArgs e)
{
// This event fires, no matter what i drag onto it. (Files from explorer, attachments from Outlook etc)
// However even after setting the effect as per below, the cursor still shows the 'not allowed' symbol.
e.Effect = DragDropEffects.Move;
}
I have enabled AllowDrop = true on the control, and it works perfectly with Windows Explorer files, just not outlook files.
The strange thing is that the DragEnter event is firing, but the DragDrop event does not fire with Outlook attachments.

Ended up using this code, seemed to work fine.
//// Use Like This
private void gcImages_DragDrop(object sender, DragEventArgs e)
{
DragDropHelper.AcceptDroppedFile(e, AddAndSaveNewDocument);
}
private void AddAndSaveNewDocument(FileSystemInfo fileInfo)
{
}
////
public static class DragDropHelper
{
public static void AcceptDroppedFile(DragEventArgs e, Action<FileInfo> addAndSaveNewDocument)
{
string[] fileNames = null;
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
var dataObject = new OutlookDataObject(e.Data);
fileNames = (string[])dataObject.GetData("FileGroupDescriptor");
for (var i = 0; i < fileNames.Length; i++)
{
var itm = fileNames[i];
using var ms = dataObject.GetData("FileContents", i);
var tmpFileName = Path.Combine(Path.GetTempPath(), itm);
using (var file = new FileStream(tmpFileName, FileMode.Create, System.IO.FileAccess.Write))
{
byte[] bytes = new byte[ms.Length];
ms.Read(bytes, 0, (int)ms.Length);
file.Write(bytes, 0, bytes.Length);
ms.Close();
}
fileNames[i] = tmpFileName;
}
}
if (fileNames != null)
{
foreach (var fileName in fileNames)
{
var fileInfo = new FileInfo(fileName);
addAndSaveNewDocument(fileInfo);
if (fileName.Contains(Path.GetTempPath(), StringComparison.CurrentCultureIgnoreCase))
{
File.Delete(fileName);
}
}
}
}
}
Code here for OutlookDataObject class https://codeshare.io/G7747D

Related

How can i add to the listView the searching results in real time?

My program search for text inside files.
But what i want to do is to see the searching progress in real time.
I want to add the current file name search in to listView and also to display to a progressBar the percentages from 0 to 100%.
I added a backgroundworker but the way i'm using the ReportProgress is not working good. I need to wait for it to finish the foreach in the FindLines method and then only in the end i see the items in the listView and even then the items are a big mess.
This is the line that report:
backgroundWorker1.ReportProgress(0, fi.Name);
This is a screenshot of the items in the listView when the foreach is over:
What i want to do is to display the items adding to the listView in real time without waiting first to the operation over and also to add each item to a line and if a line is too long then later i will add a tip baloon or something. But now the listView look like a big mess.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
namespace Search_Text_In_Files
{
public partial class Form1 : Form
{
StreamWriter w = new StreamWriter(#"e:\textresults.txt");
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
public List<string> FindLines(string DirName, string TextToSearch)
{
int counter = 0;
List<string> findLines = new List<string>();
DirectoryInfo di = new DirectoryInfo(DirName);
if (di != null && di.Exists)
{
if (CheckFileForAccess(DirName) == true)
{
foreach (FileInfo fi in di.EnumerateFiles("*", SearchOption.AllDirectories))
{
if (string.Compare(fi.Extension, ".cs", true) == 0)
{
backgroundWorker1.ReportProgress(0, fi.Name);
using (StreamReader sr = fi.OpenText())
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
if (s.Contains(TextToSearch))
{
counter++;
findLines.Add(s);
}
}
}
}
}
}
w.Close();
}
return findLines;
}
private bool CheckForAccess(string PathName)
{
// Determine if the path is a file or a directory
if (File.Exists(PathName) == true)
return CheckFileForAccess(PathName);
if (Directory.Exists(PathName) == true)
return CheckFolderForAccess(PathName);
return false;
}
private bool CheckFileForAccess(string FileName)
{
FileSecurity fs = new FileSecurity(FileName, AccessControlSections.Access);
if (fs == null)
return false;
AuthorizationRuleCollection TheseRules = fs.GetAccessRules(true, true, typeof(NTAccount));
if (TheseRules == null)
return false;
return CheckACL(TheseRules);
}
private bool CheckFolderForAccess(string FolderName)
{
DirectoryInfo di = new DirectoryInfo(FolderName);
if (di == null)
return false;
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.Access);
if (acl == null)
return false;
AuthorizationRuleCollection TheseRules = acl.GetAccessRules(true, true, typeof(NTAccount));
if (TheseRules == null)
return false;
return CheckACL(TheseRules);
}
private bool CheckACL(AuthorizationRuleCollection TheseRules)
{
foreach (FileSystemAccessRule ThisRule in TheseRules)
{
if ((ThisRule.FileSystemRights & FileSystemRights.Read) == FileSystemRights.Read)
{
if (ThisRule.AccessControlType == AccessControlType.Deny)
return false;
}
// Run as many other checks as you like
}
return true;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
FindLines(#"d:\c-sharp", "string s1 = treeView1.SelectedNode.Tag as string;");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listView1.Items.Add(e.UserState.ToString());
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
}
}
This is a screenshot of the items in the listView when the foreach is
over: What i want to do is to display the items adding to the listView
in real time without waiting first to the operation over and also to
add each item to a line and if a line is too long then later i will
add a tip baloon or something. But now the listView look like a big
mess.
Did you try setting your Listview's view property to "List" ? That should display the file names in individual lines.
I think you should look into ObservableCollection(T). The collection fires events when items are added to it, and the UI element should automatically detect these changes and update the view.
At least that is how it works in WPF, but i think it should work as well in WinForms.

Overwrite file only works once

I have a simple program that copies files and directories from one place to another. I have it set-up that if there are any exceptions (such as if access to the path is denied) it will create a log file with the error.
I have a button that when pressed, performs the copy action. Everything works fine the first time I press the button and the log file is either created or overwritten with the appropriate error messages.
However, if I press the button a second time, the text file is not overwritten and instead the error messages append. If I close out of my program and run it again, the file is overwritten on the first button press. Any thoughts would be greatly appreciated.
target is a string filepath which I'm getting from a FolderBrowserDialog and taking the selected path and setting it to a textbox. loglist is just a simple List<string> I'm using to store the error messages from any exceptions that occur during the copy process.
public partial class Form1 : Form
{
static List<string> logList = new List<string>();
public Form1()
{
InitializeComponent();
}
private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
if (source.FullName.ToLower() == target.FullName.ToLower())
return;
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
try
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
}
catch (Exception ex)
{
logList.Add(ex.Message);
}
}
foreach (DirectoryInfo diSourceSub in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSub.Name);
CopyAll(diSourceSub, nextTargetSubDir);
}
}
private void directoryPickerBtn1_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
DialogResult folderResult = folderDialog.ShowDialog();
if (folderResult == DialogResult.OK)
{
directoryTextbox1.Text = folderDialog.SelectedPath;
}
}
private void directoryPickerBtn2_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
DialogResult folderResult = folderDialog.ShowDialog();
if (folderResult == DialogResult.OK)
{
directoryTextbox2.Text = folderDialog.SelectedPath;
}
}
private void copyBtn_Click(object sender, EventArgs e)
{
string source = (directoryTextbox1.Text);
string target = (directoryTextbox2.Text);
DirectoryInfo dirSource = new DirectoryInfo(source);
DirectoryInfo dirTarget = new DirectoryInfo(target);
try
{
CopyAll(dirSource, dirTarget);
if (logList.Count > 0)
{
using (StreamWriter sw = new StreamWriter(target + #"\log.txt", false))
{
foreach (string error in logList)
{
sw.WriteLine(error);
}
}
}
DialogResult result = MessageBox.Show("Copy Succeeded", "Success");
if (result == DialogResult.OK)
{
string myPath = dirTarget.ToString();
System.Diagnostics.Process prc = new System.Diagnostics.Process();
prc.StartInfo.FileName = myPath;
prc.Start();
}
}
catch (Exception)
{
MessageBox.Show("Copy Failed", "Failed");
}
}
}
}
As #Reza Aghaei pointed out in comments, the problem is that you do not clear the logList.
The file gets created anew every time, but each time you click the Copy button, the loglist still contains the results of the previous copy action.
So you need to clear the list when starting a new copy:
private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
logList.Clear();
// ...
From your code it seems that you never clear the logList, this means that it appears the file is being appending because the logList still contains all of the old entries.
You'll need to clear the list between copies if you only want relevant entries to that copy, either before you start copying or after you finish writing the file.
This would be better as a separate method
try
{
CopyAll(dirSource, dirTarget);
SaveLog(target + #"\log.txt");
ClearLog();
//...
}
private void SaveLog(string filename)
{
if (logList.Count > 0)
{
FileStream fs = File.Open(target + #"\log.txt", FileMode.Create);
using (StreamWriter sw = new StreamWriter(fs))
{
foreach (string error in logList)
{
sw.WriteLine(error);
}
}
}
}

My method did not add my file into my list although no error

This is my class that after each file choose add the file to my list and from the main form raise event that update my ListBox and add the file into my ListBox.
when i am choosing 2 files i can see (with the debugger) that the add method add the first file and my list updated but after the second file pass the add method the list.count remained still 1.
public class ListboxFile
{
public delegate void OnFileAdd(string file);
public event OnFileAdd OnFileAddEvent;
private static List<string> _files;
public ListboxFile()
{
_files = new List<string>();
}
public void add(string file)
{
_files.Add(file);
OnFileAddEvent(file);
}
public void remove(string file)
{
if (_files.Contains(file))
{
_files.Remove(file);
}
}
public void clear()
{
_files.Clear();
}
public List<string> list
{
get { return _files; }
}
}
from the main form (add files button click):
private void btnAddfiles_Click(object sender, EventArgs e)
{
#region file filter
string fileToAdd = string.Empty;
System.IO.Stream stream;
OpenFileDialog thisDialog = new OpenFileDialog();
thisDialog.InitialDirectory = (lastPath.Length > 0 ? lastPath : "c:\\");
thisDialog.Filter = "(*.snoop, *.pcap, *.cap, *.net, *.pcapng, *.5vw, *.bfr, *.erf, *.tr1)" +
"|*.snoop; *.pcap; *.cap; *.net; *.pcapng; *.5vw; *.bfr; *.erf; *.tr1|" + "All files (*.*)|*.*";
thisDialog.FilterIndex = 1;
thisDialog.RestoreDirectory = false;
thisDialog.Multiselect = true;
thisDialog.Title = "Please Select Source File";
#endregion
if (thisDialog.ShowDialog() == DialogResult.OK)
{
if (thisDialog.FileNames.Length > 0)
{
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork +=
(s3, e3) =>
{
foreach (String file in thisDialog.FileNames)
{
try
{
if ((stream = thisDialog.OpenFile()) != null)
{
int numberOfFiles = thisDialog.SafeFileNames.Length;
using (stream)
{
ListboxFile lbf = new ListboxFile();
lbf.OnFileAddEvent += lbf_OnFileAddEvent;
lbf.checkFile(file);
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
};
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
(s3, e3) =>
{
});
backgroundWorker.RunWorkerAsync();
}
}
private void lbf_OnFileAddEvent(string file)
{
if (InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
listBoxFiles.Items.Add(file);
, listBoxFiles.Items.Count.ToString("#,##0")));
if (listBoxFiles.Items.Count != 0)
listBoxFiles.SetSelected(listBoxFiles.Items.Count - 1, true);
});
}
else
{
listBoxFiles.Items.Add(file);
if (listBoxFiles.Items.Count != 0)
listBoxFiles.SetSelected(listBoxFiles.Items.Count - 1, true);
}
}
You declare and initialize the instance of ListBoxFile inside the foreach loop.
At every loop you reinitialize the instance and thus you loose the previous add
A fast fix, move the declaration and initialization of the ListboxFile instance outside the loop (also the subscription to the event)
.....
ListboxFile lbf = new ListboxFile();
lbf.OnFileAddEvent += lbf_OnFileAddEvent;
foreach (String file in thisDialog.FileNames)
{
.....
and by the way, you call lbf.checkFile(file);, did you mean lbf.Add(file) right?
You are calling lbf.checkFile(file); but I do not see that method in your class definition. Maybe inside that method you are not firing OnFileAddEvent?

Faster way of reading csv to grid

I have following in Windows Forms .NET 3.5
It works fine for csv with records less than 10,000 but is slower for records above 30,000.
Input csv file can can any records between 1 - 1,00,000 records
Code currently used :
/// <summary>
/// This will import file to the collection object
/// </summary>
private bool ImportFile()
{
try
{
String fName;
String textLine = string.Empty;
String[] splitLine;
// clear the grid view
accountsDataGridView.Rows.Clear();
fName = openFileDialog1.FileName;
if (System.IO.File.Exists(fName))
{
System.IO.StreamReader objReader = new System.IO.StreamReader(fName);
do
{
textLine = objReader.ReadLine();
if (textLine != "")
{
splitLine = textLine.Split(',');
if (splitLine[0] != "" || splitLine[1] != "")
{
accountsDataGridView.Rows.Add(splitLine);
}
}
} while (objReader.Peek() != -1);
}
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("The process cannot access the file"))
{
MessageBox.Show("The file you are importing is open.", "Import Account", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
MessageBox.Show(ex.Message);
}
return false;
}
}
Sample Input file :
18906,Y
18908,Y
18909,Y
18910,Y
18912,N
18913,N
Need some advice on optimizing this code for fast reads & view in grid.
List<string[]> rows = File.ReadAllLines("Path").Select(x => x.Split(',')).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("1");
dt.Columns.Add("2");
rows.ForEach(x => {
dt.Rows.Add(x);
});
dgv.DataSource = dt;
Try that, I suspected that you have some form of column names in the datagrid for now I just made them 1 and 2.
To filter as per your original code use:
List<string[]> rows = File.ReadAllines("Path").Select(x => x.Split(',')).Where(x => x[0] != "" && x[1] != "").ToList();
To get your columns from the DataGridView
dt.Columns.AddRange(dgv.Columns.Cast<DataGridViewColumn>().Select(x => new DataColumn(x.Name)).ToArray());
There isn't much to optimize in regards to speed, but following is much more readable. If it is too slow, it probably isn't the method reading the file, but your WinForm that needs to display >30k records.
accountsDataGridView.Rows.Clear();
using (FileStream file = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096))
using (StreamReader reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
var fields = reader.ReadLine().Split(',');
if (fields.Length == 2 && (fields[0] != "" || fields[1] != ""))
{
accountsDataGridView.Rows.Add(fields);
}
}
}
You can try to use SuspendLayout() and ResumeLayout() Methods.
From MSDN Documentation
"The SuspendLayout and ResumeLayout methods are used in tandem to suppress multiple Layout events while you adjust multiple attributes of the control. For example, you would typically call the SuspendLayout method, then set the Size, Location, Anchor, or Dock properties of the control, and then call the ResumeLayout method to enable the changes to take effect."
accountsDataGridView.SuspendLayout();
accountsDataGridView.Rows.Clear();
// .....
// in the end after you finished populating your grid call
accountsDataGridView.ResumeLayout();
Instead of putting the data directly into the grid you should take a look at the VirtualMode of the DataGridView.
In your code you're doing two things at one time (read the file, fill the grid), which leads to your freezed gui. Instead you should put the grid into the virtual mode and read the file within a BackgroundWorker into a list which holds the data for the grid. The background worker can after each line read update the virtual size of the grid, which allows to already see the data while the grid is loading. By using this approach you'll getting a smooth working grid.
Below you'll find an example which just needs to be filled into a form that uses a DataGridView with two text columns, a BackgroundWorker and a Button:
public partial class FormDemo : Form
{
private List<Element> _Elements;
public FormDemo()
{
InitializeComponent();
_Elements = new List<Element>();
dataGridView.AllowUserToAddRows = false;
dataGridView.AllowUserToDeleteRows = false;
dataGridView.ReadOnly = true;
dataGridView.VirtualMode = true;
dataGridView.CellValueNeeded += OnDataGridViewCellValueNeeded;
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += OnBackgroundWorkerDoWork;
backgroundWorker.ProgressChanged += OnBackgroundWorkerProgressChanged;
backgroundWorker.RunWorkerCompleted += OnBackgroundWorkerRunWorkerCompleted;
}
private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var filename = (string)e.Argument;
using (var reader = new StreamReader(filename))
{
string line = null;
while ((line = reader.ReadLine()) != null)
{
var parts = line.Split(',');
if (parts.Length >= 2)
{
var element = new Element() { Number = parts[0], Available = parts[1] };
_Elements.Add(element);
}
if (_Elements.Count % 100 == 0)
{
backgroundWorker.ReportProgress(0);
}
}
}
}
private void OnBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
dataGridView.RowCount = _Elements.Count;
}
private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dataGridView.RowCount = _Elements.Count;
button.Enabled = true;
}
private void OnButtonLoadClick(object sender, System.EventArgs e)
{
if (!backgroundWorker.IsBusy
&& DialogResult.OK == openFileDialog.ShowDialog())
{
button.Enabled = false;
backgroundWorker.RunWorkerAsync(openFileDialog.FileName);
}
}
private void OnDataGridViewCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
var element = _Elements[e.RowIndex];
switch (e.ColumnIndex)
{
case 0:
e.Value = element.Number;
break;
case 1:
e.Value = element.Available;
break;
}
}
private class Element
{
public string Available { get; set; }
public string Number { get; set; }
}
}

Drag and Drop file transfer in WPF. How to get file name of the dropped content in a window

I'm creating a local file transfer app. I would like the user to drag-drop an item into the file transfer application to initiate the file transfer just like skype or other messengers.
While dropping an item. The drop event was triggered. But, I don't know where to get the details of the item such as Location, Size etc., eg., If I drop an Image. I want to read the details mentioned above.
Note:I have enabled the AllowDrop & Subsribed to Drop event.[If that helps]
Do you mean Size of file or Size of image in pixel?
Anyway, use this code:
private void Window_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
{
string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[];
foreach (var path in droppedFilePaths)
{
string location = null;
int pxWidth = 0, pxHeight = 0;
FileInfo fi = new FileInfo(path);
//fi.Length //File size
//fi.DirectoryName //Directory
using (var fs = fi.OpenRead())
{
try
{
var bmpFrame = BitmapFrame.Create(fs);
var m = bmpFrame.Metadata as BitmapMetadata;
if (m != null)
location = m.Location;
pxWidth = bmpFrame.PixelWidth;
pxHeight = bmpFrame.PixelHeight;
}
catch
{
//File isn't image
}
}
this.fileList.Items.Add(string.Format("({0}x{1}), location: {2}", pxWidth, pxHeight, location));
}
}
}

Categories