Adapting ReorderList WebControl for rendering dynamic html

A

Andrew Jocelyn

Hi

I need to be able to edit and render dynamic html from a custom data source.
The rendered output is going to be html form elements for collecting survey
style data.

The main problem I´m facing is that the data which requires binding has a
different structure for each item in the list, i.e. I need to loop through a
list of objects which contain varied data and then render different html
accordingly. For example, the first item in the list may require a label and
textbox to be rendered, the second may require a label and populated
CheckboxList.

The ReorderList would be ideal I think if I can work out how to render
different html depending on the data of each item in the list. I need the
form creator to be able to sort the order of items. I also need to be able
to render the html items as a static form so need to know if the ReorderList
supports this. Can I create custom templates to use with the ReorderList,
i.e. one for each type of data item (row)? Is this the best approach or
should I consider a more custom approach e.g. overriding the
BaseDataBoundControl in some way? I guess this is more work but may be
necessary?

I hope this makes sense but please let me know if you need more information.

Many thanks

Andrew
 
A

Allen Chen [MSFT]

Hi Andrew,

My name is Allen Chen. It's my pleasure to work with you on this issue.

If my understanding is correct, you want to show different controls in each
item of ReorderList control. To do this the general way is to put all the
controls in the ItemTemplate and set their visibility according to our
custom logic. Here's the sample that demonstrates how to do this:

Aspx:
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<cc1:ReorderList ID="ReorderList1" runat="server"
DataSourceID="ObjectDataSource1" AllowReorder="false" >
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Visible='<%#
SetTextBoxVisibility(Container) %>' Text='<%#Eval("Data") %>'></asp:TextBox>
<asp:Label ID="Label1" runat="server" Visible='<%#
SetLabelVisibility(Container)%>' Text='<%#Eval("Data") %>'></asp:Label>
</ItemTemplate>
</cc1:ReorderList>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="Select" TypeName="WebApplication2.BLL"></asp:ObjectDataSource>

Aspx.cs:
namespace WebApplication2
{
public partial class WebForm1 : System.Web.UI.Page
{

public bool SetTextBoxVisibility(object container) {
ReorderListItem item = container as ReorderListItem;
if (item != null)
{
if (Convert.ToInt32(((MyData)item.DataItem).Data) % 3 == 0)
{
return true;
}
}
return false;
}
public bool SetLabelVisibility(object container)
{
ReorderListItem item = container as ReorderListItem;
if (item != null)
{
if (item.ItemIndex%2==0)
{
return true;
}
}
return false;
}
}
public class BLL
{
public IEnumerable Select()
{
//Test
List<MyData> list = new List<MyData>();
for (int i = 0; i < 10; i++)
{
list.Add(new MyData() { Data = i.ToString() });
}
return list;
}
}
public class MyData
{
public string Data { get; set; }
}
}

From the sample we can see how to get the data of each item. We can also
set the visibility according to the item index. It's very flexible for us
to decide what controls need to be displayed.


For some scenarios there is another way, that is to use extra properties in
the datasource item. Here's another sample:

aspx:

<asp:ScriptManager ID="ScriptManager1" runat="server" />
<cc1:ReorderList ID="ReorderList1" runat="server"
DataSourceID="ObjectDataSource1" AllowReorder="false" >
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Visible='<%#
Eval("ShowTextBox")%>' Text='<%#Eval("Data") %>'></asp:TextBox>
<asp:Label ID="Label1" runat="server" Visible='<%#
Eval("ShowLabel")%>' Text='<%#Eval("Data") %>'></asp:Label>
</ItemTemplate>
</cc1:ReorderList>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="Select" TypeName="WebApplication2.BLL"></asp:ObjectDataSource>


aspx.cs:

namespace WebApplication2
{
public partial class WebForm1 : System.Web.UI.Page
{

}
public class BLL
{
public IEnumerable Select()
{
//Test
List<MyData> list = new List<MyData>();
for (int i = 0; i < 10; i++)
{
list.Add(new MyData() { Data = i.ToString(),
ShowLabel=i%2==0?true:false, ShowTextBox=i%3==0?true:false });
}
return list;
}
}
public class MyData
{
public string Data { get; set; }
public bool ShowTextBox { get; set; }
public bool ShowLabel { get; set; }
}
}


