Hi
I am trying to link a selected item from one listbox to display all the results in another listbox by using a filter method (Or something similar).

Both listboxes are filled with information from a text file.
The first listbox has information such as the different Genres of a film, and a description of those genres (On the same row)
The other listbox has a range of films with different types of Genre.

What i am trying to do is, when i click an item in my first listbox, let's say 'Action' genre, the button 'View Films' is clicked, I want it to be able to process my second listbox, and filter all the films with the 'Action' Genre next to it, and to display all the films in that second listbox.

Can anybody help me?

I'm not sure what LINQ is as i am new to programming in general

I like using datatables and a rowfilter for this type of stuff.

Here is a simple example. I manually loaded the DataTables, but you would do this with your file contents. You could also do your text files as xml and load the table directly with the xml definition. Most of the code is just creating some data to play with.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private DataTable dtGenre = new DataTable();
        private DataTable dtMovies = new DataTable();

        private void Form1_Load(object sender, EventArgs e)
        {
            DataRow r = default(DataRow);

            // Build Movies Table

            dtMovies.Columns.Add("Genre", typeof(string));
            // add whatever columns you need
            dtMovies.Columns.Add("Title", typeof(string));
            dtMovies.Columns.Add("Description", typeof(string));
            dtMovies.Columns.Add("Rating", typeof(string));

            // just defining Genre and title for this example
            r = dtMovies.NewRow(); r[0] = "Action"; r[1] = "Title1"; dtMovies.Rows.Add(r);
            r = dtMovies.NewRow(); r[0] = "Action"; r[1] = "Title2"; dtMovies.Rows.Add(r);
            r = dtMovies.NewRow();r[0] = "Action"; r[1] = "Title3"; dtMovies.Rows.Add(r);
            r = dtMovies.NewRow(); r[0] = "Adventure"; r[1] = "Title4"; dtMovies.Rows.Add(r);
            r = dtMovies.NewRow(); r[0] = "Comedy"; r[1] = "Title5"; dtMovies.Rows.Add(r);
            r = dtMovies.NewRow(); r[0] = "Comedy"; r[1] = "Title6"; dtMovies.Rows.Add(r);

            dtMovies.DefaultView.RowFilter = null;

            // Build Genre Table
            dtGenre.Columns.Add("Genre", typeof(string));
            dtGenre.Columns.Add("Description", typeof(string));
            r = dtGenre.NewRow(); r[0] = "Action";
            r[1] = "Action: Usually include high energy, big-budget physical stunts and chases, possibly with rescues, battles, fights, escapes, destructive crises (floods, explosions, natural disasters, fires, etc.), non-stop motion, spectacular rhythm and pacing, and adventurous, often two-dimensional 'good-guy' heroes (or recently, heroines) battling 'bad guys' - all designed for pure audience escapism. Includes the James Bond 'fantasy' spy/espionage series, martial arts films, and so-called 'blaxploitation' films. A major sub-genre is the disaster film.";

            dtGenre.Rows.Add(r); r = dtGenre.NewRow();
            r[0] = "Adventure";     r[1] = "Adventure: Usually exciting stories, with new experiences or exotic locales, very similar to or often paired with the action film genre. They can include traditional swashbucklers, serialized films, and historical spectacles (similar to the epics film genre), searches or expeditions for lost continents, 'jungle' and 'desert' epics, treasure hunts, disaster films, or searches for the unknown.";
            dtGenre.Rows.Add(r);

            r = dtGenre.NewRow(); r[0] = "Comedy";
            r[1] = "Comedy: Light-hearted plots consistently and deliberately designed to amuse and provoke laughter (with one-liners, jokes, etc.) by exaggerating the situation, the language, action, relationships and characters. This section describes various forms of comedy through cinematic history, including slapstick, screwball, spoofs and parodies, romantic comedies, black comedy (dark satirical comedy), and more.";
            dtGenre.Rows.Add(r);

            // set datasources

            lbxMovies.DataSource = dtMovies.DefaultView;
            lbxMovies.DisplayMember = "Title";

            lbxGenre.DataSource = dtGenre;
            lbxGenre.DisplayMember = "Description";
            lbxGenre.ValueMember = "Genre";
            lbxGenre.HorizontalScrollbar = true;
        }

        private void lbxGenre_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbxGenre.SelectedIndex != -1 && dtMovies.Columns["Genre"] != null)
            {   // build the RowFilter based on selected Genre
                dtMovies.DefaultView.RowFilter = "[Genre]='" + lbxGenre.SelectedValue.ToString() + "'";
            }
        }
    }
}

