locked
ListView Double Click File Open

    Question

  • Hello,

    I was using this example:

    http://www.codeproject.com/csharp/tv-lv-basic41.asp

    On how to make a listview and listbox to amke a file explorer.

    I want it so that if you double click an html file or .rtf file in the listview then it will open that file in my RIchTextBox.

    How can I do this?

     

     

     

     

    Thanks,

    Programmer01

    Monday, November 05, 2007 8:39 PM

Answers

  • Hello again,

     

    Your in luck I needed something similar for a project I'll be doing once I get around to it. It's not fully polished but its a good start. The trick with parsing a directory structure (or any tree structure) is recursion. Recursion is where a method calls itself over and over again until some condition is true. It is ideal when you need to parse a complex structure where you do not know how many items are in the structure, like a directory (unknown number of files and sub directories).

     

    So here is the bulk of the code, this actually populate a TreeView rather than a ListView, a TreeView is more like how a directory structure is. If this isn't suitable you can adapt this code to suit, the logic stays the same it just creates ListView items instead of TreeNodes.

     

    private void PopulateTreeFromDirectory(DirectoryInfo currentDirectory, TreeNode currentNode)

    {

    //for each file in the directory

    FileInfo[] files = currentDirectory.GetFiles("*.*", SearchOption.TopDirectoryOnly);

    foreach (FileInfo file in files)

    {

    //create a new node on the tree

    TreeNode newFileNode = new TreeNode();

    newFileNode.Name = file.FullName;

    newFileNode.Text = Path.GetFileName(file.FullName);

    //TODO: set a file image

    currentNode.Nodes.Add(newFileNode);

    }

    //for each sub directory in the directory

    DirectoryInfo[] subDirectories = currentDirectory.GetDirectories();

    foreach (DirectoryInfo subDirectory in subDirectories)

    {

    //create a new node on the tree

    TreeNode newDirectoryNode = new TreeNode();

    newDirectoryNode.Name = subDirectory.FullName;

    newDirectoryNode.Text = "\\" + Path.GetDirectoryName(subDirectory.FullName);

    //TODO: set a directory image

    currentNode.Nodes.Add(newDirectoryNode);

    //populate the tree from a directory, this time the sub directory

    PopulateTreeFromDirectory(subDirectory, newDirectoryNode);

    }

    }

     

    And you can call the code like this, this will populate a TreeView with the files and folders in the MyDocuments folder

    DirectoryInfo myDocumentsFolder =

    new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));

    TreeNode root = new TreeNode("MyDocuments");

    PopulateTreeFromDirectory(myDocumentsFolder, root);

    this.treeView1.Nodes.Add(root);

     

    It's not perfect but it's a good start. Each TreeNode.Name property is set to the filename of the file so whenever a selection is made you can check the TreeView.SelectedNodes.Name property to get the path to the file that was selected.

     

     

     

    Tuesday, November 06, 2007 12:29 PM
  • Hi,

     

    Here is an update to the code, the directory name displays better, the directories are listed first and then the files (looks more familiar) and the AfterSelect event will display a message box displaying the name of the file that needs to be open.

     

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();

                DirectoryInfo myDocumentsFolder =
                    new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
                TreeNode root = new TreeNode("MyDocuments");

                PopulateTreeFromDirectory(myDocumentsFolder, root);
                this.treeView1.Nodes.Add(root);

            }


            private void PopulateTreeFromDirectory(DirectoryInfo currentDirectory, TreeNode currentNode)
            {
                //for each sub directory in the directory
                DirectoryInfo[] subDirectories = currentDirectory.GetDirectories();
                foreach (DirectoryInfo subDirectory in subDirectories)
                {
                    //create a new node on the tree
                    TreeNode newDirectoryNode = new TreeNode();
                    newDirectoryNode.Name = subDirectory.FullName;
                    newDirectoryNode.Text = "\\" + subDirectory.Name;
                    //TODO: set a directory image
                    currentNode.Nodes.Add(newDirectoryNode);

                    //populate the tree from a directory, this time the sub directory
                    PopulateTreeFromDirectory(subDirectory, newDirectoryNode);
                }

                //for each file in the directory          
                FileInfo[] files = currentDirectory.GetFiles("*.*", SearchOption.TopDirectoryOnly);
                foreach (FileInfo file in files)
                {
                    //create a new node on the tree
                    TreeNode newFileNode = new TreeNode();
                    newFileNode.Name = file.FullName;
                    newFileNode.Text = Path.GetFileName(file.FullName);
                    //TODO: set a file image
                    currentNode.Nodes.Add(newFileNode);
                }
            }

            private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
            {
                MessageBox.Show(this.treeView1.SelectedNode.Name);
            }

        }

    Wednesday, November 07, 2007 10:00 AM
  • Hi again

     

    made a mistake, not the first and not the last, instead of "this.treeView1.SelectedNode" in the AfterSelect event you can use e.Node. Its the same thing, but the e.node seems more appropriate.

     

    Could it be a .html file. I'd use the System.IO.Path.GetExtension() method.

     

    if (System.IO.Path.GetExtension(e.Node.Name) = ".htm" || System.IO.Path.GetExtension(e.Node.Name) = ".html")

     

    There will be a better way to deal with the larger number of file types than lumping them into one large if statement. Set a breakpoint and see whats going on if you still can't get it to work, it will be something simple.

     

     

    Thursday, November 08, 2007 10:09 AM
  • if (System.IO.Path.GetExtension(e.Node.Name) == ".htm" || System.IO.Path.GetExtension(e.Node.Name) == ".html")

     

    Two equals signs in C#, one equals for VB.NET. Smile

    Maybe your being seduced by the dark side.

     

     

    *edit: yeah, that was my fault sorry about that.

    Friday, November 09, 2007 9:54 AM
  • Ok I solved the problem:

     

    I created this method:

    Code Block

    private void LoadFile1(string filename)

    {

    using (StreamReader thereader = File.OpenText(filename))

    {

    RichTextBox rtb2 = new RichTextBox();

    rtb2 = tabControl1.SelectedTab.Controls[0] as RichTextBox;

    rtb2.Text = thereader.ReadToEnd();

    thereader.Close();

    }

    }

     

     

     

    and instead of all that other stuff in afterselect I changed it to this:

    Code Block

    _filename = tbSelectedFile1.Text;

    LoadFile1(_filename);

     

     

     

    Now it works.

     

    Thanks for the help,

    Programmer01

    Saturday, November 17, 2007 8:31 PM

