|
optgroup{font-style:normal}
is not IE compatible what will be the compatible code
|
|
|
|
|
The style can be overridden in IE7+; for IE6 and under it will always be presented in bold italic.
|
|
|
|
|
Hi,
Following my previous post, since there is no known solution for the Viewstate issue, i decided to use the list as a control.
I did find out do that the items have to be added in grouping order, this cause a problem to me.
So below is a fix so you can add items in any order, note that items without a group will always be added on top.
<br />
[DefaultValue(true)]<br />
public bool SortItems<br />
{<br />
get<br />
{<br />
return ViewState["SortItems"] == null ? true : (bool)ViewState["SortItems"];<br />
}<br />
set<br />
{<br />
ViewState["SortItems"] = value;<br />
}<br />
}<br />
protected override void RenderContents(HtmlTextWriter writer)<br />
{<br />
SortedDictionary<string, List<ListItem>> sortedItems = new SortedDictionary<string, List<ListItem>>();<br />
<br />
foreach (ListItem item in this.Items)<br />
{<br />
if (item.Attributes[OptionGroupAttribute] == null)<br />
{<br />
if (sortedItems.ContainsKey("_"))<br />
{<br />
sortedItems["_"].Add(item);<br />
}<br />
else<br />
{<br />
sortedItems.Add("_", new List<ListItem>());<br />
sortedItems["_"].Add(item);<br />
}<br />
}<br />
else<br />
{<br />
string optionGroup = item.Attributes[OptionGroupAttribute];<br />
<br />
if (sortedItems.ContainsKey(optionGroup))<br />
{<br />
sortedItems[optionGroup].Add(item);<br />
}<br />
else<br />
{<br />
sortedItems.Add(optionGroup, new List<ListItem>());<br />
sortedItems[optionGroup].Add(item);<br />
}<br />
}<br />
}<br />
<br />
foreach (KeyValuePair<string, List<ListItem>> entry in sortedItems)<br />
{<br />
if (SortItems)<br />
{<br />
entry.Value.Sort(delegate(ListItem item1, ListItem item2)<br />
{<br />
return item1.Text.CompareTo(item2.Text);<br />
});<br />
}<br />
<br />
if (entry.Key != "_")<br />
RenderOptionGroupBeginTag(entry.Key, writer);<br />
<br />
foreach (ListItem item in entry.Value)<br />
{<br />
Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);<br />
<br />
RenderListItem(item, writer);<br />
}<br />
<br />
if (entry.Key != "_")<br />
RenderOptionGroupEndTag(writer);<br />
}<br />
}<br />
And also here is a fix for the viewstate issues:
<br />
protected override object SaveViewState()<br />
{<br />
this.ViewState["-1Saved"] = (this.SelectedIndex == -1);<br />
<br />
for (int i = 0; i < this.Items.Count; i++)<br />
{<br />
if (this.Items[i].Attributes[OptionGroupAttribute] != null)<br />
this.ViewState["ListItemGroup" + i] = this.Items[i].Attributes[OptionGroupAttribute];<br />
}<br />
return base.SaveViewState();<br />
}<br />
<br />
<br />
protected override void LoadViewState(object state)<br />
{<br />
<br />
base.LoadViewState(state);<br />
<br />
for (int i = 0; i < this.Items.Count; i++)<br />
{<br />
if (this.ViewState["ListItemGroup" + i] != null)<br />
this.Items[i].Attributes[OptionGroupAttribute] = (string)this.ViewState["ListItemGroup" + i];<br />
}<br />
<br />
if (this.ViewState["-1Saved"] != null && (Boolean)this.ViewState["-1Saved"])<br />
{<br />
this.SelectedIndex = -1;<br />
}<br />
}<br />
|
|
|
|
|
I used Albert Weinert's code below to show my optgroups, everything works fine until the page postbacks. The list gets added again making duplicates and each time there is a post back the list grows. What could be wrong?
|
|
|
|
|
Please see my fixes on a post above!
The list is being stored in viewstate, you shouldn't have to re-populate after every postback:
For example:
<br />
protected void Page_Load(object sender, EventArgs e)<br />
{<br />
if (!IsPostBack)<br />
{<br />
PopulateListWithValues();<br />
}<br />
}<br />
|
|
|
|
|
For anyone in need, here is a VB version of the class:
For anyone in need for a VB version:
Public Class DropDownListAdapter
Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter
Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
Dim list As DropDownList = Me.Control
Dim currentOptionGroup As String
Dim renderedOptionGroups As New ArrayList
Dim item As ListItem
For Each item In list.Items
If item.Attributes("OptionGroup") Is Nothing Then
RenderListItem(item, writer)
Else
currentOptionGroup = item.Attributes("OptionGroup")
If renderedOptionGroups.Contains(currentOptionGroup) Then
RenderListItem(item, writer)
Else
If renderedOptionGroups.Count > 0 Then
RenderOptionGroupEndTag(writer)
End If
RenderOptionGroupBeginTag(currentOptionGroup, writer)
renderedOptionGroups.Add(currentOptionGroup)
RenderListItem(item, writer)
End If
End If
Next
If renderedOptionGroups.Count > 0 Then
RenderOptionGroupEndTag(writer)
End If
End Sub
Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
writer.WriteBeginTag("optgroup")
writer.WriteAttribute("label", name)
writer.Write(HtmlTextWriter.TagRightChar)
writer.WriteLine()
End Sub
Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
writer.WriteEndTag("optgroup")
writer.WriteLine()
End Sub
Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
writer.WriteBeginTag("option")
writer.WriteAttribute("value", item.Value, True)
If (item.Selected) Then
writer.WriteAttribute("selected", "selected", False)
End If
Dim key As String
For Each key In item.Attributes.Keys
writer.WriteAttribute(key, item.Attributes(key))
Next key
writer.Write(HtmlTextWriter.TagRightChar)
HttpUtility.HtmlEncode(item.Text, writer)
writer.WriteEndTag("option")
writer.WriteLine()
End Sub
End Class
Jaywon
|
|
|
|
|
...and a couple of other minor alterations:
Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections
Imports System.Collections.Generic
Public Class DropDownListAdapter
Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter
Private Const m_optionGroupAttribute As String = "OptionGroup"
Private Const m_tagOptionGroup As String = "optgroup"
Private Const m_attributeLabel As String = "label"
Private m_viewstates As Object()
Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Me.Control, DropDownList)
Dim l_currentOptionGroup As String
Dim l_renderedOptionGroups As New ArrayList()
For Each l_item As ListItem In l_list.Items
Page.ClientScript.RegisterForEventValidation(l_list.UniqueID, l_item.Value)
If l_item.Attributes(m_optionGroupAttribute) Is Nothing Then
RenderListItem(l_item, writer)
Else
l_currentOptionGroup = l_item.Attributes(m_optionGroupAttribute)
If (l_renderedOptionGroups.Contains(l_currentOptionGroup)) Then
RenderListItem(l_item, writer)
Else
If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
RenderOptionGroupBeginTag(l_currentOptionGroup, writer)
l_renderedOptionGroups.Add(l_currentOptionGroup)
RenderListItem(l_item, writer)
End If
End If
Next
If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
Else
MyBase.RenderContents(writer)
End If
End Sub
Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
writer.AddAttribute(m_attributeLabel, name)
writer.RenderBeginTag(m_tagOptionGroup)
End Sub
Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
writer.RenderEndTag()
End Sub
Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
For Each key As String In item.Attributes.Keys
If (key <> m_optionGroupAttribute) Then
writer.AddAttribute(key, item.Attributes(key))
End If
Next
writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, True)
If (item.Selected) Then
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected")
End If
writer.RenderBeginTag(HtmlTextWriterTag.Option)
writer.WriteEncodedText(item.Text)
writer.RenderEndTag()
End Sub
Protected Overrides Function SaveAdapterViewState() As Object
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Control, DropDownList)
Dim l_viewState(l_list.Items.Count + 1) As Object
Dim i As Integer = 0
For Each item As ListItem In l_list.Items
l_viewState(i) = item.Attributes(m_optionGroupAttribute)
i += 1
Next
l_viewState(i) = MyBase.SaveAdapterViewState()
Return l_viewState
Else
Return MyBase.SaveAdapterControlState()
End If
End Function
Protected Overrides Sub LoadAdapterViewState(ByVal state As Object)
If (Not Page Is Nothing) Then
m_viewstates = CType(state, Object())
MyBase.LoadAdapterViewState(m_viewstates(m_viewstates.Length - 1))
Else
MyBase.LoadAdapterViewState(state)
End If
End Sub
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
If (Not Page Is Nothing) Then
If (Not m_viewstates Is Nothing AndAlso m_viewstates.Length > 1) Then
Dim l_list As DropDownList = CType(Control, DropDownList)
If (Page.EnableEventValidation) Then
If (m_viewstates.Length <> l_list.Items.Count + 1) Then
Throw New ViewStateException()
End If
End If
Dim l_max As Integer = m_viewstates.Length
If (l_list.Items.Count < l_max) Then
l_max = l_list.Items.Count
End If
For i As Integer = 0 To l_max - 1
l_list.Items(i).Attributes(m_optionGroupAttribute) = CStr(m_viewstates(i))
Next
End If
End If
MyBase.OnPreRender(e)
End Sub
End Class
Marc
|
|
|
|
|
I modified the code above to make a standalone version.
Now you can add this class to a Web Control Library to use it as a web control.
Here's the code (enjoy):
Imports System.Web.UI
Imports System.Web.UI.WebControls
Public Class ComboBox
Inherits DropDownList
Private Const m_optionGroupAttribute As String = "OptionGroup"
Private Const m_tagOptionGroup As String = "optgroup"
Private Const m_attributeLabel As String = "label"
Private m_viewstates As Object()
Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Me, DropDownList)
Dim l_currentOptionGroup As String
Dim l_renderedOptionGroups As New ArrayList()
For Each l_item As ListItem In l_list.Items
Page.ClientScript.RegisterForEventValidation(l_list.UniqueID, l_item.Value)
If l_item.Attributes(m_optionGroupAttribute) Is Nothing Then
RenderListItem(l_item, writer)
Else
l_currentOptionGroup = l_item.Attributes(m_optionGroupAttribute)
If (l_renderedOptionGroups.Contains(l_currentOptionGroup)) Then
RenderListItem(l_item, writer)
Else
If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
RenderOptionGroupBeginTag(l_currentOptionGroup, writer)
l_renderedOptionGroups.Add(l_currentOptionGroup)
RenderListItem(l_item, writer)
End If
End If
Next
If (l_renderedOptionGroups.Count > 0) Then
RenderOptionGroupEndTag(writer)
End If
Else
MyBase.RenderContents(writer)
End If
End Sub
Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
writer.AddAttribute(m_attributeLabel, name)
writer.RenderBeginTag(m_tagOptionGroup)
End Sub
Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
writer.RenderEndTag()
End Sub
Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
For Each key As String In item.Attributes.Keys
If (key <> m_optionGroupAttribute) Then
writer.AddAttribute(key, item.Attributes(key))
End If
Next
writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value, True)
If (item.Selected) Then
writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected")
End If
writer.RenderBeginTag(HtmlTextWriterTag.Option)
writer.WriteEncodedText(item.Text)
writer.RenderEndTag()
End Sub
Protected Overrides Function SaveViewState() As Object
If (Not Page Is Nothing) Then
Dim l_list As DropDownList = CType(Me, DropDownList)
Dim l_viewState(l_list.Items.Count) As Object
Dim i As Integer = 0
For Each item As ListItem In l_list.Items
l_viewState(i) = item.Attributes(m_optionGroupAttribute)
i += 1
Next
l_viewState(i) = MyBase.SaveViewState()
Return l_viewState
Else
Return MyBase.SaveViewState()
End If
End Function
Protected Overrides Sub LoadViewState(ByVal state As Object)
If (Not Page Is Nothing) Then
m_viewstates = CType(state, Object())
MyBase.LoadViewState(m_viewstates(m_viewstates.Length - 1))
Else
MyBase.LoadViewState(state)
End If
End Sub
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
If (Not Page Is Nothing) Then
If (Not m_viewstates Is Nothing AndAlso m_viewstates.Length > 1) Then
Dim l_list As DropDownList = CType(Me, DropDownList)
If (Page.EnableEventValidation) Then
If (m_viewstates.Length <> l_list.Items.Count + 1) Then
Throw New ViewStateException()
End If
End If
Dim l_max As Integer = m_viewstates.Length
If (l_list.Items.Count < l_max) Then
l_max = l_list.Items.Count
End If
For i As Integer = 0 To l_max - 1
l_list.Items(i).Attributes(m_optionGroupAttribute) = CStr(m_viewstates(i))
Next
End If
End If
MyBase.OnPreRender(e)
End Sub
End Class
Note that I modified something in the SaveViewState function.
-- modified at 13:22 Thursday 4th October, 2007
|
|
|
|
|
WOWZERS, After a few hours of putzing about, I finally got this working. It appears that the + 1 in the SaveViewState IS REQUIRED! othewise an index out of bounds error occurs.
I REALLY appreciate you all getting this working, thanks a bunch!
|
|
|
|
|
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.
|
|
|
|
|