Here's a simple way using a combobox, a listbox and a custom class. the file is in the format, Title,Genre on each line. Instead of using a button, this reacts instead to clicking an item in the combobox and changing the contents of the listbox.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WindowsFormsApplication6
{

    public partial class Form1 : Form
    {
        List<Movie> MovieList = new List<Movie>();
        public Form1()
        {
            InitializeComponent();
            foreach (string Line in File.ReadAllLines(@"..\..\TextFile1.txt"))
            {
                Movie AddMovie = new Movie();
                AddMovie.Title = Line.Split(',')[0];
                AddMovie.Genre = Line.Split(',')[1];
                MovieList.Add(AddMovie);
                if (!(comboBox1.Items.Contains(AddMovie.Genre)))
                    comboBox1.Items.Add(AddMovie.Genre);
            }
        }    
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            listBox2.Items.Clear();
            listBox2.Items.AddRange(new object[]{comboBox1.Items[comboBox1.SelectedIndex].ToString(), ""});

            List<Movie> ByGenre = MovieList.FindAll(
            delegate(Movie m)
            {
                return m.Genre == comboBox1.Items[comboBox1.SelectedIndex].ToString();
            });
            foreach(Movie NextMovie in ByGenre)
            {
                listBox2.Items.Add(NextMovie.Title);
            }
        }
    }
    public class Movie
    {
        public string Title { get; set; }
        public string Genre { get; set; }
    }
}

Edited 3 Years Ago by tinstaafl

Hello, although the combo box is a good idea, my design has to be a listbox with a button, that in turn searches through another listbox to filter data, i already have all the required classes for this.

Thank you for your help so far :)

Here's a way with the 2 listboxes and a button. Design the button with the enabled property set to false. When a selection is made in the first listbox, the button is enabled and the second one filled, then the button is disabled again.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WindowsFormsApplication6
{

    public partial class Form1 : Form
    {
        List<Movie> MovieList = new List<Movie>();
        public Form1()
        {
            InitializeComponent();
            foreach (string Line in File.ReadAllLines(@"..\..\TextFile1.txt"))
            {
                Movie AddMovie = new Movie();
                AddMovie.Title = Line.Split(',')[0];
                AddMovie.Genre = Line.Split(',')[1];
                MovieList.Add(AddMovie);
                if (!(listBox1.Items.Contains(AddMovie.Genre)))
                    listBox1.Items.Add(AddMovie.Genre);
            }
        }
        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            button1.Enabled = true;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            listBox2.Items.Clear();
            listBox2.Items.AddRange(new object[] { listBox1.Items[listBox1.SelectedIndex].ToString(), "" });

            List<Movie> ByGenre = MovieList.FindAll(
            delegate(Movie m)
            {
                return m.Genre == listBox1.Items[listBox1.SelectedIndex].ToString();
            });
            foreach (Movie NextMovie in ByGenre)
            {
                listBox2.Items.Add(NextMovie.Title);
            }
            button1.Enabled = false;
        }
    }
    public class Movie
    {
        public string Title { get; set; }
        public string Genre { get; set; }
    }
}

Hello, just reading your code -> I have developed a seperate .cs file for my Movie Class, would i need to change anything within your code at all?

