Click here to Skip to main content
15,888,202 members
Articles / Programming Languages / C#
Article

Localizing .NET Enums

Rate me:
Please Sign up or sign in to vote.
4.94/5 (101 votes)
17 Oct 2007CPOL6 min read 342.6K   3.8K   249   81
Describes a technique for displaying localized text for enums

Introduction

One of the neat things about enumerations in .NET is that you can use data binding to display and select enum values in a list or drop down combo. For instance, consider the following enum definition:

C#
enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

We can display the enumeration values (and allow the user to select a value) by adding a ListBox to a form and setting the DataSource with one line of code:

C#
_enumListBox.DataSource = Enum.GetValues(typeof(SampleEnum));

That's great - when we run our code now the list box shows the enumeration values:

Screenshot - EnumList.jpg

We can get the selected enum value by simply using the list box SelectedItem property:

C#
SampleEnum selectedSampleEnum = (SampleEnum)_enumListBox.SelectedItem;

There are two problems with this technique however. The first problem is that the text we wish to display to the user may not be exactly the same as the enumeration value. In the example above, it would be much nicer to display Very Small with a space between the words. We can't use this as an enumeration value however because enum values cannot have spaces. The second problem is that there is no way to translate the enum values if we want to localize our application (i.e. provide a translated user interface for different cultures).

This article presents a simple solution to the above problems that leverages the power of .NET TypeConverters.

Background

The article Humanizing the Enumerations by Alex Kolesnichenko also addresses the problems discussed above by attaching a custom attribute to each enumeration value and using an adaptor class (EnumToHumanReadableConverter) as the data source. Our approach instead leverages the power of the .NET TypeConverter mechanism to automatically handle conversion to and from localized text and enum values. The benefit of this approach is that not only does it work for data binding (with no extra code), it can also be used anywhere in your application that you need to convert between localized text and enum values.

What is a TypeConverter

TypeConverters are the in-built .NET mechanism for converting objects of one type to another (for instance from an enum value to a string value). When the ListBox control (and other .NET controls) displays enum values, it first converts them to strings using a TypeConverter. The TypeConverter it uses depends on the TypeConverter that has been associated with the type using the System.ComponentModel.TypeConverterAttribute. By default all enum types use the predefined EnumConverter class. As we have seen, the EnumConverter simply converts the enum value to its exact string representation. Fortunately we can define our own derived TypeConverter class and associate it with our enum type when we declare it as follows:

