Custom Property Type with related dropdown lists

Have anyone developed a Custon Property Type with multiple related dropdown lists?, or multiple Custom Property Types that are populated by relations to each other. Example, car model dropdown is populated when selecting car brand. /Jonas
Jul 10, 2007 10:45
Hi Jonas! One problem is that the editpage is not intended to be postbacked, so populating the lists serverside requires some hacking to prevent the "you're navigating away from this page"-popup warning. Setting 'window.onbeforeunload = null' removes this warning, so you'd have to make that call in client script before your'e control performs its postback. Another problem is that you cannot get straight access to ViewState from your Property (as it's not technically a Control), but this can be solved by either using hidden Controls holding data or by adding custom Controls to the control collection of the container. A third problem is that you cannot look at container.Page.IsPostBack to determine if list's have to initialized or not, as the editpage will be postbacked when user clicks the [Edit]-tab. ONe solution would be to use a hidden textbox and set its value to "loaded" the first time. The next roundtrip, you can check the textbox for the value "loaded", and if found you'll know its a "real" postback. If you can populate the lists through clientside scriptcode and not require postbacks, it would probably be the easiest solution, but that would require all listdata for all "combinations" to be present in the html code, possibly making the page quite "heavy". Regards, Johan Olofsson EPiServer AB
Jul 10, 2007 11:15
Here's some working sample code to demonstrate how to use two related dropdownlists (car and model): using System; using System.Data; using System.Collections.Generic; using System.Web.UI; using System.Web.UI.WebControls; using EPiServer.Core; using EPiServer.PlugIn; namespace SomeNamespace { [ PageDefinitionTypePlugIn( DisplayName = "PropertyMultipleListDemo")] public class PropertyMultipleListDemo : PropertyString { protected static DataSet car_models; /// /// setup some sample dataset with related tables, car & model /// static PropertyMultipleListDemo() { car_models = new DataSet(); DataTable ct = car_models.Tables.Add( "Car" ); DataTable mt = car_models.Tables.Add( "Model" ); ct.Columns.Add( "Car", typeof(string)); ct.PrimaryKey = new DataColumn[]{ct.Columns["Car"]}; mt.Columns.Add( "Car", typeof(string)); mt.Columns.Add( "Model", typeof(string)); mt.PrimaryKey = new DataColumn[]{mt.Columns["Car"], mt.Columns["Model"]}; car_models.Relations.Add( "car_model", ct.Columns["Car"], mt.Columns["Car"], true ); ct.Rows.Add( "Chevrolet" ); ct.Rows.Add( "Dodge" ); ct.Rows.Add( "Ford" ); mt.Rows.Add( "Chevrolet", "Impala" ); mt.Rows.Add( "Chevrolet", "Caprice" ); mt.Rows.Add( "Chevrolet", "G10" ); mt.Rows.Add( "Chevrolet", "G20" ); mt.Rows.Add( "Chevrolet", "G30" ); mt.Rows.Add( "Chevrolet", "Truck 1500" ); mt.Rows.Add( "Chevrolet", "Truck 2500" ); mt.Rows.Add( "Chevrolet", "Truck 3500" ); mt.Rows.Add( "Dodge", "WC" ); mt.Rows.Add( "Dodge", "Saloon" ); mt.Rows.Add( "Dodge", "RAM Van" ); mt.Rows.Add( "Ford", "F-100" ); mt.Rows.Add( "Ford", "F-150" ); mt.Rows.Add( "Ford", "F-250" ); mt.Rows.Add( "Ford", "F-350" ); } protected string selectedCar, selectedModel; protected TextBox oLoaded; protected DropDownList carDropDownList, modelDropDownList; public override void CreateChildControls(string renderType, Control container) { if(renderType == "edit") { // oLoaded is used to check wether we are initally loaded, in which // case we should initialize the dropdown's with initial data (if any) oLoaded = new TextBox(); oLoaded.Visible = false; container.Controls.Add(oLoaded); // add the car dropdown and model dropdown Panel panel = new Panel(); carDropDownList = new DropDownList(); carDropDownList.ID = Name + "carList"; // car dropdownlist should autopostback when selection changes carDropDownList.AutoPostBack = true; carDropDownList.SelectedIndexChanged += new EventHandler( carDropDownList_SelectedIndexChanged ); // we databind on every roundtrip carDropDownList.DataSource = car_models.Tables["Car"]; carDropDownList.DataTextField = "Car"; carDropDownList.DataValueField = "Car"; carDropDownList.DataBind(); carDropDownList.Items.Insert(0, new ListItem()); panel.Controls.Add( carDropDownList ); container.Controls.Add(panel); panel = new Panel(); // model dropdown is initally empty, and is databound when carDropDownList_SelectedIndexChanged is called modelDropDownList = new DropDownList(); modelDropDownList.ID = Name + "modelList"; panel.Controls.Add( modelDropDownList ); container.Controls.Add( panel ); container.Controls.Add( CreateCustomParseValidator( new ServerValidateEventHandler( CustomParseValidation ) ) ); // is it the ntial load? // Note: cannot check container.Page.IsPostBack, as it would *always* be false, even // when loaded the first, as clicking [Edit]-tab would postback the page. if( !"loaded".Equals(oLoaded.Text) ) { if(!String.IsNullOrEmpty( selectedCar )) { ListItem selectedItem = carDropDownList.Items.FindByValue( selectedCar ); if(null != selectedItem) { selectedItem.Selected = true; // explicit call SelectedIndexChanged to databind model dropdownlist carDropDownList_SelectedIndexChanged(null, new EventArgs()); selectedItem = modelDropDownList.Items.FindByValue(selectedModel); if(null!=selectedItem) selectedItem.Selected = true; } } oLoaded.Text = "loaded"; } } else { // simply render selected car & model as textstring container.Controls.Add( new LiteralControl(String.Format("{0} {1}", selectedCar, selectedModel))); } } protected void CustomParseValidation( object sender, ServerValidateEventArgs ea ) { if(carDropDownList.SelectedIndex > 0) { if(modelDropDownList.SelectedIndex > 0) { // combine (using , ) selected car with selected model into a string String = carDropDownList.SelectedValue + "," + modelDropDownList.SelectedValue; ea.IsValid = true; } else ea.IsValid=false; // if a car selected, then a model MUST be selected } else ea.IsValid = true; } protected void carDropDownList_SelectedIndexChanged( object sender, EventArgs e ) { // databind modelList if(!String.IsNullOrEmpty( carDropDownList.SelectedValue )) { modelDropDownList.DataSource = new DataView( car_models.Tables["Model"], String.Format( "Car='{0}'", carDropDownList.SelectedValue ), "Model", DataViewRowState.CurrentRows ); modelDropDownList.DataTextField = "Model"; modelDropDownList.DataValueField = "Model"; modelDropDownList.DataBind(); modelDropDownList.Items.Insert( 0, new ListItem() ); } else { modelDropDownList.ClearSelection(); modelDropDownList.Items.Clear(); } } /// /// we override set method of String property to split a given "car,model" string into /// selectedCar and selectedModel /// these values are then used in initally load (CreateChildControls) to initialize the /// dropdownlists to their initial values. /// public override string String { set { base.String = value; if(!String.IsNullOrEmpty( base.String )) { // split into car & model string[] temp = base.String.Split( ',' ); if(temp.Length != 2) throw new Exception( "Invalid string, expected \"car,model\";" ); selectedCar = temp[0]; selectedModel = temp[1]; } } } } } Regards, Johan Olofsson EPiserver AB
Jul 10, 2007 12:45
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.