Please have a try and let me know if it works. If you have further
questions please feel free to ask.

Regards,
Allen Chen
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Andrew Jocelyn

Hi Allen

Thanks for your suggestions. It looks like the right way to proceed. I´d
like to take your suggestion further and I am wondering about extending the
ReorderList control. It provides me with a lot of the functionality I need
already and I´d like to add a couple more templates, for example a header
and footer. Also I´d like to pre-populate the existing templates
(ItemTemplate, EditItemTemplate, InsertItemTemplate) with controls based on
the data source. The extended control must then only support a datasource of
type List<CustomObject>.

I have some questions:

1. Where do I add the logic for adding the custom controls to the
ItemTemplate, EditItemTemplate, InsertItemTemplate? Where do I set
visibility and add property values to the controls from the data source?

2. How should I add the new header and footer templates?

3. The extended control is going to render form controls which need their
values collecting after saving. Should I handle this in the extended
control? Can you tell me the best practice for doing this?

4. Should I throw an exception if the data source is not of the required
type? Where is the best place to handle this?

I´d appreciate any advice, suggestions, comments you can give me regarding
this.

Thanks again
Andrew
 
A

Allen Chen [MSFT]

Hi Andrew,

I have wrote a demo that may help to solve most of your concern. Please try
the following code first:

Aspx:
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>

<cc1:MyReorderList runat="server" ID="MyReorderList1"
SortOrderField="Priority">
<HeaderTemplate>
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox><asp:Button
ID="Button2"
runat="server" Text="Test Header" />
</HeaderTemplate>
<FooterTemplate>
<asp:Label ID="Label2" runat="server" Text="I'm a footer :)"
BackColor="Pink"></asp:Label>

</FooterTemplate>
</cc1:MyReorderList>

Aspx.cs:
public class MyObject
{
public int Priority { get; set; }
public bool Flag { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

List<MyObject> list = new List<MyObject>();
for (int i = 0; i < 10; i++) {
list.Add(new MyObject() { Priority = i });

}
list[1].Address = "Address1";
list[1].Flag = true;
list[3].Address = "Address3";
list[3].Name = "Allen";
list[5].Flag = true;
this.MyReorderList1.ItemTemplate = new
MyTemplate(typeof(MyObject), "Priority");
this.MyReorderList1.DataSource = list;
this.MyReorderList1.DataBind();


}


}

public class MyTemplate : ITemplate
{
Type type;
string FieldColName;
public MyTemplate( Type t, string fieldname)
{

type = t;
this.FieldColName = fieldname;
}
public void InstantiateIn(Control objContainer)
{
Panel p = new Panel();
Label lbl = new Label();

foreach (var item in MyReorderList.MyMapping)
{
Control c = Activator.CreateInstance(item.Value) as Control;
if (c != null) {
c.Visible = false;
p.Controls.Add(c);
}
}
p.Controls.Add(lbl);
lbl.DataBinding += new EventHandler(lblHeader_DataBinding);
objContainer.Controls.Add(p);
}
private void lblHeader_DataBinding(object sender, EventArgs e)
{
object bound_value_obj = null;
Label lbl = (Label)sender;
IDataItemContainer data_item_container =
(IDataItemContainer)lbl.NamingContainer;
foreach (var p in
data_item_container.DataItem.GetType().GetProperties())
{

Type propertytype = p.PropertyType;
bound_value_obj = p.GetValue(data_item_container.DataItem,
null);
Type controltype = null;
bool canfind =
MyReorderList.MyMapping.TryGetValue(propertytype, out controltype);
if (canfind)
{


lbl.Visible = false;
foreach (Control c in lbl.Parent.Controls)
{
if (c.GetType() == controltype)
{
c.Visible = true;

//add logic here if you want to set initial
value
//here is a sample
if (controltype == typeof(CheckBox))
{
if (bound_value_obj is bool)
{
((CheckBox)c).Checked =
(bool)bound_value_obj;
}
}
if (controltype == typeof(Label))
{
if (bound_value_obj != null)
((Label)c).Text =
bound_value_obj.ToString();
else
{
((Label)c).Text = "NULL";
}
}
//if you want more you can add extra logic
}
}
}
else
{
lbl.Text = bound_value_obj.ToString();
}
}
}
}


