Live update of TreeView while populating in background thread - c#

Topic says it all , here it is code i have right now :
public void StartCDA()
{
backgroundWorker1.RunWorkerAsync();
}
public delegate void AddCDAnode(DirectoryInfo dirinfo);
public void ListDirectory(DirectoryInfo path)
{
ImagesTV.Nodes.Add(CreateDirectoryNode(path));
}
public void Addnode(DirectoryInfo dirinfo)
{
Invoke(new AddCDAnode(ListDirectory), new object[] {dirinfo});
}
private TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
Statustext = directory.FullName;
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
foreach (var file in directoryInfo.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Addnode(CdaDir);
}
Right now problem is that actually Treeview is not being populated in background and that's pretty strange as i am using background worker for that. But the main problem is that here i have kind of image viewer (form is splitted in two parts, left is treeview and right is picture preview box) and list of images to view sometime contains about 1000 elements , that's why i need to be able to see "live" update of treeview , while being able to go through items and view images. There will be no ability to delete , move or somehow affect Treeview , the only action is to click through tree.
Thanks.

Related

Dragging a file from a TreeNode to the desktop or Windows Explorer?

I am attempting to drag a file represented by a TreeView node onto the desktop, Windows Explorer, or any other applications that allow files to be dropped onto them. I've written the code below based on various Internet examples I've found and I'm running it as administrator. It does allow me to drag as long as I remain in the TreeView control that contains the nodes, displaying the Copy icon with the cursor as it moves. However, when I drag it off the control to the desktop or Windows Explorer for example, the icon turns into the red circle with a slash across it and nothing gets dropped. I've ensured that the dragged file actually does exist.
private void treeView_Files_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item, DragDropEffects.Copy);
}
private void treeView_Files_DragDrop(object sender, DragEventArgs e)
{
TreeNode NewNode;
if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
{
Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
TreeNode DestinationNode = ((TreeView)sender).GetNodeAt(pt);
NewNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
string[] files = new string[] { "C:\\temp\\TestFile.pdf" };
DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
DoDragDrop(dataObject, DragDropEffects.Copy);
}
}
I was unable to figure out why my original code above would not display the Copy icon on the cursor unless it was within the TreeView control itself and, hence, would not let me drop the desired file. However, I was able to come up with a solution that does display the icon and will drop on any application capable of accepting a dropped file. I simply abandoned the use of the ItemDrag and DragDrop events entirely and used the MouseDown event instead as shown below. As in my original code I'm using a test file instead of the file represented by the node, but extracting the real file path from the node is trivial. Of course some additional simple code is necessary to determine if the coordinates are actually on a node:
private void treeView_Files_MouseDown(object sender, MouseEventArgs e)
{
TreeNode node = treeView_Files.GetNodeAt(e.X, e.Y);
string[] files = new string[] { "C:\\temp\\TestFile.pdf" };
DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
DoDragDrop(dataObject, DragDropEffects.Copy);
}
Try constructing the DataObject and performing the DoDragDrop() operation inside the ItemDrag function, instead of the DragDrop function:
private void treeView_Files_ItemDrag(object sender, ItemDragEventArgs e)
{
string[] files = new string[] { "C:\\temp\\TestFile.pdf"};
DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
DoDragDrop(dataObject, DragDropEffects.Copy);
}
The e.Item parameter (which is the TreeView item) is not really the object that you want to pass to Explorer, so you shouldn't be putting it into the DoDragDrop call.
On a side note, I spent a long time researching how it's possible to get the "destination" folder onto which the item was dropped. My solution involves roughly the following:
Create a FileSystemWatcher and make it send events when new files are created.
Give it a Filter that matches the file name that you're dropping.
When the file is dropped (i.e. when it's created in the destination folder), you'll receive an event via the watcher.
Something like this:
private void CreateWatcher()
{
var watcher = new FileSystemWatcher();
watcher.Filter = "TestFile.pdf";
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created += new FileSystemEventHandler(OnWatcherFileCreated);
watcher.IncludeSubdirectories = true;
watcher.Path = "C:\\";
watcher.EnableRaisingEvents = true;
}
private void OnWatcherFileCreated(object sender, FileSystemEventArgs e)
{
// Note: this happens in a separate non-UI thread.
Console.WriteLine("Dropped onto: " + e.FullPath);
}

Hide/modify string in listView

I am making a program in which users can modify remote files. I put the selected files (depending on some predefined criteria) in a listView, but I display only the file names, not full filepaths.
The problem I get however, is that when a user would double-click on an item, it should open another window to modify that item.
private void listView1_DoubleClick(object sender, EventArgs e)
{
account = File.ReadAllLines("\\\\myremoteserver\\ftp\\"+listView1.SelectedItems[0].Text+".txt");
Form3 passForm = new Form3();
passForm.ShowDialog();
}
private void Form2_Load(object sender, EventArgs e)
{
string[] files = Directory.GetFiles("\\\\myremotserver\\ftp\\","*.txt", System.IO.SearchOption.AllDirectories);
foreach (string s in files)
{
listView1.Items.Add(Path.GetFileNameWithoutExtension(s));
}
}
The problem is, that the files are all in different subfolders, so if I leave the code as is, it will not display the correct content of the file. For example, the file is called test1.txt, it is placed in myremoteserver\ftp\testfolder\test1.txt, but with my program, it will try to find the file in myremoteserver\ftp\test1.txt.
What I am asking is, if it is possible to modify the listView in such a way, that the full file path is always saved, but only the file names are displayed? I do not want the user to see the complete file path of the files, just the file names.
Use the Tag property of the ListViewItem
So to create items...
foreach (string s in files)
{
ListViewItem lvi = new ListViewItem(Path.GetFileNameWithoutExtension(s));
lvi.Tag = s;
listView1.Items.Add(lvi);
}
Then in event handler...
account = File.ReadAllLines("\\\\myremoteserver\\ftp\\"+listView1.SelectedItems[0].Tag +".txt);

Drag multiple files

I have a ListBox with a list of filepaths, which has the property SelectionMode set to MultiExtended. Thus, I can select many items from this list.
Now, I want to Drag and Drop files starting from those paths in the destination folder where I drop them.
My code:
private void Form1_Load(object sender, EventArgs e)
{
// populate the FileList
// i.e. FileList.Items.Add("d:/Samples/file-00" + i + ".wav");
this.FileList.MouseDown += new MouseEventHandler(FileList_MouseDown);
this.FileList.DragOver += new DragEventHandler(FileList_DragOver);
}
void FileList_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
void FileList_MouseDown(object sender, MouseEventArgs e)
{
List<string> filesToDrag = new List<string>();
foreach (var item in FileList.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
this.FileList.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
it works perfect if I select and drop 1 single line/file from the ListBox to the destination folder.
Instead, if I do a multiple selection and I try to drag and drop, it can just select that one line where I start to drag. Seems that MouseDown prevent this?
How would you fix the problem?
Doing this with a ListBox seems to be ridiculously hard. Actually I haven't found a solution at all..
Use a ListView instead! It is easy as pie, using the ItemDrag event and is a much better control anyway.. I can't count how often I had to change from a 'cheap' ListBox to ListView because I needed this or that 'little' extra..
Here is your code moved to a ItemDrag:
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
List<string> filesToDrag = new List<string>();
foreach (var item in listView1.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
this.listView1.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
Note that this only solves the problem of the MouseDown changing the selection. It is in itself not a guarantuee that the actual copying will work.
I found this interesting article that proposes a solution. Maybe you don't need it, as you have said that you got copying one file already working..
Yeah...I don't think there's a good way around that problem. When you click again to initiate the drag it will toggle that item. We don't know that the user actually wants to do a drag until the mouse is held down and moved.
One potential solution is to initiate the drag/drop from something else, just somehow make it really clear that is what the user should drag. Here I'm using a Label instead of the ListBox:
void label1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
List<string> filesToDrag = new List<string>();
foreach (var item in FileList.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
if (filesToDrag.Count > 0)
{
this.FileList.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
else
{
MessageBox.Show("Select Files First!");
}
}
}
You have to be picky about the mouse down and mouse move activities. When it is within the graphics rectangle of your listbox, you'll want normal behavior. When it is outside the bounds of this rectangle, you'll want drag/drop functionality. You can try the pseudo code below:
MouseDown(sender, e)
{
var x = <your sender as control>.ItemFromPoint(.....)
this.mouseLocation = x == null ? x : e.Location;
}
MouseMove(sender, e)
{
if control rectangle doesn't contain the current location then
<your sender as control>.Capture = false
DoDragDrop
}

Text_Changed problems

Hello i have a search box, that is a TextBox, that filters the ListBox.
I have an array of items and the code does next thing:
private async void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
ListBox.Items.Clear();
foreach (Item a in arr)
{
if(a.Title.Contains(SearchTextBox.Text))
{
ListBox.Items.Add(a);
}
}
}
All works fine when i delete by 1 symbol, but when i hold backspace something strange happens, items start to duplicate or change their positions - what is the problem and how to prevent this?
You are likely re-entering the handler on the same thread (the UI thread) while you are looping. One option is to detach the handler upon entry and then reattach on exit. You will probably need to do a double check then that nothing else happened while you had it detached.
I am not as familiar with Window 8 apps, but filtering a ListBox based on a TextBox is very common. I would expect you to be able to use something like CollectionViewSource.Filter to do the filtering automatically (see this question for an example).
A basic implementation:
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
string initialText = SearchTextBox.Text;
SearchTextBox.TextChanged -= SearchTextBox_TextChanged;
do
{
ListBox.Items.Clear();
foreach (Item a in arr)
{
if(a.Title.Contains(initialText))
{
ListBox.Items.Add(a);
}
}
} while (SearchTextBox.Text != initialText)
SearchTextBox.TextChanged += SearchTextBox_TextChanged;
}
Ok, I see this is async, you need to synchronize this method
Create an instance object named _syncRoot:
private object _syncRoot = new Object();
private async void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
lock( _syncRoot)
{
ListBox.Items.Clear();
foreach (Item a in arr)
{
if(a.Title.Contains(SearchTextBox.Text))
{
ListBox.Items.Add(a);
}
}
}
}

C#: how to check and display the content of a folder?

I'm not sure whether this topics has been disscussed before or not, but I'm not sure the exact word to search for it. What method/class should I use?
The program has 3 buttons: 1) for folder browsing, 2) scan for the selected folder content, and 3) open the file. When user browse the selected folder**(1), user click scan button to scan from the first file until the last available files and listed it text box(2)** and from that user can decide whether to open the files or not**(3)**.
Here are what have I done so far (no 1 and 3):
//For browse.
private void browse2()
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
this.txtDest.Text = folderBrowserDialog1.SelectedPath;
}
}
//For opening folder.
private void btnOpen_Click(object sender, EventArgs e)
{
try
{
Process.Start(txtDest.Text);
}
catch
{
MessageBox.Show("Please select one file/folder");
}
}
If you are trying to just open a file you can directly use an Open File Dialog.
If you need to display the contents of a directory you can use the Directory Info Class.
Well my example is a WPF app that adds files/ folders in a directory to a treeview, but you should get the general idea:
Note: Code was written for a training exercise, and therefore only goes 3 levels deep, as a proof of concept kind of thing
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (DriveInfo di in DriveInfo.GetDrives())
{
TreeViewItem drive = new TreeViewItem();
drive.Header = di.Name;
treeView1.Items.Add(drive);
DirectoryInfo folders = new DirectoryInfo(di.Name);
// The depth count means that it only goes 3 levels deep, to make it quick to load
GetFoldersAndFiles(drive, folders, 3);
}
}
private static void GetFoldersAndFiles(TreeViewItem parent, DirectoryInfo folders, int depth)
{
if ((depth > 0)
{
foreach (DirectoryInfo dirI in folders.GetDirectories())
{
TreeViewItem dir = new TreeViewItem();
dir.Header = dirI.Name;
parent.Items.Add(dir);
GetFoldersAndFiles(dir, dirI, depth - 1);
}
foreach (FileInfo fileI in folders.GetFiles())
{
TreeViewItem file = new TreeViewItem();
file.Header = fileI.Name;
parent.Items.Add(file);
}
}
}

Categories