Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Loading unbound combo boxes on the fly

Status
Not open for further replies.

imterpsfan3

Programmer
Jul 22, 2005
160
US
I'm creating an unbound form that has 4 combo boxes that serve as look up tables for related data. I have a method in a class module in which I pass the combo box name and the stored procedure that will serve to load that combo box. I then use DictionaryEntry to load a key, value pair for each row into the combo box. This works fine.

The problem arises in synchronizing the combo boxes to a selectedindex value of the main combo box I use to search for existing records.

When I select an entry in the main combo box I want the other combo boxes to move to the related record in its own combo box and display to the user.

I really like the concept of unbound forms so I hope I can find a remedy for this. Most of the documentation/books I've seen only deal with combo boxes in a bound environment. Is there a better way than to load these combos with dictionaryentrys?

I can post existing code if needed. Thanks in advance.
 
- where have you put the code for updating the other combos? (which event handler?)

- To do the search thing, you:
- Select an item from the main combo?
or
- You need to type in it the search-for string?
 
Directly below is a method in a class module where I load the combo boxes
Code:
public void LoadGenericCombo(ComboBox cbo, string dataSource)
		{
			objCmd = new SqlCommand(dataSource,objConn);
			objReader = objCmd.ExecuteReader();

			while (objReader.Read())
			{
				cboEntry = new DictionaryEntry(objReader[0],objReader[1]);				
				cbo.Items.Add(cboEntry);
				
			}
			objReader.Close();
			objReader = null;
			objCmd = null;
		}
On Form load I open the connection and load the combos
Code:
private void frmDocumentReviews_Load(object sender, System.EventArgs e)
		{
			clsData z = new clsData();
			
			//Open the Connection to the database
			z.OpenConnection();
			//Load all the combo boxes, passing the control name and stored proc
			z.LoadGenericCombo(this.cboTeam_Leader,"proc_Select_TeamLeader");
			z.LoadGenericCombo(this.cboProject,"proc_Select_Projects");
			z.LoadGenericCombo(this.cboAuthor,"proc_Select_Authors");
			z.LoadGenericCombo(this.cboCompany,"proc_Select_Company");
			z.LoadGenericCombo(this.cboCM_Document_Type,"proc_Select_DocType");
			z.LoadGenericCombo(this.cboReview_Type,"proc_Select_ReviewType");
			z.LoadGenericCombo(this.cbo_Document_Search,"proc_DocSearch");
			
			z.CloseConnection();

			
			

		}
On the Selected Index Changed event of the main combo box, I call a method in the data class to load data on the form
Code:
		private void cbo_Document_Search_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			
			de = (DictionaryEntry)this.cbo_Document_Search.SelectedItem;
			cc = Convert.ToInt32(de.Key.ToString());
			
			review = clsData.GetReview(cc);
			if (review == null)
			{
				MessageBox.Show("Another user has deleted this review");
			}
			ShowReviewData();
		}
ShowReviewData is a method on the form that displays the data-- the mistake I have made is using the selectedindex value of the other combo boxes
Code:
		private void ShowReviewData()
		{
			
			this.txtDate_Completed.Text = review.dateReceived.ToShortDateString();
			this.txtDate_Received.Text = review.dateCompleted.ToShortDateString();
			this.txtTask_Number.Text = review.taskNumber;
			this.txtDocument_Title.Text = review.documentTitle;
			this.txtTRB_Due_Date.Text = review.trbDue;
			this.txtDocument_Cover_Date.Text = review.documentCover;
			this.txtPVCS_Workset.Text = review.pvcsWorkset;
			//this.txtNotes.Text = review.notes.ToString();
			this.txt_TSG.Text = review.tsgCDRL;
			this.cboProject.SelectedItem = review.projectID;
			
			//this.cboTeam_Leader.SelectedIndex = review.teamLeader;
			this.cboCM_Document_Type.SelectedIndex = review.cmDocument;
			this.cboCompany.SelectedIndex = review.companyID;
			//this.txtCM_Update_Date.Text = review.cmUpdate.ToShortDateString();
			this.cboReview_Type.SelectedIndex = review.reviewType;
			this.cboAuthor.SelectedIndex = review.authorID;
			this.txtVersion_Number.Text = review.versionNumber;
			this.txtNum_Pages.Text = review.numPages.ToString();
			this.txtReview_Time.Text = review.reviewTime.ToString("f1");
		}
 
