Thursday, March 22, 2012

Strange Listbox Behavior when using a Datasource

I have 2 Listboxes placed nexto each other with buttons bellow them to Add, Add All, Remove, Remove All.

Both list boxes use a select query from a SQL 2005 table to get their values.

Thats about up to where things behave normally. If I click Add, it should remove the selected item from listbox1 and add it to listbox2. It adds the item to listbox2, but doesnt remove it from listbox1. If however, I click remove on the newly added item, it will remove that item and place it back in listbox1, except there are now two entries in listbox1 with the same name/value. The add all and remove all buttons cause the browser to "get stuck", im assuming because it gets stuck in a endless loop because it can never remove the values from listbox1.

Any help would be GREATLY appreciated, this has been driving me up the wall for the better part of the day.

Some code snippets:

<asp:ListBox ID="AvailableManufacturers" runat="server" DataSourceID="SqlDataSource1" DataTextField="manufacturerName"
DataValueField="manufacturerID" SelectionMode="Multiple"></asp:ListBox>
<asp:ListBox ID="CurrentManufacturers" runat="server" DataSourceID="SqlDataSource2" DataTextField="manufacturerName"
DataValueField="manufacturerID" SelectionMode="Multiple"></asp:ListBox>

<asp:Button ID="RemoveAll" runat="server" Text="<<" OnClick="RemoveAll_Click"/>
<asp:Button ID="Remove" runat="server" Text="<" OnClick="Remove_Click"/>

<asp:Button ID="Add" runat="server" OnClick="Add_Click" Text=">"/>
<asp:Button ID="AddAll" runat="server" OnClick="AddAll_Click" Text=">>"/><br />
<br />
<asp:Button ID="Update" runat="server" BackColor="#FFFFC0" BorderColor="Maroon"
BorderStyle='Ridge' ForeColor='Red' Text='Update' />
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:connectionString1 %>"
SelectCommand="SELECT manufacturerID, manufacturerName FROM manufacturers WHERE (manufacturerID NOT IN (SELECT DISTINCT manufacturerID FROM dealerManufacturers WHERE (dealerUsername = @dotnet.itags.org.dealerUsername)))">
<SelectParameters>
</SelectParameters>
</asp:SqlDataSource>
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:connectionString1 %>"
SelectCommand="SELECT manufacturerID, manufacturerName FROM manufacturers WHERE (manufacturerID IN (SELECT DISTINCT manufacturerID FROM dealerManufacturers WHERE (dealerUsername = @dotnet.itags.org.dealerUsername)))">
<SelectParameters>
</SelectParameters>
</asp:SqlDataSource>

protected void Page_Load(object sender, EventArgs e)
{
SqlDataSource1.SelectParameters.Add("dealerUsername", TypeCode.String, User.Identity.Name);
SqlDataSource2.SelectParameters.Add("dealerUsername", TypeCode.String, User.Identity.Name);

}

protected void Add_Click(Object Src, EventArgs E)
{

if (AvailableManufacturers.SelectedIndex != -1)
{

CurrentManufacturers.Items.Add(new ListItem(AvailableManufacturers.SelectedItem.Text));
AvailableManufacturers.Items.Remove(AvailableManufacturers.SelectedItem.Text);
}
}

protected void AddAll_Click(Object Src, EventArgs E)
{

while (AvailableManufacturers.Items.Count != 0)
{

CurrentManufacturers.Items.Add(new ListItem(AvailableManufacturers.Items[0].Text));
AvailableManufacturers.Items.Remove(AvailableManufacturers.Items[0].Text);
}
}

protected void Remove_Click(Object Src, EventArgs E)
{

if (CurrentManufacturers.SelectedIndex != -1)
{

AvailableManufacturers.Items.Add(new ListItem(CurrentManufacturers.SelectedItem.Text));
CurrentManufacturers.Items.Remove(CurrentManufacturers.SelectedItem.Text);
}
}

protected void RemoveAll_Click(Object Src, EventArgs E)
{

while (CurrentManufacturers.Items.Count != 0)
{

AvailableManufacturers.Items.Add(new ListItem(CurrentManufacturers.Items[0].Text));
CurrentManufacturers.Items.Remove(CurrentManufacturers.Items[0].Text);
}
}

You have to iterate in reverse order (bottom to top) through all the selected items of the first listbox and add them to the second listbox and then remove them from the first listbox. Doing in reverse order is very important since you have to remove them based on an index. If you do it in a normal order, then after you remove the first item (let's say index 3) then all the indexes change and you might get an index outside of the boundaries exception.

Having that said you will implement the add button this way:

protected void Add_Click(object sender, EventArgs e) {for (int i = AvailableManufacturers.Items.Count - 1; i >= 0; i--) {if (AvailableManufacturers.Items[i].Selected)//if this item was selected { CurrentManufacturers.Items.Add(AvailableManufacturers.Items[i].Text); AvailableManufacturers.Items.RemoveAt(i); } } }
The same thing applies for the remove, but with the opposite listboxes and for the remove all and add all you do the same thing, but doin't check for selected.

..and if that drives you nuts, there are components likeEasyListBox and Metabuilders'DualList that will do all this for you :)

0 comments:

Post a Comment