|
Hi everybody.
I have exactly the same problem. According to K. Scott Allen, it seems you're right : the original DDL losts his validation data group.
The solution can be find here :
simply add Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value); in the foreach loop in RenderContents method and it works.
[...]
List<string> renderedOptionGroups = new List<string>();
foreach (ListItem item in list.Items)
{
Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
if (item.Attributes["OptionGroup"] == null)
{
RenderListItem(item, writer);
[...]
Hope this helps.
Thxman
-- modified at 9:26 Monday 25th September, 2006
By the way, thanks to K. Scott Allen for his articles
|
|
|
|
|
Thanks for that Thxman... I'll update the code download shortly.
|
|
|
|
|
By the way, I have noticed another thing :
when you look at the html source, you can see that the 'optiongroup'
attribute and his value is propagated to all 'option' tags.
Since the 'option' tag hasn't such attribute, why do youattach it ?
So I removed those attributes by commenting the foreach loop in the RenderListItem method :
[...]
if (item.Selected)
{
writer.WriteAttribute("selected", "selected", false);
}
writer.Write(HtmlTextWriter.TagRightChar);
HttpUtility.HtmlEncode(item.Text, writer);
[...].
It works as well and the page is validated by Tidy.
Last thing : in the browserfile, you can replace refID="IE6to9" by refID="Default"
to make this nice control works with all browsers
(I'm only using Firefox on my windows and linux boxes, noone's perfect !! ).
Anyway, thanks lotuspro for the original code.
It's really nicer when using long list of choices.
Thxman
PS: just too say I'm french, so excuse my english ...
|
|
|
|
|
commenting "foreach ..." statements will cause error when you want to set list item's css style,such as background-color,all style will not work.
|
|
|
|
|
Do this instead:
<code>
foreach (string key in item.Attributes.Keys)
{
if(key != "OptionGroup")
writer.WriteAttribute(key, item.Attributes[key]);
}
</code>
This will ensure the rendered HTML is correct. If the end user (programmer) adds his custom attributes they will be rendered, but that's his 'problem', the adapter is clear.
Regards,
Pawel
|
|
|
|
|
When I first populate my dropdownlist (using a BindDLL() method in a "if (!IsPostBack)" test in the Page_Load), I have the nice optgroup structure.
So the first time the page loads, everything's fine.
I added a button to trigger a PostBack and write the SelectedItem.Text from the DDL in a Label.
It does write the SelectedItem.Text in the Label after PostBack BUT I lose the neat optgroup structure : all items are left one after another like in a "regular" DDL
The selected item in the DDL remains correct though.
Is this a known "bug" and is there a workaround or am I missing something here?
Thanks again,
Jerome
|
|
|
|
|
Are you sure you're setting the "OptionGroup" attribute when you rebuild the DropDownList on the postback?
|
|
|
|
|
I am currently rebinding the DDL after each PostBack (in order to rebuild correctly the optgroup structure) but I wish I did not have to !
With a regular DDL, I only have to populate/bind it once in the if(!IsPostback) test and the viewstate would save everything in it after every postback.
Here apparently, the ViewState does not keep the OptionGroup attribute of the control adapter.
A colleague came up with that conclusion.
Here is an excerpt of my code at the moment where I need to "cheat" :
protected void Page_PreRender(object sender,System.EventArgs args)
{
BindDDL();
}
protected void BindDDL()
{
ddlDomInter.Items.Clear();
foreach (CatComp cc in tousLesCatComp)
{
DomInterCollection lesDI = (DomInterCollection)DomInter.GetCollection(ctx, typeof(DomInter), "CATCOMP_ID=" + cc.Id.ToString());
lesDI.Sort = "DOMINTER_LIB";
foreach (DomInter di in lesDI)
{
currentItem = new ListItem(di.Libelle, di.Id.ToString());
currentItem.Attributes["OptionGroup"] = di.CatComp.Libelle;
ddlDomInter.Items.Add(currentItem);
}
}
}
My colleague googled and found what seems to be a known issue and its workaround.
http://aspnet.4guysfromrolla.com/articles/110205-1.aspx[^]
"...override the SaveViewState() and LoadViewState(object) methods in the extended ListControl class".
Your article is very recent which might explain why ppl haven't reported any problems yet.
On the other hand you seem like you haven't had that problem yourself
If you have a working example other than the attached zip file, I'm interested
Thanks again,
Jerome (jerome[at]oudoul[dot]com)
|
|
|
|
|
Aah, I understand the problem now. The problem is actually a flaw in the ListItem control. ListItem attributes are actually never rendered. I am not sure if this behaviour is just a simple omission or by design. The problem affects all controls using ListItem such as DropDownList, RadioButtonList, CheckBoxList.
I haven't encounteted this problem myself thus far because I don't use viewstate wherever possible. I (and I think users also) much prefer something that can bookmarked.
|
|
|
|
|
Thanks Jerome,
I looked at that article and created a DDL which overrides those two methods as outlined.
Now the viewstate problems are sorted and everything works.
Cheers
Mick
namespace overloadedDDL
{
public class overDDL : DropDownList
{
protected override object SaveViewState()
{
// Create an object array with one element for the CheckBoxList's
// ViewState contents, and one element for each ListItem in skmCheckBoxList
object[] state = new object[this.Items.Count + 1];
object baseState = base.SaveViewState();
state[0] = baseState;
// Now, see if we even need to save the view state
bool itemHasAttributes = false;
for (int i = 0; i < this.Items.Count; i++)
{
if (this.Items[i].Attributes.Count > 0)
{
itemHasAttributes = true;
// Create an array of the item's Attribute's keys and values
object[] attribKV = new object[this.Items[i].Attributes.Count * 2];
int k = 0;
foreach (string key in this.Items[i].Attributes.Keys)
{
attribKV[k++] = key;
attribKV[k++] = this.Items[i].Attributes[key];
}
state[i + 1] = attribKV;
}
}
// return either baseState or state, depending on whether or not
// any ListItems had attributes
if (itemHasAttributes)
return state;
else
return baseState;
}
protected override void LoadViewState(object savedState)
{
if (savedState == null) return;
// see if savedState is an object or object array
if (savedState is object[])
{
// we have an array of items with attributes
object[] state = (object[])savedState;
base.LoadViewState(state[0]); // load the base state
for (int i = 1; i < state.Length; i++)
{
if (state[i] != null)
{
// Load back in the attributes
object[] attribKV = (object[])state[i];
for (int k = 0; k < attribKV.Length; k += 2)
this.Items[i - 1].Attributes.Add(attribKV[k].ToString(), attribKV[k + 1].ToString());
}
}
}
else
// we have just the base state
base.LoadViewState(savedState);
}
}
}
|
|
|
|
|
I tried the code above within the DropDownListAdapter code. I made a few changes to work with the ControlAdapter class. I had to insert the following line to get access to the DropDownlist:
DropDownList list = this.Control as DropDownList;
My problem: base.SaveAdapterViewState() returns null, thus the LoadAdapterViewState methode doesn't work right.
base.LoadAdapterViewState(state[0]); // load the base state
no state is loaded because state[0] is null
DropDownList list = this.Control as DropDownList;
this.Control returns a DropDownList with no items.
What's wrong with the code? Why does "base.SaveAdapterViewState()" return null?
This is the code I used:
protected override object SaveAdapterViewState()
{
// Create an object array with one element for the CheckBoxList's
// ViewState contents, and one element for each ListItem in skmCheckBoxList
object baseState = base.SaveAdapterViewState();
DropDownList list = this.Control as DropDownList;
object[] state = new object[list.Items.Count + 1];
state[0] = baseState;
// Now, see if we even need to save the view state
bool itemHasAttributes = false;
for (int i = 0; i < list.Items.Count; i++)
{
if (list.Items[i].Attributes.Count > 0)
{
itemHasAttributes = true;
// Create an array of the item's Attribute's keys and values
object[] attribKV = new object[list.Items[i].Attributes.Count * 2];
int k = 0;
foreach (string key in list.Items[i].Attributes.Keys)
{
attribKV[k++] = key;
attribKV[k++] = list.Items[i].Attributes[key];
}
state[i + 1] = attribKV;
}
}
// return either baseState or state, depending on whether or not
// any ListItems had attributes
if (itemHasAttributes)
return state;
else
return baseState;
}
protected override void LoadAdapterViewState(object savedState)
{
if (savedState == null) return;
// see if savedState is an object or object array
if (savedState is object[])
{
// we have an array of items with attributes
object[] state = (object[])savedState;
base.LoadAdapterViewState(state[0]); // load the base state
DropDownList list = this.Control as DropDownList;
for (int i = 1; i < state.Length; i++)
{
if (state[i] != null)
{
// Load back in the attributes
object[] attribKV = (object[])state[i];
for (int k = 0; k < attribKV.Length; k += 2)
list.Items[i - 1].Attributes.Add(attribKV[k].ToString(), attribKV[k + 1].ToString());
}
}
}
else
// we have just the base state
base.LoadAdapterViewState(savedState);
}
|
|
|
|
|
The problem is the null.
The problem is, that on LoadAdapterViewState no LIstItem are present in Item. So you must store your ViewState on LoadAdapterViewState in a field.
And on a place where the item exists you must restore you viewstate. This is likely OnPreRender()
Here is the "final" Class. It also removed the "OptionGroup" within the rendering and Renders slightly differently.
public class DropDownListAdapter : WebControlAdapter
{
private const string OptionGroupAttribute = "OptionGroup";
private const string TagOptionGroup = "optgroup";
private const string AttributeLabel = "label";
protected override void RenderContents(HtmlTextWriter writer)
{
DropDownList list = Control as DropDownList;
string currentOptionGroup;
List<string> renderedOptionGroups = new List<string>();
foreach (ListItem item in list.Items)
{
Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
if (item.Attributes[OptionGroupAttribute] == null)
{
RenderListItem(item, writer);
}
else
{
currentOptionGroup = item.Attributes[OptionGroupAttribute];
if (renderedOptionGroups.Contains(currentOptionGroup))
{
RenderListItem(item, writer);
}
else
{
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
RenderOptionGroupBeginTag(currentOptionGroup, writer);
renderedOptionGroups.Add(currentOptionGroup);
RenderListItem(item, writer);
}
}
}
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
}
private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
{
writer.AddAttribute(AttributeLabel, name);
writer.RenderBeginTag(TagOptionGroup);
}
private void RenderOptionGroupEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
private void RenderListItem(ListItem item, HtmlTextWriter writer)
{
foreach (string key in item.Attributes.Keys)
{
if (key != OptionGroupAttribute)
{
writer.AddAttribute(key, item.Attributes[key]);
}
}
writer.AddAttribute(HtmlTextWriterAttribute.Value,item.Value, true);
if (item.Selected)
{
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
}
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(item.Text);
writer.RenderEndTag();
}
protected override object SaveAdapterViewState()
{
DropDownList list = Control as DropDownList;
object[] viewState = new object[list.Items.Count + 1];
int i = 0;
foreach (ListItem item in list.Items)
{
viewState[i] = item.Attributes[OptionGroupAttribute];
i++;
}
viewState[i] = base.SaveAdapterViewState();
return viewState;
}
object[] viewStates;
protected override void LoadAdapterViewState(object state)
{
viewStates = (object[]) state;
base.LoadAdapterViewState(viewStates[viewStates.Length-1]);
}
protected override void OnPreRender(System.EventArgs e)
{
if (viewStates != null && viewStates.Length>1)
{
DropDownList list = Control as DropDownList;
if (Page.EnableEventValidation)
{
if (viewStates.Length != list.Items.Count+1)
{
throw new ViewStateException();
}
}
int max = viewStates.Length - 1;
if (list.Items.Count < max)
{
max = list.Items.Count;
}
for (int i = 0; i < max; i++)
{
list.Items[i].Attributes[OptionGroupAttribute] = (string)viewStates[i];
}
}
base.OnPreRender(e);
}
}
Regards
Der Albert
|
|
|
|
|
This worked fine for us for a while, but when we changed the option groups in a postback, the changes were lost, because they are being overwritten in the OnPreRender method.
Restoring the option groups from view state should take place in the LoadAdapterViewState method, but after the base.LoadAdapterViewState has run, which is when the items are loaded into the dropdown. This way changes can be made to the option groups after the view state is restored, with no risk of them being overwritten.
Modified code as below:
public class DropDownListAdapter : WebControlAdapter
{
private const string OptionGroupAttribute = "OptionGroup";
private const string TagOptionGroup = "optgroup";
private const string AttributeLabel = "label";
protected override void RenderContents(HtmlTextWriter writer)
{
DropDownList list = Control as DropDownList;
string currentOptionGroup;
List<string> renderedOptionGroups = new List<string>();
foreach (ListItem item in list.Items)
{
Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
if (item.Attributes[OptionGroupAttribute] == null)
{
RenderListItem(item, writer);
}
else
{
currentOptionGroup = item.Attributes[OptionGroupAttribute];
if (renderedOptionGroups.Contains(currentOptionGroup))
{
RenderListItem(item, writer);
}
else
{
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
RenderOptionGroupBeginTag(currentOptionGroup, writer);
renderedOptionGroups.Add(currentOptionGroup);
RenderListItem(item, writer);
}
}
}
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
}
private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
{
writer.AddAttribute(AttributeLabel, name);
writer.RenderBeginTag(TagOptionGroup);
}
private void RenderOptionGroupEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
private void RenderListItem(ListItem item, HtmlTextWriter writer)
{
foreach (string key in item.Attributes.Keys)
{
if (key != OptionGroupAttribute)
{
writer.AddAttribute(key, item.Attributes[key]);
}
}
writer.AddAttribute(HtmlTextWriterAttribute.Value,item.Value, true);
if (item.Selected)
{
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
}
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(item.Text);
writer.RenderEndTag();
}
protected override object SaveAdapterViewState()
{
DropDownList list = Control as DropDownList;
object[] viewState = new object[list.Items.Count + 1];
int i = 0;
foreach (ListItem item in list.Items)
{
viewState[i] = item.Attributes[OptionGroupAttribute];
i++;
}
viewState[i] = base.SaveAdapterViewState();
return viewState;
}
object[] viewStates;
protected override void LoadAdapterViewState(object state)
{
viewStates = (object[]) state;
base.LoadAdapterViewState(viewStates[viewStates.Length-1]);
if (viewStates != null && viewStates.Length>1)
{
DropDownList list = Control as DropDownList;
if (Page.EnableEventValidation)
{
if (viewStates.Length != list.Items.Count+1)
{
throw new ViewStateException();
}
}
int max = viewStates.Length - 1;
if (list.Items.Count < max)
{
max = list.Items.Count;
}
for (int i = 0; i < max; i++)
{
list.Items[i].Attributes[OptionGroupAttribute] = (string)viewStates[i];
}
}
}
}
|
|
|
|
|
First off great article...
I have tried the new code for the adapter but it keeps throwing the ViewStateException in the LoadAdapterViewState. The list.Items.Count is equal to 0 when I do a postback. What am I doing wrong? Does anyone else have this problem?
Thanks...
Jason
|
|
|
|
|
Jason is absolutely correct, list.Items are not available even after base.LoadAdapterViewState, so I wonder how did you manage this code to work.
I used the code posted by Albert Weinert above (with OnPreRender()), modified for the case you've described when ListItemCollection is changed on postback.
I restore OPTGROUP attributes only for the lists that are not changed on postback, for this, I extend viewStates array with one more entry, which keeps a hash code for list items, like this:
<br />
viewStates = new object[list.Items.Count + 2];<br />
<br />
int i = 0;<br />
foreach (ListItem item in list.Items)<br />
viewStates[i++] = item.Attributes[OPTIONGROUP_ATTRIBUTE];<br />
<br />
viewStates[i++] = base.SaveAdapterViewState();<br />
viewStates[i] = Hash(list.Items);<br />
And restore attributes only
<br />
if (Hash(list.Items) != (int)viewStates[viewStates.Length - 1])<br />
Note that hash code for ListItemCollection is a separate method, based on item values, simple ListItemCollection.GetHashCode() does not work when the list is re-filled with the same listItems:
<br />
private static int Hash(ListItemCollection listItems)<br />
{<br />
int hash = 0;<br />
foreach (ListItem listItem in listItems)<br />
hash += listItem.GetHashCode();<br />
<br />
return hash;<br />
}<br />
|
|
|
|
|
In fact I have taken a slightly different approach - I have subclassed the DropDownList and built it into a DLL. Perhaps the code doesn't then work when ported back to an adapter style solution.
The exact code I use is here:
[ToolboxData("<{0}:GroupDropDownList runat=server></{0}:GroupDropDownList>")]
public class GroupDropDownList : DropDownList
{
private const string OptionGroupAttribute = "OptionGroup";
private const string TagOptionGroup = "optgroup";
private const string AttributeLabel = "label";
protected override void RenderContents(HtmlTextWriter writer)
{
string currentOptionGroup;
List<string> renderedOptionGroups = new List<string>();
foreach (ListItem item in this.Items)
{
Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
if (item.Attributes[OptionGroupAttribute] == null)
{
RenderListItem(item, writer);
}
else
{
currentOptionGroup = item.Attributes[OptionGroupAttribute];
if (renderedOptionGroups.Contains(currentOptionGroup))
{
RenderListItem(item, writer);
}
else
{
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
RenderOptionGroupBeginTag(currentOptionGroup, writer);
renderedOptionGroups.Add(currentOptionGroup);
RenderListItem(item, writer);
}
}
}
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
}
protected void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
{
writer.AddAttribute(AttributeLabel, name);
writer.RenderBeginTag(TagOptionGroup);
}
protected void RenderOptionGroupEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
protected void RenderListItem(ListItem item, HtmlTextWriter writer)
{
foreach (string key in item.Attributes.Keys)
{
if (key != OptionGroupAttribute)
{
writer.AddAttribute(key, item.Attributes[key]);
}
}
writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, true);
if (item.Selected)
{
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
}
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(item.Text);
writer.RenderEndTag();
}
protected override object SaveViewState()
{
object[] viewState = new object[this.Items.Count + 1];
int i = 0;
foreach (ListItem item in this.Items)
{
viewState[i] = item.Attributes[OptionGroupAttribute];
i++;
}
viewState[i] = base.SaveViewState();
return viewState;
}
protected object[] viewStates;
protected override void LoadViewState(object state)
{
viewStates = (object[])state;
if (viewStates != null && viewStates.Length != 0)
{
if (Page.EnableEventValidation)
{
if (viewStates.Length != this.Items.Count + 1)
{
}
}
int max = viewStates.Length - 1;
base.LoadViewState(viewStates[max]);
if (this.Items.Count < max)
{
max = this.Items.Count;
}
for (int i = 0; i < max; i++)
{
this.Items[i].Attributes[OptionGroupAttribute] = (string)viewStates[i];
}
}
}
}
you can see it in action at http://eu.levi.com/storefinder/[^]
|
|
|
|
|
Good Code, 5/5 from my side
Thanks
Ashish
|
|
|
|
|
Hi, could you please post the code after you have made the changes to add Hash.
|
|
|
|
|
I know this is a few years after the original posts, but for anyone who also happens upon this thread, here is the combined code from Albert Weinert and Illya Kozachenko.
Using this I was able to have my main drop-down keep the OptGroups when it changed selection. And when another function fired that subsequently caused the main drop-down to re-populate, it displays the correct OptGroups based on the new bindings (which is what Illya's hash code provided).
Hope it helps.
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.Adapters;
using System.Collections.Generic;
public class DropDownListAdapter : WebControlAdapter
{
private const string OptionGroupAttribute = "OptionGroup";
private const string TagOptionGroup = "optgroup";
private const string AttributeLabel = "label";
protected override void RenderContents(HtmlTextWriter writer)
{
DropDownList list = Control as DropDownList;
string currentOptionGroup;
List<string> renderedOptionGroups = new List<string>();
foreach (ListItem item in list.Items)
{
Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
if (item.Attributes[OptionGroupAttribute] == null)
{
RenderListItem(item, writer);
}
else
{
currentOptionGroup = item.Attributes[OptionGroupAttribute];
if (renderedOptionGroups.Contains(currentOptionGroup))
{
RenderListItem(item, writer);
}
else
{
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
RenderOptionGroupBeginTag(currentOptionGroup, writer);
renderedOptionGroups.Add(currentOptionGroup);
RenderListItem(item, writer);
}
}
}
if (renderedOptionGroups.Count > 0)
{
RenderOptionGroupEndTag(writer);
}
}
private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
{
writer.AddAttribute(AttributeLabel, name);
writer.RenderBeginTag(TagOptionGroup);
}
private void RenderOptionGroupEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
private void RenderListItem(ListItem item, HtmlTextWriter writer)
{
foreach (string key in item.Attributes.Keys)
{
if (key != OptionGroupAttribute)
{
writer.AddAttribute(key, item.Attributes[key]);
}
}
writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, true);
if (item.Selected)
{
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
}
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(item.Text);
writer.RenderEndTag();
}
protected override object SaveAdapterViewState()
{
DropDownList list = Control as DropDownList;
object[] viewStates = new object[list.Items.Count + 2];
int i = 0;
foreach (ListItem item in list.Items)
viewStates[i++] = item.Attributes[OptionGroupAttribute];
viewStates[i++] = base.SaveAdapterViewState();
viewStates[i] = Hash(list.Items);
return viewStates;
}
private static int Hash(ListItemCollection listItems)
{
int hash = 0;
foreach (ListItem listItem in listItems)
hash += listItem.GetHashCode();
return hash;
}
object[] viewStates;
protected override void LoadAdapterViewState(object state)
{
viewStates = (object[])state;
base.LoadAdapterViewState(viewStates[viewStates.Length - 1]);
}
protected override void OnPreRender(System.EventArgs e)
{
if (viewStates != null && viewStates.Length > 1)
{
DropDownList list = Control as DropDownList;
if (Page.EnableEventValidation)
{
if (viewStates.Length != list.Items.Count + 1)
{
throw new ViewStateException();
}
}
if (Hash(list.Items) == (int)viewStates[viewStates.Length - 1])
{
int max = viewStates.Length - 2;
if (list.Items.Count < max)
{
max = list.Items.Count;
}
for (int i = 0; i < max; i++)
{
list.Items[i].Attributes[OptionGroupAttribute] = (string)viewStates[i];
}
}
}
base.OnPreRender(e);
}
}
|
|
|
|
|
Awesome! Worked flawlessly. Thank you so much for you contribution. I appreciate it!
|
|
|
|
|
Has anyone gotten this to work completely? I am using the above code from Albert Weinert.
I am using the DDL in a Wizard step, but when I move to the next step and go back, then the OptGroups are forgotten.
I have determined the following:
When moving to the next step, DDL viewstate is loaded (LoadAdapterViewState), but OnPreRender is never called and hence OptGroup attribute not set on list items. So when viewstate is saved (SaveAdapterViewState), there are no OptGroups available on the list items. And then when I return to the same step again, viewstate (optgroup) is empty...
I have made some code in SaveAdapterto re-use the previously loaded viewstate when I 'determine' that no optgroups are set on the list items, but it seems silly and perhaps dangerous...
Help, anyone?
|
|
|
|
|
None of above examples worked for me.
Instead of adding an attribute, I use enabled property to determine if the list item is an optgroup or not.
It is the easies way, I guess. This solution is NOT good for you if you need to populate enabled property for your DropDowns.
I hope that helps!
The code is as follows:
public class DropDownListAdapter : WebControlAdapter
{
const string AttributeLabel = "label";
const string TagOptionGroup = "optgroup";
private DropDownList ctl = null;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ctl = base.Control as DropDownList;
}
protected override void RenderContents(HtmlTextWriter writer)
{
if (ctl == null)
return;
List<string> renderedOptionGroups = new List<string>();
foreach(ListItem item in ctl.Items)
{
if (item.Enabled)
RenderListItem(item, writer);
else
{
if (renderedOptionGroups.Contains(item.Text))
RenderListItem(item, writer);
else
{
if (renderedOptionGroups.Count > 0)
RenderOptionGroupEndTag(writer);
RenderOptionGroupBeginTag(item.Text, writer);
renderedOptionGroups.Add(item.Text);
}
}
}
if (renderedOptionGroups.Count > 0)
RenderOptionGroupEndTag(writer);
}
private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
{
writer.AddAttribute(AttributeLabel, name);
writer.RenderBeginTag(TagOptionGroup);
}
private void RenderOptionGroupEndTag(HtmlTextWriter writer)
{
writer.RenderEndTag();
}
private void RenderListItem(ListItem item, HtmlTextWriter writer)
{
if (Page != null)
Page.ClientScript.RegisterForEventValidation(ctl.UniqueID, item.Value);
writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, true);
if (item.Selected)
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(item.Text);
writer.RenderEndTag();
}
}
--
mrtoper
-- modified at 11:04 Thursday 8th March, 2007
|
|
|
|
|
Thanks mrtoper ...
It's working fine with me ...
----
Alaa Moustafa
|
|
|
|
|
Hi,
Thank you for your example but isn't there a typo in the zip file?
Shouldn't the subfolder Code be named App_Code ?
Even after renaming it like that and customizing the files, I can't make you example work.
Some more details about the installation and configuration of the files would be much appreciated
Thanks,
Jerome
|
|
|
|
|
Apologies, this is a slight oversight on my part. I am using Web Application Projects for VS2005. If you are unfamiliar with Web Application Projects, it is really just a patch for VS2005 to make it use the old VS2003 build model for websites. You can download the patch here: http://www.asp.net/sandbox/[^]. The reason I have no App_Code is because the old build model does not support it, so I use a 'Code' folder instead.
The most likely reason the sample is not working for you is because of the namespace declaration in the DropDownListAdapter.cs file. Just remove it and in addition, change the adapterType declaration in the BrowserFile from:
adapterType="CraigBlog.Net.Code.Adapters.DropDownListAdapter"
to:
adapterType="DropDownListAdapter"
It should then work fine. If not, create the files yourself and copy only the contents of the sample files over.
I hope that helps.
-- modified at 10:56 Wednesday 13th September, 2006
|
|
|
|
|