|
Hi
I am getting an error for any page that uses a ddl unless i put <pages enableEventValidation="false"> in my web.config file. Please could you advise how i can use the adapter without turning this security feature off?
Thanks
<div class="ForumMod">modified on Thursday, November 13, 2008 5:25 AM</div>
|
|
|
|
|
Hello, You example is great and just what I want. But if you use your dropdown box in a usercontrol and add it to a page with Ajax wrapped around it. it fires off an error due to I think the HttpModules. Any idea how to get around this ?
David
-- modified at 5:47 Wednesday 21st March, 2007
|
|
|
|
|
I'm working with VS2005 but when I Build My Web Site it always shows this error. What should I do?
Thanks!
|
|
|
|
|
|
Try renaming the code folder to App_Code, that's what worked for me.
|
|
|
|
|
I had the same error come up when I tried it. I found the problem to be in the Page Directive of the HTML page. The following line:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DropDownListOptionGroup.aspx.cs" Inherits="CraigBlog.Net.DropDownListOptionGroup" %>
Should be changed to:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DropDownListOptionGroup.aspx.cs" Inherits="CraigBlog.Net.TestDropDownList" %>
Specifically, the Inherits attribute is given the wrong value. The page file name may be "DropDownListOptionGroup" but the partial class name is "TestDropDownList". Making that change, all was right in the world once more.
-- Andrew
|
|
|
|
|
Hi,
I'm wanting to make this work in FireFox too - any clues as to what I need to be tinkering with? I assume I need to add something to the browser file? How does the code reference the browser file?
Thanks in advance
V
|
|
|
|
|
|
This link doesn't work currently. This is the control that would work great but we need it to work in FireFox as well. Any one know what I would need to do to fix this.
|
|
|
|
|
I'm not sure if I understand, I tested this control using FireFox and it works fine. Could you be more specific?
|
|
|
|
|
I faced this problem, too. And I've already found a way to fix it.
Open BrowserFile.browser in App_Browsers folder of your project.
Change the value of refID from "IE6to9" to "default"
As you can see, this is the result:
<browsers>
<browser refID="default">
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.DropDownList" adapterType="CraigBlog.Net.Code.Adapters.DropDownListAdapter" />
</controlAdapters>
</browser>
</browsers>
Regards,
LamNguyen
|
|
|
|
|
Why it firing the above as "Invalid postback or callback argument" exception -
I guess it is because the orginal DDL rendered different HTML and when it submit the form the HTML is now including optgroup ...
ItzikBS
|
|
|
|
|
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];
}
}
}
}
|
|
|
|