C#
[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

In this solution we define a custom TypeConverter class (LocalizedEnumConverter) that converts enum values to and from string values using localized strings from the project resources.

Using the Code

The sample project code consists of a library (Infralution.Localization) and a separate test application. The library defines a base TypeConverter class (ResourceEnumConverter) that converts enum values to and from strings using a ResourceManager that reads the string values from a compiled RESX file. The ResourceManager that is used to do the lookup is passed to the constructor of the ResourceEnumConverter class. Follow the simple steps below to localize enums in your application using this class.

Define a class derived from ResourceEnumConverter in your application project that passes the ResourceManager for the project RESX file you wish to use for the enum lookups. Typically you would just use the standard project Properties.Resources:

C#
class LocalizedEnumConverter : Infralution.Localization.ResourceEnumConverter
{
    public LocalizedEnumConverter(Type type)
        : base(type, Properties.Resources.ResourceManager)
    {
    }
}

Use the System.ComponentModel.TypeConverterAttribute attribute on each enum declaration to associate this TypeConverter with the enum:

C#
[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

Open the Properties\Resources.resx file in the resources editor and enter the text to display for each enum value. The resource name is just the enum type name followed by the value (underscore separated):

Screenshot - EnglishResx.jpg

Now we are ready to add some localized enum text values. Create a set of French resources by copying the Properties\Resources.resx file to Properties\Resources.fr.resx. Make sure that the "Custom Tool" (in the Properties window) for this new RESX file is blank - or you will end up with some strange compilation errors. Open the Properties\Resources.fr.resx file in the resources editor and enter the translated values:

Screenshot - FrenchResx.jpg

Now set the user locale to French using the Control Panel Regional options and run your application. The enum values are now displayed in French. The sample application demonstrates the above and also allows you to dynamically set the CurrentThread.CurrentCulture property by selecting it from a drop down list:

Screenshot - SampleApp.jpg

The ResourceEnumConverter class also supports converting text value back to enum values. The sample application allows you to test this by entering values into the text box and clicking the Convert button.

Localizing Enums in ASP.NET

In Windows Forms, all of the standard controls use TypeConverters to convert bound data values to display strings. Unfortunately for some reason Microsoft did not use this same approach when developing ASP.NET controls. ASP.NET controls typically just use the Object.ToString() method to convert the bound data value to text. This means that while we can still define our TypeConverter (as described above) it won't be used by default by ASP.NET controls.

To solve this problem we have added a static GetValues method to the ResourceEnumConverter class. This method uses the TypeConverter to return a list of KeyValuePair objects for the given enum type. The Key is the enum value and the Value is the localized display text for that enum. To bind an ASP.NET control we set the DataValueField property of the control to Key and the DataTextField property to Value. Then we bind the control to the list returned by the GetValues method as follows:

C#
protected void Page_Load(object sender, EventArgs e)
{
    _enumListBox.DataSource = 
        LocalizedEnumConverter.GetValues(typeof(SampleEnum));
    this.DataBind();
}

Flag Enumerations

Andy Mase pointed out that the original code posted did not handle bit field enumerations (defined using the Flag attribute). In this case an enumeration value can be a bitwise combination of the named enumeration values. The enum may also define named enumeration values which are bitwise combinations of other named values. In the example below an All value is defined which is the bitwise combination of all other values.

C#
[TypeConverter(typeof(LocalizedEnumConverter))]
[Flags]
public enum TextStyle : byte
{
    None = 0x0,
    Bold = 0x1,
    Italic = 0x2,
    Underline = 0x4,
    All = 0xFF
}

The ResourceEnumConverter class now handles converting bit field enums to and from text. When converting a bit field enum value to text, it first checks if the value is one of the named enumeration values. If it is, then the localized text corresponding to the named value will be used. Otherwise it finds the combination of single bit values that are set and creates the text by concatenating the localized text for these together. For example the value 0x3 would be converted to Bold, Italic in English. The download now includes a separate project (TestLocalizedFlagEnum) that demonstrates using the LocalizedEnumConverter with a bit field enum.

History

  • 2007.08.09 - Initial posting
  • 2007.10.17 - Added handling of flagged enums and localizing enums in ASP.NET

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Infralution
Australia Australia
I am currently the Software Architect at Infralution. Infralution develops .NET components and solutions including:

Globalizer - localization for .NET made easy. Let your translators instantly preview translations and even build the localized version of your application without giving away your source code.

Infralution Licensing System - simple, secure and affordable licensing for .NET apps and components

Virtual Tree - superfast, flexible, databound tree/list view

Comments and Discussions

 
QuestionEnums database Pin
Cool Smith28-Feb-19 21:16
Cool Smith28-Feb-19 21:16 
QuestionUsage with WinForms DatagridView Pin
smaier19817-Jan-15 4:54
smaier19817-Jan-15 4:54 
AnswerRe: Usage with WinForms DatagridView Pin
Anas Teinah17-Jun-19 12:04
Anas Teinah17-Jun-19 12:04 
QuestionNice article, but quick question... Pin
polo2827-Nov-12 22:48
polo2827-Nov-12 22:48 
AnswerRe: Nice article, but quick question... Pin
Grant Frisken28-Nov-12 10:59
Grant Frisken28-Nov-12 10:59 
GeneralRe: Nice article, but quick question... Pin
polo2828-Nov-12 21:21
polo2828-Nov-12 21:21 
AnswerRe: Nice article, but quick question... Pin
_Codebeast6-Aug-13 8:07
_Codebeast6-Aug-13 8:07 
QuestionGreat article! ..i have 2 problems (underscore as separator, enums with same name and value) Pin
Pao-lino15-Oct-12 22:42
Pao-lino15-Oct-12 22:42 
AnswerRe: Great article! ..i have 2 problems (underscore as separator, enums with same name and value) Pin
Grant Frisken16-Oct-12 12:10
Grant Frisken16-Oct-12 12:10 
GeneralRe: Great article! ..i have 2 problems (underscore as separator, enums with same name and value) Pin
Pao-lino21-Oct-12 22:41
Pao-lino21-Oct-12 22:41 
GeneralMy vote of 5 Pin
emoulin26-Jul-12 0:41
emoulin26-Jul-12 0:41 
QuestionGreat code, but some questions Pin
Berryl Hesh27-May-12 4:32
Berryl Hesh27-May-12 4:32 
AnswerRe: Great code, but some questions Pin
Grant Frisken28-May-12 1:50
Grant Frisken28-May-12 1:50 
QuestionNice Pin
karenpayne2-Nov-11 10:45
karenpayne2-Nov-11 10:45 
GeneralAnother way using extension methods [modified] Pin
si61826-Mar-11 16:50
si61826-Mar-11 16:50 
GeneralRe: Another way using extension methods Pin
Grant Frisken27-Mar-11 12:25
Grant Frisken27-Mar-11 12:25 
GeneralRe: Another way using extension methods [modified] Pin
si61827-Mar-11 14:20
si61827-Mar-11 14:20 
GeneralRe: Another way using extension methods Pin
Grant Frisken27-Mar-11 16:28
Grant Frisken27-Mar-11 16:28 
GeneralRe: Another way using extension methods Pin
si61827-Mar-11 20:54
si61827-Mar-11 20:54 
QuestionLocalized enum with gridview Pin
spcghst440@hotmail.com24-Jan-11 9:42
spcghst440@hotmail.com24-Jan-11 9:42 
AnswerRe: Localized enum with gridview Pin
spcghst440@hotmail.com24-Jan-11 10:32
spcghst440@hotmail.com24-Jan-11 10:32 
GeneralRe: Localized enum with gridview Pin
Grant Frisken24-Jan-11 11:03
Grant Frisken24-Jan-11 11:03 
GeneralRe: Localized enum with gridview Pin
spcghst440@hotmail.com25-Jan-11 4:56
spcghst440@hotmail.com25-Jan-11 4:56 
GeneralRe: Localized enum with gridview Pin
spcghst440@hotmail.com25-Jan-11 9:09
spcghst440@hotmail.com25-Jan-11 9:09 
GeneralRe: Localized enum with gridview Pin
spcghst440@hotmail.com27-Jan-11 4:42
spcghst440@hotmail.com27-Jan-11 4:42 
I sent the solution I came up with to Grant and he requested that I post it here for others as it will be a while before he can update the sample. First off I need to credit Javad Zarrinabadi[^] as I heavily sampled his code from here Creating an ASP.NET GridView Custom Field of type DropDownList[^]

I worked off of the "Infralution.Localization\TestASP\TestASP.sln" solution in Grant's sample. Hope this helps anyone else out there looking to do the same thing.

Step 1: Add this class to the Infralution.Localization (Converted to .Net 3.5) solution, and compile:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Compilation;
using System.Web.UI.WebControls;
using System.Reflection;

namespace Infralution.Localization {
	/// <summary>
	/// Tim Cartwright: Provides standard label/textbox binding. Also more effiecient if the grid is read only.
	/// </summary>
	public class EnumLabelBoundField : BoundField {
		protected override string FormatDataValue(object dataValue, bool encode) {
			TypeConverter converter = TypeDescriptor.GetConverter(dataValue.GetType());
			if (converter.CanConvertTo(typeof(string))) {
				return converter.ConvertToString(dataValue);
			}
			return base.FormatDataValue(dataValue, encode);

		}
	}
	/// <summary>
	/// Tim Cartwright: This class is designed to make it easy to bind an enum to a combox and also localize it. It extends the work of Grant Frisken here : http://www.codeproject.com/kb/cs/localizingenums.aspx
	/// 
	/// <example>
	/// <%@ Register Assembly="Infralution.Localization" Namespace="Infralution.Localization" TagPrefix="ctl" %>
	/// 
	///	<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
	///		AutoGenerateEditButton="True" onrowcancelingedit="GridView1_RowCancelingEdit" 
	/// 	onrowediting="GridView1_RowEditing" onrowupdating="GridView1_RowUpdating" 
	/// 	OnRowCreated="GridView1_RowCreated">
	/// 	<Columns>
	///			<asp:BoundField DataField="FirstName" HeaderText="First Name" />
	///			<asp:BoundField DataField="LastName" HeaderText="Last Name" />
	///			<ctl:EnumComboBoxField DataField="ShoeSize" HeaderText="Shoe Size" />
	///		</Columns>
	///	</asp:GridView>
	///
	/// //Then right before you bind to the grid, do this :
	/// ((EnumComboBoxField)GridView1.Columns[2]).ComboBoxDataSource = LocalizedEnumConverter.GetValues(typeof(SampleEnum));  
	/// 
	/// LocalizedEnumConverter is a class inheriting from ResourceEnumConverter. Example:
	/// 	class LocalizedEnumConverter : Infralution.Localization.ResourceEnumConverter {
	///			public LocalizedEnumConverter(Type type): base(type, Properties.Resources.ResourceManager) {
	///			}
	///		}
	///
	/// </example>
	/// Resources: 
	///		http://www.codeproject.com/kb/cs/localizingenums.aspx
	///		http://www.codeproject.com/KB/webforms/GridViewComboBoxField.aspx
	/// </summary>
	public class EnumComboBoxField : BoundField {

		// Fields

		/// <summary>
		/// The ID field for the data field. If omitted; this.DataField + "ID" is assumed. Gets passed back as part of the NewValues dictionary in the RowUpdating event.
		/// </summary>
		private string _IDDataField;
		[Bindable(true), Category("Behavior"), DefaultValue(""), Localizable(false)]
		public string IDDataField {
			get { return _IDDataField ?? this.DataField + "ID"; }
			set { _IDDataField = value; } 
		}

		/// <summary>
		/// The data source of the combo for the column. Needs to be set before the grid is data bound the first time.
		/// <example>
		/// </example>
		/// ((EnumComboBoxField)GridView1.Columns[2]).ComboBoxDataSource = LocalizedEnumConverter.GetValues(typeof(SampleEnum)); 
		/// GridView1.DataBind();
		/// </summary>
		[Bindable(false), Category("Behavior"), DefaultValue(""), Localizable(false)]
		public List<KeyValuePair<Enum, string>> DropDownDataSource {
			get {
				List<KeyValuePair<Enum, string>> val = (List<KeyValuePair<Enum, string>>)ViewState["ComboBoxDataSource"];
				return ((val == null) ? null : val);
			}
			set {
				ViewState["ComboBoxDataSource"] = value; 
			}
		}
		
		[Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
		public string ComboBoxToolTip {
			get {
				string val = (string)ViewState["ComboBoxToolTip"];
				return ((val == null) ? this.HeaderText : val);
			}
			set { ViewState["ComboBoxToolTip"] = value; }
		}
		


		[Localizable(true), DefaultValue("")]
		public virtual string Text {
			get {
				object obj2 = base.ViewState["Text"];
				if (obj2 != null) {
					return (string)obj2;
				}
				return string.Empty;
			}
			set {
				if (!object.Equals(value, base.ViewState["Text"])) {
					base.ViewState["Text"] = value;
					this.OnFieldChanged();
				}
			}
		}

		private bool _suppressPropertyThrows;

		/// <summary>
		/// Not sure why this code is here, or what it does. It was part of one of the code samples 
		/// I borrowed code from. It looks... odd. Leaving as is for now.
		/// </summary>
		/// <param name="newField"></param>
		protected override void CopyProperties(DataControlField newField) {
			((EnumComboBoxField)newField).Text = this.Text;
			this._suppressPropertyThrows = true;
			((EnumComboBoxField)newField)._suppressPropertyThrows = true;
			base.CopyProperties(newField);
			this._suppressPropertyThrows = false;
			((EnumComboBoxField)newField)._suppressPropertyThrows = false;
		}

		/// <summary>
		/// Create Field over ride
		/// </summary>
		/// <returns></returns>
		protected override DataControlField CreateField() {
			return new EnumComboBoxField();
		}

		/// <summary>
		/// Extracts the values from the cell. Called automatically by the grid during updating. However, if you are not bound using the DataSourceId of the grid you will need to call this method manually.
		/// </summary>
		/// <param name="dictionary"></param>
		/// <param name="cell"></param>
		/// <param name="rowState"></param>
		/// <param name="includeReadOnly"></param>
		public override void ExtractValuesFromCell(IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly) {
			Control control = null;
			string dataField = this.DataField;
			object itemText = null;
			object itemValue = null;

			if (cell.Controls.Count > 0) {
				// Get column editor of type DropDownList of current cell 
				control = cell.Controls[0];
				DropDownList box = control as DropDownList;
				if ((box != null) && (includeReadOnly || box.Enabled)) {
					if (box.SelectedItem != null) {
						itemText = box.SelectedItem.Text;
						itemValue = box.SelectedItem.Value;
					}
				}
			}
			if (itemText != null) {
				if (dictionary.Contains(dataField)) {
					dictionary[dataField] = itemText;
				} else {
					dictionary.Add(dataField, itemText);
				}
			}
			if (itemValue != null) {
				if (dictionary.Contains(this.IDDataField)) {
					dictionary[this.IDDataField] = itemValue;
				} else {
					dictionary.Add(this.IDDataField, itemValue);
				}
			}
		}

		protected override object GetDesignTimeValue() {
			return true;
		}

		/// <summary>
		/// This over ride Initializes the data cell with either just a text value, or if editing a dropdownlist
		/// </summary>
		/// <param name="cell"></param>
		/// <param name="rowState"></param>
		protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState) {
			Control child = null;
			Control control2 = null;
			
			if (this.DropDownDataSource == null) {
				throw new NullReferenceException("ComboBoxDataSource can not be null. Please set ComboBoxDataSource before data binding the Grid.");
			}

			if ((!this.ReadOnly && (rowState & DataControlRowState.Edit) != DataControlRowState.Normal) || rowState == DataControlRowState.Insert){
				// If data cell is in edit mode, create DropDownList editor for this cell
				// and set data properties.
				DropDownList box = new DropDownList();
				box.DataSource = this.DropDownDataSource;
				box.DataTextField = "Value";
				box.DataValueField = "Key";
				box.DataBind();
				box.ToolTip = this.ComboBoxToolTip;
				child = box;
				if ((this.DataField.Length != 0) && ((rowState & DataControlRowState.Edit) != DataControlRowState.Normal)) {
					control2 = box;
				}
			} else if (this.DataField.Length != 0) {
				control2 = cell;
			}
			if (child != null) {
				cell.Controls.Add(child);


			}
			if ((control2 != null) && base.Visible) {
				control2.DataBinding += new EventHandler(this.OnDataBindField);
			}

		}

		/// <summary>
		/// Over ride of the databinding for the internal control of the cell
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected override void OnDataBindField(object sender, EventArgs e) {
			Control control = (Control)sender;
			Control namingContainer = control.NamingContainer;
			object dataValue = this.GetValue(namingContainer);
			
			bool encode = (this.SupportsHtmlEncode && this.HtmlEncode) && (control is TableCell);
			string str = this.FormatDataValue(dataValue, encode);
			if (control is TableCell) {
				if (str.Length == 0) {
					str = "&nbsp;";
				} else {
					str = FindValue(str);
				}
				((TableCell)control).Text = str;
			} else {
				//If data cell is in edit mode, set selected value of DropDownList 
				if (dataValue != null) {
					ListItem itm = ((DropDownList)control).Items.FindByValue(dataValue.ToString());
					if (itm != null) { ((DropDownList)control).SelectedValue = itm.Value; }
				}
			}
		}

		/// <summary>
		/// Helper method to find the item by value from the datasource
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		internal string FindValue(string key) {
			var ret = (from ds in this.DropDownDataSource
				where Convert.ToString(ds.Key).Equals(key, StringComparison.OrdinalIgnoreCase)
				select ds.Value).FirstOrDefault();

			return ret;
		}
	}
}


Step 2: Replace the contents of the Default.aspx with this code:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestASP._Default"
	Culture="auto" meta:resourcekey="PageResource1" UICulture="auto" %>

<%@ Register Assembly="Infralution.Localization" Namespace="Infralution.Localization"
	TagPrefix="ctl" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
	<title>Untitled Page</title>
</head>
<body>
	<form id="form1" runat="server">
	<div>
		Language:
		<asp:DropDownList ID="ddlLanguageSelector" runat="server" AutoPostBack="True">
			<asp:ListItem Value="en-us">English</asp:ListItem>
			<asp:ListItem Value="fr-fr">French</asp:ListItem>
		</asp:DropDownList>
		<p>This page does not demonstrate pure localization. The purpose of this page is to
			demonstrate localization when binding to various controls using an enum.<br />
		</p>
		<p>DropDownList:<asp:DropDownList ID="DropDownList2" runat="server" DataTextField="Value"
			DataValueField="Key">
		</asp:DropDownList>
		</p>
		<p>ListBox:<br />
			<asp:ListBox ID="_enumListBox" runat="server" DataTextField="Value" DataValueField="Key"
				Height="311px" Width="271px" meta:resourcekey="_enumListBoxResource1"></asp:ListBox>
		</p>
		<p>GridView:
			<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" AutoGenerateEditButton="True"
				OnRowCancelingEdit="GridView1_RowCancelingEdit" OnRowEditing="GridView1_RowEditing"
				OnRowUpdating="GridView1_RowUpdating">
				<Columns>
					<asp:BoundField DataField="FirstName" HeaderText="First Name" />
					<asp:BoundField DataField="LastName" HeaderText="Last Name" />
					<ctl:EnumComboBoxField DataField="ShoeSize" HeaderText="Shoe Size" />
					<ctl:EnumLabelBoundField DataField="ShoeSize" HeaderText="Shoe Size" />
				</Columns>
			</asp:GridView>
		</p>
	</div>
	</form>
</body>
</html>


Step 3: Replace the contents of the Default.aspx.cs with this code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Web.UI.WebControls;
using Infralution.Localization;

namespace TestASP {
	public class Ppl {
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public SampleEnum ShoeSize { get; set; }
	}

	public partial class _Default : System.Web.UI.Page {
		List<Ppl> Pplz = null;
		protected bool __languageChanged;
		private string CheckNull(object value) {
			string ret = Convert.ToString(value);
			if (String.IsNullOrEmpty(ret.Trim())) return null;
			return ret;
		}
		/// <summary>
		/// This code does not really have anything to do with the sameple, just allows for the dropdown to flip the language back and forth
		/// </summary>
		protected override void InitializeCulture() {
			//search the reqest.form. viewstate is not populated in this method yet, so that is our only recourse to determine if the language selector changed
			//this kill will be null unless the dropdown auto posts back normally
			string key = Array.Find(Request.Form.AllKeys, strKey => !String.IsNullOrEmpty(strKey) && strKey.ToLower().Contains("ddllanguageselector"));
			// first one not null wins. normally i would also inspect the user profile(instead of session) however that is outside the scope of this example.
			string language = CheckNull(Request.Form[key]) ?? CheckNull(Request.QueryString["language"]) ?? CheckNull(Session["language"]) ?? Request.UserLanguages[0];

			//determine if the language changed.
			__languageChanged = Convert.ToString(Session["language"]).ToLower() != Convert.ToString(language).ToLower();
			//set the culture. this sets them on both the UI and resources. MAY NOT BE what you want to do, so evaluate whether you want to set them both.
			Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
			//language changed, so save it for next compare.
			if (__languageChanged) { Session["language"] = language; }
		}
		protected void Page_Load(object sender, EventArgs e) {
			DropDownList2.DataSource = _enumListBox.DataSource = LocalizedEnumConverter.GetValues(typeof(SampleEnum));

			Random rand = new Random();
			//get the list out of the session, if that is null, create a new one
			Pplz = Session["pplz"] as List<Ppl> ?? new List<Ppl>();

			//this code generates some fake data to bind to the grid
			if (!IsPostBack || __languageChanged) {
				if (Pplz.Count == 0) {
					Pplz = new List<Ppl>();
					for (int i = 0; i < 10; i++) {
						Pplz.Add(new Ppl() {
							FirstName = "First" + i,
							LastName = "Last" + i,
							ShoeSize = (SampleEnum)Enum.Parse(typeof(SampleEnum), rand.Next(5).ToString())
						});
					}
				}
				BindGrid();
				this.DataBind();
				Session["pplz"] = Pplz;
			}
		}
		protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e) {
			GridView1.EditIndex = e.NewEditIndex;
			BindGrid();
		}
		protected void GridView1_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e) {
			GridView1.EditIndex = -1;
			BindGrid();
		}
		private void BindGrid() {
			GridView1.DataSource = Pplz;
			//*****************************************************//
			//this is the important line, we set the data source on the combo for the column 
			((EnumComboBoxField)GridView1.Columns[2]).DropDownDataSource = LocalizedEnumConverter.GetValues(typeof(SampleEnum));
			//*****************************************************//
			GridView1.DataBind();
		}
		protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e) {
			//The Keys, OldValues and NewValues collections are automatically populated ONLY when the GridView control is bound to data by using the DataSourceID property. 
			//foreach (DictionaryEntry entry in e.NewValues) {
			//	Trace.Write(entry.Key + "==" + e.NewValues[entry.Key]);
			//}

			//FOR OTHER NON DataSourceID binding methods, you can use:
			DataControlFieldCell cell = GridView1.Rows[e.RowIndex].Cells[3] as DataControlFieldCell;
			GridView1.Columns[2].ExtractValuesFromCell(e.NewValues, cell, DataControlRowState.Edit, true);

			foreach (DictionaryEntry entry in e.NewValues) {
				Trace.Write(entry.Key + "==" + e.NewValues[entry.Key]);
			}

			Pplz[e.RowIndex].ShoeSize = (SampleEnum)Enum.Parse(typeof(SampleEnum), e.NewValues["ShoeSizeID"].ToString());
			Session["pplz"] = Pplz;
			//not really saving the data, so just rebind. Just want to see the changed values come out of the combo. However, here is where you would save the data if need be.
			GridView1.EditIndex = -1;
			BindGrid();
		}

	}
}


What is important in the whole of the Default.aspx.cs code is this:
private void BindGrid() {
	GridView1.DataSource = Pplz;
	//*****************************************************//
	//this is the important line, we set the data source on the combo for the column 
	((EnumComboBoxField)GridView1.Columns[2]).DropDownDataSource = LocalizedEnumConverter.GetValues(typeof(SampleEnum));
	//*****************************************************//
	GridView1.DataBind();
}

You need to set the DropDownDataSource on the column right before you rebind the grid. Also, you need to rebind any of these controls any time the language changes. Which is why if __languageChanged is checked during page load in addition to the !IsPostBack.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.