Generate a Visualforce Page from an Existing Page Layout

February 9th, 2009

We have a rather large Org with 600+ Page Layouts. When Visualforce came out we had a number of requests from our business units for customized layouts. After about 3 weeks of hand-coding Visualforce pages I soon realized I needed a better way to communicate these change as well as manage the code modifications. Being that I am relatively lazy by nature, I decided that I needed to develop a way to make my computer do the work that I didn’t want to do.

Generate Visualforce Pages

The C# code that I wrote utilizes the Metadata API and inspects the Page Layout for a specified Object and Recordtype and generates similar Visualforce code that you can paste into the Force.com IDE. Is it perfect? No. Could it have been done better? Yes. Does it save me time, money and aggravation? Most certainly!

It also saves me time during the change management process. Now when a BA or Project Manager modifies an existing Page Layout that I have used for a Visualforce page, I don’t need documentation on what fields have changed, I just point my handy-dandy generator at the Page Layout and regenerate the code.

Default.aspx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Visualforce_Scaffolding._Default" %>
 
<!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>Visualforce Scaffolding Generator</title>
	<style type="text/css">
	.style1
	{
	    height: 25px;
	}
	</style>
</head>
<body>
	<form id="form1" runat="server">
<div>
<h1>Visualforce Scaffolding Generator</h1>
<asp:Label ID="Label3" runat="server" Text="This page generates the Visualforce code for a specific object and recordtye. The code inspects the page layout for the recordtype and creates a very similar replica of the sections and field layouts as Visualforce code. You can then take this code and paste it into a Visualforce page with minimal changes."/>
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td><asp:Label ID="Label1" runat="server" Text="Object"></asp:Label></td>
<td><asp:TextBox ID="txtObject" runat="server" Width="200px">Account</asp:TextBox></td>
</tr>
<tr>
<td><asp:Label ID="Label2" runat="server" Text="RecordType ID"></asp:Label></td>
<td><asp:TextBox ID="txtRecordTypeId" runat="server" Width="200px">01260000000Dxxx</asp:TextBox></td>
</tr>
<tr>
<td class="style1">Page Type</td>
<td class="style1">
				<asp:DropDownList ID="ddlMode" runat="server">
				<asp:ListItem Value="Edit">New/Edit</asp:ListItem>
				<asp:ListItem>Display</asp:ListItem>
				</asp:DropDownList></td>
</tr>
<tr>
<td></td>
<td><asp:Button ID="Button1" runat="server" Text="Generate Code" onclick="Button1_Click" /></td>
</tr>
</table>
</div>
<asp:Literal ID="Code" runat="server"></asp:Literal>
 
	</form>
</body>
</html>

Here is the code behind for Default.aspx.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Visualforce_Scaffolding.sforce;
 
namespace Visualforce_Scaffolding
{
    public partial class _Default : System.Web.UI.Page
    {
 
        private string username;
        private string password;
        private string sessionId;
        private string serverUrl;
        private SforceService proxy;
 
        protected void Page_Load(object sender, EventArgs e)
        {
            proxy = new sforce.SforceService();
            if (Request.QueryString["sessionId"] != null)
            {
                sessionId = Request.QueryString["sessionId"].ToString();
                serverUrl = Request.QueryString["url"].ToString();
            }
            else
            {
                // hardcode the u/p for development
                username = "YOUR_USERNAME";
                password = "YOUR_PASSWORD";
            }
        }
 