public class MyReorderList : ReorderList
{
public static Dictionary<Type, Type> MyMapping { get; private set; }
static MyReorderList()
{
//add default mappings.
MyMapping = new Dictionary<Type, Type>();
MyMapping.Add(typeof(string), typeof(Label));
MyMapping.Add(typeof(bool), typeof(CheckBox));
MyMapping.Add(typeof(int), typeof(Button));
}
[DefaultValue((string)null),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(RepeaterItem)), Browsable(false)]
public virtual ITemplate HeaderTemplate
{
get;
set;
}
[DefaultValue((string)null),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(RepeaterItem)), Browsable(false)]
public virtual ITemplate FooterTemplate
{
get;
set;
}



protected override void Render(HtmlTextWriter writer)
{
Panel header=new Panel();
this.HeaderTemplate.InstantiateIn(header);
header.RenderControl(writer);
base.Render(writer);
Panel footer = new Panel();
this.FooterTemplate.InstantiateIn(footer);
footer.RenderControl(writer);
}
}

About the code:

1. The footer and header is simply created via HeaderTemplate and
FooterTemplate. In Render event we can create instance of the footer/header
and render the HTML tags via RenderControl method.

2. To give user flexibility to change the mapping of the control and the
type of the data I added a global mapping MyMapping in MyReorderList class.
There're some predefined mappings added initially and users can add/remove
them when using MyReorderList control.

3. We can add much more functionalities that can provide more flexibility.
But to make it simple I opt to just provide the code above. You can see
though I tried to make it as simple as possible it still involved a lot of
code.

Please try it to see if it's the effect you're looking for and feel free to
let me know if you need further assistance.

Regards,
Allen Chen
Microsoft Online Support
 
A

Allen Chen [MSFT]

Hi Andrew,

Have you tested my code? Is it what you need?

Regards,
Allen Chen
Microsoft Online Community Support
 
A

Andrew Jocelyn

Hi Allen

Sorry it's taken me a while to reply to this. I thought I would just send
you an update. I have used some of your suggestions to extend the
ReorderList, adding new Header/Footer templates and applying a similar
visibility technique but I decided that creating a new custom template for
my application was a lot of work. Instead I created some custom controls
which can work within the ReorderList, but also a Repeater and other
controls if I need to.

I do have a question regardin the ReorderList you may be able to help me
with. I wonder if it's possible to change the size (height) of the
ReorderTemplate to match the size of the ItemTemplate? At the moment if the
ItemTemplates vary in size the ReorderTemplate is fixed whch looks a bit odd
when dragging Items. If you have any ideas how I can get round this I'd
appreciate it.

Many thanks
Andrew
 
A

Allen Chen [MSFT]

Hi Andrew,

The suggestion I could provide is, you can get the ClientID of the control
in the ReorderTemplate. After that hook the client side onmousedown event
to the control in the ItemTemplate. Then in the JavaScript function that
handles onmousedown, change the size of the control in the ReorderTemplate
to make it the same with that of the event sender.

AjaxControlToolkit is following Microsoft Public License (Ms-PL), which is
out of the support boundaries of our managed newsgroups. If you have
further questions please post it to http://forums.asp.net/1022.aspx. It is
not managed, but this
forum will provide support for AjaxControlToolkit professionally.

AjaxControlToolkit License:
http://www.codeplex.com/AjaxControlToolkit/license

MSDN Newsgroup Support Boundary:
http://blogs.msdn.com/msdnts/archive/2006/11/08/msdn-service-introduction.as
px

Regards,
Allen Chen
Microsoft Online Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top