Basically I am looking for a way to automate the loading of combo boxes with data in an unbound fashion. Then I want to be able to synchronize these combo boxes, which serve as lookup tables. I do not like the performance of using a bunch of data adapters- lots of resources and much slower than unbound forms.
 
I come from the vb world and I would map the related combo boxes on the combo boxes listindex property. There is no such property in .net.

I can easily synchronize all the combos in C# using bound data with the binding manager but I don't want to use that.
 
.NET has a SelectedIndex (Read/Write) property so that ....

(VB.NET example)


Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged

ComboBox2.SelectedIndex = ComboBox1.SelectedIndex

End Sub


Hope this helps.
 
I think I'm going to ditch the dictionaryentry method of filling the combos. I think I'll create a class with a constructor for the ID and Value, which will translate to the ValueMember and DisplayMember. I'll create a DataSource of an arraylist and populate the arraylist with the datareader.

Unless someone else has a more enlightening idea. I need to get to the selectedvalue of the other combo boxes based on the main combo box, NOT the selectedindex.

 
Suggestion:

Create a model class that holds all your dictionary entries. Let the comboboxes display the conent only - don't make them do work.

When an item in a combobox is selected, you should be asking the model class to change the selected data.

dictionaryClass.SelectItem(this.selectedValue);

Have your model class (dictionaryClass) throw an event which the other combo boxes subscribe to. This would keep the code small and simple.

The other possibility (which I have done in the past) is create a new UserControl that extends ComboBox. In that user control, create a public object Tag where you store the object related to that combo box entry.
 
I don't know how to create a DictionaryClass so I created a ComboBox class, which will work for now. I may be introduced to a better way to build this mousetrap. The class I created is as follows:

Code:
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;

namespace MyListControlExample
{
	/// <summary>
	/// Summary description for States.
	/// </summary>
	public class clsDataReader
	{
		private int _ID;
		private string myLongName;

		 public static SqlConnection objConn;
		 public static SqlCommand objCmd;
		 public static SqlDataReader objReader;
		
		public clsDataReader(string strLongName, int intID)
		{
			this._ID = intID;
			this.myLongName = strLongName;
		}
		public clsDataReader()
		{
		}
		public int ID
		{
			get
			{
				return _ID;
			}
		}
		public string LongName
		{
			get
			{
				return myLongName;
			}
		}
		public override string ToString()
		{
			return this.ID + " - " + this.LongName;
		}
		public static void LoadGenericCombo(ComboBox cbo, string dataSource)
		{
			objCmd = new SqlCommand(dataSource,objConn);
			objReader = objCmd.ExecuteReader(CommandBehavior.CloseConnection);
			
			ArrayList arlCombo = new ArrayList();
			cbo.BeginUpdate();
			while (objReader.Read())
			{
				arlCombo.Add(new clsDataReader(objReader[1].ToString(),Convert.ToInt32(objReader[0])));
				
			}
			
			cbo.EndUpdate();

			cbo.DataSource = arlCombo;
			cbo.DisplayMember = "LongName";
			cbo.ValueMember = "ID";

			
			arlCombo = null;
		}
		public static void OpenConnection()
		{
			string strConn = "Data Source = myServer;Initial Catalog = DocumentReviews;Integrated Security = SSPI;";
			
			objConn = new SqlConnection(strConn);
			objConn.Open();

		}
	}
}
 
JurkMonkey, if I could use your expert opinion. Or anyone else who wants to jump in on this.

I do not know how to create a dictionary class. Would this buy me anything in efficiency? Is there a good tutorial or code example someone can provide on creating a connection class.

Lastly, this is what I have come up with. It works, but I'm willing to admit there are better ways to do this. Tell me what is good or bad about this code.

I create a generic class to handle filling the combos. The method MultiCombos takes a parameter of string that serves as the name of the stored procedure to load all the combos. In this method I am calling a stored procedure (proc_MultiCombos) that is creating several select statements in one procedure, which would fill all the combos on the calling form. I am returning a dataset to the calling form.