I have a class for my Movies (Title/Genre/Description/Age Rating etc...) & a class for the Genres (First List box) -> Is the 'Public Class Movie' from line 52 based upon getting the information from the movie details textfile?

Lastly, with the lines 22-24, could you possibly explain what the 'Split' part means as i am unsure?

Thank You & Sorry, Still a learner on C# :P

Also, my "Title, Genre" is listed in my textfile as:

Title
Genre

Is there another way to split this data or?

is the 'Public Class Movie' from line 52 based upon getting the information from the movie details textfile?

Yes

Lastly, with the lines 22-24, could you possibly explain what the 'Split' part means as i am unsure?

That function splits a string according to a delimiter, a comma in this case, and puts the results into an array.

Here's a modification that should do the job:

        public Form1()
        {
            InitializeComponent();
            string[] Filelist = File.ReadAllLines(@"..\..\TextFile1.txt");
            for(int i=0;i<Filelist.Count();i+=2)
            {
                Movie AddMovie = new Movie();
                AddMovie.Title = Filelist[i];
                AddMovie.Genre = Filelist[i+1];
                MovieList.Add(AddMovie);
                if (!(listBox1.Items.Contains(AddMovie.Genre)))
                    listBox1.Items.Add(AddMovie.Genre);
            }
        }

Edited 3 Years Ago by tinstaafl

& Im guessing from Line 5 of that, the i+=2 can go up to whatever i wish, as it'll have Title, Genre, Description, Main Char etc, so it'll be i+=4, and the i+1, +2, +3 etc?

Currently writing it out & adapting it where necessary - Also wondering what the part from lines 3 up until the end of that if statement does, I am aware that it reads all the data and stores it, but not the part within the For-loop..

& with the IF statement, as my classes have separate varaible declarations for the word 'Genre', would i need to specify that anywhere? --> My 'Genre' class contains 'theGenre' & 'genreDescription', whereas my Movies class just has 'genre'

 string[] Filelist = File.ReadAllLines(@"..\..\TextFile1.txt");
      for(int i=0;i<Filelist.Count();i+=2)
      {
         Movie AddMovie = new Movie();
         AddMovie.Title = Filelist[i];
         AddMovie.Genre = Filelist[i+1];
         MovieList.Add(AddMovie);
         if (!(listBox1.Items.Contains(AddMovie.Genre)))
             listBox1.Items.Add(AddMovie.Genre);
      }

Thank You for helping me so far :)

Edited 3 Years Ago by natw888: Needed to Add more detail

Earlier in the code I declared a list of type Movie. Lines 4-7, declare a variable of type Movie, set it's properties then add it to the list. Lines 8 & 9 check if the Genre has already been added to the listbox, if not it adds it to the listbox.

If you want to add the genre to different places you'll have to change the code. In my example Filelist[i] is the movie title and Filelist[i+1] is the genre. Without seeing the specifics of your classes it's hard to suggest exactly how to declare them. But, if they are simple like my Movie class, you can declare them as lists and use the same pattern I did. Declare a temporary variable of the same type as the list, set the properties, then add it to the list.

Ok Thank you, my classes are very simple (pretty much exact to your classes)

I've used a StreamReader to input all my data (For both listboxes) so I don't think it would be necessary to check whether the specific genre has been added.

With lines 45-47, where you add the films into the 2nd listbox, would i separate each variable with a comma, or do i have to write a new line each time (See below)?

foreach (Movie NextMovie in ByGenre)
            {
                listBox2.Items.Add(NextMovie.Title, NextMovie.Description, NextMovie.Genre);
            }

Since you want to have different type of data for each line a listview would probably be much better.

If you have to use a listbox, and since you will be filtering by genre I would suggest Adding the Genre as the first item to use as a header then an empty line(listBox2.Items.AddRange(new object[] { listBox1.Items[listBox1.SelectedIndex].ToString(), "" });),

then each movie tite, description, and an empty line(listBox2.Items.AddRange(new object[] {NextMovie.Title + " - " + NextMovie.Description, ""})) the extra empty line should increase the readability.