        private void output(string s, int tabs)
        {
            // fix some characters for html output
            s = s.Replace("<", "&#60");
            s = s.Replace(">", "&#62");
 
            for (int j = 0; j < tabs; j++)
            {
                Code.Text = Code.Text + ("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                //Response.Write("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
            }
 
            Code.Text = Code.Text + (s);
            Code.Text = Code.Text + ("[br]");
        }
 
        public void generateDisplayPageLayout(string objectToDescribe, string recordType)
        {
            output("<apex:page standardController=\"" + txtObject.Text + "\">", 0);
            output("<apex:sectionHeader title=\"" + txtObject.Text + "\" subtitle=\"{!" + txtObject.Text.ToLower() + ".name}\"/>", 1);
            output("<apex:pageBlock title=\"" + txtObject.Text + "\">", 1);
 
            try
            {
                sforce.DescribeLayoutResult dlr = proxy.describeLayout(objectToDescribe, new string[] { recordType });
 
                // get all of the fields that are in this layout
                for (int i = 0; i < dlr.layouts.Length; i++)
                {
                    sforce.DescribeLayout layout = dlr.layouts[i];
                    if (layout.editLayoutSections != null)
                    {
                        for (int j = 0; j < layout.editLayoutSections.Length; j++)
                        {
                            sforce.DescribeLayoutSection els = layout.editLayoutSections[j];
 
                            output("", 2);
                            output("<apex:pageBlockSection title=\"" + els.heading + "\" columns=\"" + els.columns + "\">", 2);
 
                            for (int k = 0; k < els.layoutRows.Length; k++)
                            {
                                sforce.DescribeLayoutRow lr = els.layoutRows[k];
                                for (int h = 0; h < lr.layoutItems.Length; h++)
                                {
                                    sforce.DescribeLayoutItem li = lr.layoutItems[h];
                                    if (li.layoutComponents != null)
                                    {
                                        output("<apex:outputField title=\"" + li.label + "\" value=\"{!" + txtObject.Text.ToLower() + "." + li.layoutComponents[0].value + "}\"/>", 3);
                                        //Response.Write(" " + h + " " + li.layoutComponents[0].value + "(editable: " + li.editable + ") label: " + li.label + " required: " + li.required + "[br]");
                                    }
 
                                }
                            }
 
                            output("</apex:pageBlockSection>", 2);
 
                        }
                    }
                }
 
            }
            catch (Exception e)
            {
                Response.Write("An exceptions was caught: " + e.Message + "[br]");
            }
 
            output("</apex:pageBlock>", 1);
            output("</apex:page>", 0);
 
        }
 
        public void generateEditPageLayout(string objectToDescribe, string recordType)
        {
            output("<apex:page standardController=\"" + txtObject.Text + "\">", 0);
            output("<apex:sectionHeader title=\"" + txtObject.Text + " Edit\" subtitle=\"{!" + txtObject.Text.ToLower() + ".name}\"/>", 1);
            output("<apex:form>", 1);
            output("<apex:pageBlock title=\"" + txtObject.Text + " Edit\" mode=\"edit\">", 1);
 
            output("", 2);
 
            output("<apex:pageBlockButtons location=\"top\">", 2);
            output("<apex:commandButton value=\"Save\" action=\"{!save}\" />", 3);
            output("<apex:commandButton value=\"Save & New\" action=\"{!save}\" />", 3);
            output("<apex:commandButton value=\"Cancel\" action=\"{!cancel}\" />", 3);
            output("</apex:pageBlockButtons>", 2);
 
            output("<apex:pageBlockButtons location=\"bottom\">", 2);
            output("<apex:commandButton value=\"Save\" action=\"{!save}\" />", 3);
            output("<apex:commandButton value=\"Save & New\" action=\"{!save}\" />", 3);
            output("<apex:commandButton value=\"Cancel\" action=\"{!cancel}\" />", 3);
            output("</apex:pageBlockButtons>", 2);
 
            try
            {
                sforce.DescribeLayoutResult dlr = proxy.describeLayout(objectToDescribe, new string[] { recordType });
 
                // get all of the fields that are in this layout
                for (int i = 0; i < dlr.layouts.Length; i++)
                {
                    sforce.DescribeLayout layout = dlr.layouts[i];
                    if (layout.editLayoutSections != null)
                    {
                        for (int j = 0; j < layout.editLayoutSections.Length; j++)
                        {
                            sforce.DescribeLayoutSection els = layout.editLayoutSections[j];
 
                            output("", 2);
                            output("<apex:pageBlockSection title=\"" + els.heading + "\" columns=\"" + els.columns + "\">", 2);
 
                            for (int k = 0; k < els.layoutRows.Length; k++)
                            {
                                sforce.DescribeLayoutRow lr = els.layoutRows[k];
                                for (int h = 0; h < lr.layoutItems.Length; h++)
                                {
                                    sforce.DescribeLayoutItem li = lr.layoutItems[h];
                                    if (li.layoutComponents != null)
                                    {
                                        output("<apex:inputField value=\"{!" + txtObject.Text.ToLower() + "." + li.layoutComponents[0].value + "}\" required=\"" + li.required.ToString().ToLower() + "\"/>", 3);
                                        //Response.Write(" " + h + " " + li.layoutComponents[0].value + "(editable: " + li.editable + ") label: " + li.label + " required: " + li.required + "[br]");
                                    }
 
                                }
                            }
 
                            output("</apex:pageBlockSection>", 2);
 
                        }
                    }
                }
 
            }
            catch (Exception e)
            {
                Response.Write("An exceptions was caught: " + e.Message + "[br]");
            }
 
            output("</apex:pageBlock>", 1);
            output("</apex:form>", 1);
            output("</apex:page>", 0);
 
        }
 
        public bool login()
        {
 
            // use the existing session from the org
            if (sessionId != null)
            {
 
                try
                {
                    Response.Write("Using existing session[br]");
                    proxy.Url = serverUrl;
                    proxy.SessionHeaderValue = new sforce.SessionHeader();
                    proxy.SessionHeaderValue.sessionId = sessionId;
 
                    GetUserInfoResult userInfo = proxy.getUserInfo();
                    Response.Write("Logged in as " + userInfo.userFullName + " - " + userInfo.userName + "<hr />");
 
                }
                catch (Exception ex)
                {
                    Response.Write("Error logging in with session: " + ex.Message + "[br]");
                    return false;
                }
 
                return true;
 
            }
            else
            {
 
                // this is for development
                Response.Write("Logging in with u/p[br]");
                sforce.LoginResult lr = proxy.login(username, password);
                if (!lr.passwordExpired)
                {
                    // Reset the SOAP endpoint to the returned server URL
                    proxy.Url = lr.serverUrl;
                    // Create a new session header object and add the session ID returned from the login
                    proxy.SessionHeaderValue = new sforce.SessionHeader();
                    proxy.SessionHeaderValue.sessionId = lr.sessionId;
 
                    GetUserInfoResult userInfo = lr.userInfo;
                    Response.Write("Logged in as " + userInfo.userFullName + " - " + userInfo.userName + "<hr />");
                }
                else
                {
                    Response.Write("Your password is expired.[br]");
                    return false;
                }
                return true;
            }
        }
 
        protected void Button1_Click(object sender, EventArgs e)
        {
            Code.Text = "";
            if (login())
            {
                Code.Text = Code.Text + "<hr />";
                if (ddlMode.Text == "Edit")
                {
                    generateEditPageLayout(txtObject.Text, txtRecordTypeId.Text);
                }
                else
                {
                    generateDisplayPageLayout(txtObject.Text, txtRecordTypeId.Text);
                }
            }
        }
 
    }
}
VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay

Categories: .NET, Code Sample, Salesforce

Leave a comment

Comments Feed8 Comments