All replies

  • Hello again,

     

    Your in luck I needed something similar for a project I'll be doing once I get around to it. It's not fully polished but its a good start. The trick with parsing a directory structure (or any tree structure) is recursion. Recursion is where a method calls itself over and over again until some condition is true. It is ideal when you need to parse a complex structure where you do not know how many items are in the structure, like a directory (unknown number of files and sub directories).

     

    So here is the bulk of the code, this actually populate a TreeView rather than a ListView, a TreeView is more like how a directory structure is. If this isn't suitable you can adapt this code to suit, the logic stays the same it just creates ListView items instead of TreeNodes.

     

    private void PopulateTreeFromDirectory(DirectoryInfo currentDirectory, TreeNode currentNode)

    {

    //for each file in the directory

    FileInfo[] files = currentDirectory.GetFiles("*.*", SearchOption.TopDirectoryOnly);

    foreach (FileInfo file in files)

    {

    //create a new node on the tree

    TreeNode newFileNode = new TreeNode();

    newFileNode.Name = file.FullName;

    newFileNode.Text = Path.GetFileName(file.FullName);

    //TODO: set a file image

    currentNode.Nodes.Add(newFileNode);

    }

    //for each sub directory in the directory

    DirectoryInfo[] subDirectories = currentDirectory.GetDirectories();

    foreach (DirectoryInfo subDirectory in subDirectories)

    {

    //create a new node on the tree

    TreeNode newDirectoryNode = new TreeNode();

    newDirectoryNode.Name = subDirectory.FullName;

    newDirectoryNode.Text = "\\" + Path.GetDirectoryName(subDirectory.FullName);

    //TODO: set a directory image

    currentNode.Nodes.Add(newDirectoryNode);

    //populate the tree from a directory, this time the sub directory

    PopulateTreeFromDirectory(subDirectory, newDirectoryNode);

    }

    }

     

    And you can call the code like this, this will populate a TreeView with the files and folders in the MyDocuments folder

    DirectoryInfo myDocumentsFolder =

    new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));

    TreeNode root = new TreeNode("MyDocuments");

    PopulateTreeFromDirectory(myDocumentsFolder, root);

    this.treeView1.Nodes.Add(root);

     

    It's not perfect but it's a good start. Each TreeNode.Name property is set to the filename of the file so whenever a selection is made you can check the TreeView.SelectedNodes.Name property to get the path to the file that was selected.

     

     

     

    Tuesday, November 06, 2007 12:29 PM
  • I think I like your way better. I was thinking of how I could do somthing similar to the file browser in Expression Web or Frontpage how it just shows files in your current project opened. And the way you ddi this can make it easier for me to make this happen than the other way I was doing it. One thing: I was messing around with the treeView1_double click event but I dont see what to say to make it find the file path for the selected item... I tried a few things but nothing worked.

     

     

     

     

    Thanks,

    Programmer01

    Wednesday, November 07, 2007 5:35 AM
  • Hi,

     

    Here is an update to the code, the directory name displays better, the directories are listed first and then the files (looks more familiar) and the AfterSelect event will display a message box displaying the name of the file that needs to be open.

     

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();

                DirectoryInfo myDocumentsFolder =
                    new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
                TreeNode root = new TreeNode("MyDocuments");

                PopulateTreeFromDirectory(myDocumentsFolder, root);
                this.treeView1.Nodes.Add(root);

            }


            private void PopulateTreeFromDirectory(DirectoryInfo currentDirectory, TreeNode currentNode)
            {
                //for each sub directory in the directory
                DirectoryInfo[] subDirectories = currentDirectory.GetDirectories();
                foreach (DirectoryInfo subDirectory in subDirectories)
                {
                    //create a new node on the tree
                    TreeNode newDirectoryNode = new TreeNode();
                    newDirectoryNode.Name = subDirectory.FullName;
                    newDirectoryNode.Text = "\\" + subDirectory.Name;
                    //TODO: set a directory image
                    currentNode.Nodes.Add(newDirectoryNode);

                    //populate the tree from a directory, this time the sub directory
                    PopulateTreeFromDirectory(subDirectory, newDirectoryNode);
                }

                //for each file in the directory          
                FileInfo[] files = currentDirectory.GetFiles("*.*", SearchOption.TopDirectoryOnly);
                foreach (FileInfo file in files)
                {
                    //create a new node on the tree
                    TreeNode newFileNode = new TreeNode();
                    newFileNode.Name = file.FullName;
                    newFileNode.Text = Path.GetFileName(file.FullName);
                    //TODO: set a file image
                    currentNode.Nodes.Add(newFileNode);
                }
            }

            private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
            {
                MessageBox.Show(this.treeView1.SelectedNode.Name);
            }

        }

    Wednesday, November 07, 2007 10:00 AM
  • Ah there we go, cool.

    I added that and all, now in the AfterSelect I want to test the extension by doing this:

    Code Block

    private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)

    {

    //MessageBox.Show(this.treeView1.SelectedNode.Name);

    if (tabControl1.SelectedIndex > 0)

    {

    RichTextBox rtb2 = new RichTextBox();

    rtb2 = tabControl1.SelectedTab.Controls[0] as RichTextBox;

    if(this.treeView1.SelectedNode.Name.Contains(".htm"))

    {

    MessageBox.Show("it knows it is an html doc.");

    }

    }

    }

     

     

     

    but now if I select an html document nothing happens...

    Is this the right way to find the extention?

     

     

    Thanks,

    Programmer01

    Wednesday, November 07, 2007 11:57 PM
  • Hi again

     

    made a mistake, not the first and not the last, instead of "this.treeView1.SelectedNode" in the AfterSelect event you can use e.Node. Its the same thing, but the e.node seems more appropriate.

     

    Could it be a .html file. I'd use the System.IO.Path.GetExtension() method.

     

    if (System.IO.Path.GetExtension(e.Node.Name) = ".htm" || System.IO.Path.GetExtension(e.Node.Name) = ".html")

     

    There will be a better way to deal with the larger number of file types than lumping them into one large if statement. Set a breakpoint and see whats going on if you still can't get it to work, it will be something simple.

     

     

    Thursday, November 08, 2007 10:09 AM
  • ok thanks,

    when I add

    if (System.IO.Path.GetExtension(e.Node.Name) = ".htm" || System.IO.Path.GetExtension(e.Node.Name) = ".html")

    I get an error from ".htm" all the way to ".html" saying:

    Operator '||' cannot be applied to operands of type 'string' and 'string'

     

    and if I take out || to the end then I get these 2 errors:


     

    Error 1 The left-hand side of an assignment must be a variable, property or indexer

    Error 2 Cannot implicitly convert type 'string' to 'bool'

     

     

     

     

    Thanks,

    Programmer01

    Thursday, November 08, 2007 11:11 PM
  • if (System.IO.Path.GetExtension(e.Node.Name) == ".htm" || System.IO.Path.GetExtension(e.Node.Name) == ".html")

     

    Two equals signs in C#, one equals for VB.NET. Smile

    Maybe your being seduced by the dark side.

     

     

    *edit: yeah, that was my fault sorry about that.

    Friday, November 09, 2007 9:54 AM
  •  

    No Problem,

    yeah I actaully at first was wondering if it was supposed to have 2 or not, I should have tested it with 2 before I asked about the problem

     

    yeah no errors with that part now. But now I am stumped, I tried a few things but nothing works here is my code:

    Code Block

    private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)

    {

    //MessageBox.Show(this.treeView1.SelectedNode.Name);

    if (tabControl1.SelectedIndex == 0)

    {

    System.Windows.Forms.DialogResult answer;

    RichTextBox rtb2 = new RichTextBox();

    rtb2 = tabControl1.SelectedTab.Controls[0] as RichTextBox;

    if (System.IO.Path.GetExtension(e.Node.Name) == ".htm" || System.IO.Path.GetExtension(e.Node.Name) == ".html")

    {

    answer = MessageBox.Show("Are you shure you want to open this file?", "Open File", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

    if (answer == DialogResult.Yes)

    {

    //Create new tab

    RichTextBox rtb = new RichTextBox();

    //Rich Text Box properties:

    rtb.Dock = DockStyle.Fill;

    //end properties

    TabPage tab = new TabPage();

    tab.Text = "Page " + tabControl1.TabCount.ToString();

    tab.Controls.Add(rtb);

    tabControl1.TabPages.Add(tab);

    tabControl1.SelectedIndex

    //open file in new tab:

    //MessageBox.Show("works");

    System.IO.StreamReader txtReader;

    txtReader = new

    System.IO.StreamReader(e.Node.Name);

    rtb2.Text = txtReader.ReadToEnd();

    txtReader.Close();

    txtReader = null;

    rtb2.SelectionStart = 0;

    rtb2.SelectionLength = 0;

    }

    else if (answer == DialogResult.No)

    {

    return;

    }

    }

     

     

    So far all of that works but the open the file in new tab part, I highlighted it yellow. What I think the problem is, is that becuase when you create a new tab the control does not automatically select that tab it just made, becuase of this the application can't find rtb2(the richtextbox). How can I make it automatically select that new tab it made?

     

     

    Thanks,

    Programmer01

    Saturday, November 10, 2007 12:49 AM
  • nvm,

    I added this to the end of the creating a new tab:

    int selectI = tabControl1.Controls.Count;

    tabControl1.SelectedIndex = selectI - 1;

     

    now it will automatically select the tab that was jsut added.

    But I still have the problem with that code, this part:

    rtb2.Text = txtReader.ReadToEnd();

     

    has this runtime error:

    Object reference not set to an instance of an object.

     

    How can I fix this?

     

     

    Thanks,

    Programmer01

    Monday, November 12, 2007 7:16 PM
  • Hi

     

    That error happens when you have an object that has not been set to an instance. 

     

    Basically it happens if you do this for example.

     

    TextBox txt;  

    txt.Text = "Hello World";

     

    when it should have been

     

    TextBox txt = new TextBox();

    txt.Text = "Hello World";

     

    You can think of it like this, the first call creates the memory to store a TextBox but doesn't actually store a TextBox so when you try to use it you get the Object reference not set to an instance of an object error. The second example creates the memory and then fills the memory with an instance of a TextBox so that when you use it there is an actual TextBox there.

     

    Why this is happening in your code is because either rtb2 or txtReader hasn't been created correctly, the memory is there but it hasn't been initialised correctly.

     

    Monday, November 12, 2007 9:20 PM
  • ah I see now.

    Odd becuase I copied this code from my OpenFile event and edited a few parts to make it look at the node name instead of open file dialog, and it did not have a problem. Here is where it might be:

    txtReader = new

     

    that line might be the problem, it is just like it is in my openfile event though, and when I try and do this:

    txtReader = new txtReader();

     

    or this:

    txtReader txt = new txtReader();

     

    I just get errors...

    Actually I am looking over it and comparing it to the open file event right now and I dont think it is either of those you suggested. Here is that is making txtReader:

    Code Block

    System.IO.StreamReader txtReader;

    txtReader = new

     

     

    Could it have anything to do with this:

    System.IO.StreamReader(e.Node.Name);

    ?

    That last line of code looks like this with open file dialog:

    Code Block
    System.IO.
    StreamReader(openFileDialog.FileName);

     

     

     

    Becuase even if I do it this way:

    Code Block

    rtb2.LoadFile(e.Node.Name, RichTextBoxStreamType.PlainText);

     

     

    I get the same error, and that other way should work aslo...Mabee it has to do with e.Node.Name?

     

     

    Thanks,

    Programmer01

    Monday, November 12, 2007 10:43 PM
  • Hello,

    refering to my previous post about the problem might be the e.Node.Text, I think I ahve found the problem with it but dont see how to fix it. Here is what I did to figure it out:

    I created a string variable just before the System.IO stuff like this:

    Code Block

    string TheFileName = e.Node.Text;

    System.IO.StreamReader txtReader;

    txtReader = new

    System.IO.StreamReader(TheFileName);

    rtb2.Text = txtReader.ReadToEnd();

    txtReader.Close();

    txtReader = null;

    rtb2.SelectionStart = 0;

    rtb2.SelectionLength = 0;

     

     

    The variable is TheFileName and it is = to e.Node.Text. And then I replaced:

    Code Block
    System.IO.StreamReader(e.Node.Text);

    with:

    Code Block
    System.IO.StreamReader(TheFileName);

     

     

     

    Then to see if somehow this solves the problem I run the application and in the default node in the listbox called MyDocuments I open a .htm file called HtmlTest.htm. Once I click it, it makes the new tab and does the same thing as it did before, a runtime error, BUT this time this runtime error is different, and I think if we can fix this the problem will be solved, the error said that it could not find that file and it specified the path it was looking at. The odd thing is that the path lead to the bin\Debug folder in my project and then the HtmlTest.htm file name inside the Debug folder...I tried this on other html files and the same thing happened. So it looks like it is doing the thing it should but for some reason it is looking in the wrong path...

     

     

    How can this be fixed?

     

     

    Thanks,

    Programmer01

    Tuesday, November 13, 2007 11:40 PM
  • Ok I solved the problem:

     

    I created this method:

    Code Block

    private void LoadFile1(string filename)

    {

    using (StreamReader thereader = File.OpenText(filename))

    {

    RichTextBox rtb2 = new RichTextBox();

    rtb2 = tabControl1.SelectedTab.Controls[0] as RichTextBox;

    rtb2.Text = thereader.ReadToEnd();

    thereader.Close();

    }

    }

     

     

     

    and instead of all that other stuff in afterselect I changed it to this:

    Code Block

    _filename = tbSelectedFile1.Text;

    LoadFile1(_filename);

     

     

     

    Now it works.

     

    Thanks for the help,

    Programmer01

    Saturday, November 17, 2007 8:31 PM