Thank you, so the code above should show something like this? (Just working on other aspects of my form first :/)

Genre

Movie Title - Movie Description - Main Character

Hi again,

With this piece of code, as i've used a streamreader to pull both text files (One for genre with a list of details etc) and one for movies, I've also used arraylists called allGenres (To store genre & other details relating to that) & an arraylist called allMovies to store data relating to the movie itself..

How would i go abouts amending this button code so that it would follow the same purpose, as i do not see a necessity to create a List<Movie> due to my arraylist?

        private void button1_Click(object sender, EventArgs e)
        {
            listBox2.Items.Clear();
            listBox2.Items.AddRange(new object[] { listBox1.Items[listBox1.SelectedIndex].ToString(), "" });
            List<Movie> ByGenre = MovieList.FindAll(
            delegate(Movie m)
            {
                return m.Genre == listBox1.Items[listBox1.SelectedIndex].ToString();
            });
            foreach (Movie NextMovie in ByGenre)
            {
                listBox2.Items.Add(NextMovie.Title);
            }
            button1.Enabled = false;
        }
    }

Currently, all the genres show up in listbox1 and all the movies (Every movie regardless of Genre) show up in listbox2 on form load (As expected), i've added this code to my button so that a genre must be selected in listbox1 in order for the button to perform an action:

private void button1_Click(object sender, EventArgs e)
{
    if (genreSelected == -1)
     Messagebox.Show("Error - Please select a Genre")
     else
     {
      listbox2.Items.Clear();
      etc.....

This has gone way beyond your original question. I think you might need a little restructuring of your data. It might sound like a lot of work, but it will make things much easier in the end and simplify your code as well.

I would suggest using nested classes this. something like this:

Public Class Movie
{
    public string Title{get; set;}
    public string Description{get; set;}
    public string Character{get; set;}
}
Public Class Genre
{
    public string Name{get; set;}
    public string Description{get; set;}
    public List<Movie> Movies{get; set;}
}

I used the List<> instead of the Arraylist, because the arraylist is very generic and in order to access specific properties of an item you have to cast the item in the arraylist as that type. The List<> on the other hand is type specific so that each member is of that type and you can access it's properties directly.

With a class structure like this, you declare a List<Genre>. with your streamreader you add the genres to the list, then add each movie to the list of the genre it belongs to. If you want you can even have a movie that belongs to 2 or more genres, just add it to their lists.

Now when your genre list is complete it will contain all the genres and the movies for each genre already organized.

Displaying is simply a matter of looping through the list of genres to display their names, then when one is selected just loop through it's list of movies and display them.

Using 2 lists like you have makes your code very complicated to coordinate the lists.

As for controlling the button, I would suggest starting with the button disabled. In the SelectedIndexChanged event of the listbox set the enabled property to true, then in the button click event just before it exits set enabled false again. This is a simpler way to make sure a selection is made before the button can be pressed.

Edited 3 Years Ago by tinstaafl

Ok, as for the code below, I am still unsure how it would focus on the 'Genre' name itself, as there are multiple variables in listbox1?

listBox2.Items.AddRange(new object[] { listBox1.Items[listBox1.SelectedIndex].ToString(), "" });

are you using a format like this:
Genre - Description

If so something like this would work, string Genre = listBox1.Item[listBox1.SelectedIndex].ToString().Split('-')[0].Trim();

My textfile format is like this:

Genre
Description

So, as for the code above, I'd be putting the below? since Genre is declared as a variable, it can replace the part inside { }?

listBox2.Items.AddRange(new object[] { Genre, "" });

Edited 3 Years Ago by natw888: change

yes that's right. but if the genre name is on a separate line then, you will probably have to put code in there to make sure that the user isn't clicking the description, instead of the name. Perhaps checking if the selectedindex is odd or even.

This article has been dead for over six months. Start a new discussion instead.