Code:
public DataSet MultiCombos(string cmd)
		{
			string strConnection = "Data Source = xxxx;" +
				"Initial Catalog = DocumentReviews; Integrated Security = SSPI";
			objConn = new SqlConnection(strConnection);
			ds = new DataSet();
			try
			{
				objConn.Open();
				objAdapter = new SqlDataAdapter("proc_MultiCombos",objConn);
				objAdapter.Fill(ds);
				
				return ds;
				
			}
			catch(SqlException ex)
			{
				
			}
			finally
			{
				ds.Dispose();
				ds = null;
				objAdapter.Dispose();
				objAdapter = null;
				objConn.Dispose();	
				
				
			}
			return ds;
		}

On form load of the calling form, I run this code below. I create an array of combo boxes and iterate through the array, binding the appropriate data table from the passed dataset from the class module to the combo box in the loop.

Code:
private void FillCombos()
		{
			
			ComboBox[] cbo = new ComboBox[7];
			cbo[0] = this.cbo_Document_Search;
			cbo[1] = this.cboTeam_Leader;
			cbo[2] = this.cboProject;
			cbo[3] = this.cboAuthor;
			cbo[4] = this.cboCompany;
			cbo[5] = this.cboCM_Document_Type;
			cbo[6] = this.cboReview_Type;

			int z = cbo.Length;
			try
			{
				
				ds = cls.MultiCombos("proc_MultiCombos");
				
				
				

				for (int k = 0;k == cbo.Length;k++)
				{
					cbo[k].Visible = false;
				}
			
			
				/* Loop through the array of dropdown lists and bind the associated
				 * dataset table to the dropdown */
				for(int r = 0;r < z;r++)
				{
				//	cbo[r].BeginUpdate();
					cbo[r].DataSource = ds.Tables[r];
					cbo[r].ValueMember = ds.Tables[r].Columns[0].ToString();
					cbo[r].DisplayMember = ds.Tables[r].Columns[1].ToString();
					cbo[r].SelectedIndex = -1;
					//cbo[r].EndUpdate();
					//cbo[r].DataBind();
				}

				for (int k = 0;k < z;k++)
				{
					cbo[k].Visible = true;
				}
				
				
			}
			catch(Exception ex)
			{
				//Display error message to the user
				string msg = "Error Source: " + ex.Source + "\n" + ex.Message;				
				MessageBox.Show(msg,"Fill Combo Error",
					MessageBoxButtons.OK,MessageBoxIcon.Error);
				this.Close();
			}
			finally
			{
				//Cleanup code
				ds.Dispose();
				ds = null;	
				if (cls != null)
				{
										cls.CloseConnection();				
				}
				cbo  = null;				
				
			}	
			
		}

 
Maybe this thread thread796-1127589 will be helpful? It has to do with creating unbound combo boxes. Like you, I came from the VB6 world and don't like binding either.
 
Let the comboboxes display the conent only - don't make them do work.
If you create a helper class for your combo content, if you override the ToString() method, you can control what gets displayed, while still having access to the entire object when the user clicks on it.

Chip H.


____________________________________________________________________
Donate to Katrina relief:
If you want to get the best response to a question, please read FAQ222-2244 first
 
That is one creative solution you posted. Seems like a lot of work. I agree with Chip. A Helper Class would allow you to manage the content of the combos a bit better.

I also don't like dealing with bound items. I don't use datagrids for that exact reason.

Dictionary files are a lot of work. We use dictionary files for the lookup of how to parse medical images. Companies have put months of work into creating dictionary files.

Stick with your solution for now. You may find a design pattern that makes this problem a whole lot easier in the future.
 
So I am assuming if I don't have the combo boxes do any work and only display content, then the form will take less time to load and will take up less of a memory footprint? Loading combo boxes or drop down lists seems to be a very memory-intensive task. My way of getting around it is to load as little data into these controls as possible.

I have always designed the unbound forms in VB6 apps and found that method to be much more efficient than the data binding methodology. I have endeavored to do the same with .Net apps. But I've heard it said that as for binding data, this is not as much of an issue with ADO.NET, because it supports a disconnected architecture?

Now as far as a helper class, can you steer me in the right direction on this? I am not familiar with this concept. I did find something in the .NET documentation about building a combo box class where you are overriding the tostring method. Is this what you're talking about?



 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top