  1. John Coppedge

    That’s pretty cool Jeff. I may have to copy/paste this bad boy down the line. Hopefully as VF matures they’ll include this sort of functionality out of the box.

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  2. RM

    Nice tool Jeff. Thanks for sharing.

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  3. Mario C

    Looks like this might come in REALLY handy for me. I tried it, but got the following Parser Error- Could not load type ‘Visualforce_Scaffolding._Default’.
    Am I missing something?

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  4. Doug

    Looks great but I am getting the same issue as Mario C.

    I am not a .net developer but I kind of think “using Visualforce_Scaffolding.sforce” means we need a file we don’t have.

    Any one have any luck getting this to run?

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  5. jeffdonthemic

    Mario, “sforce” is the name of the web reference that is created when you import the Saleforce.com WSDL into Visual Studio. I’m sure there are a number of tutorials for Visual Studio but here is one that may get you started… http://wiki.developerforce.com/index.php/Force.com_for_ASP.NET_Developers.

    Good luck!

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  6. Krishna

    I am finding the same issue even after adding the API in visual studio. Which API should be shared

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  7. osama

    Simply Amazing!

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  8. Ryan

    We purchased EasyPage to do this. It’s an AppExchange program that costs $250 a year for unlimited users and usage. Soooo glad we did.

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)

Leave a comment

Feed

http://blog.jeffdouglas.com / Generate a Visualforce Page from an Existing Page Layout

WordPress Appliance - Powered by TurnKey Linux