The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
<
div
>
<
p
>
<
strong
>Categories:</
strong
>
<
asp:Repeater
ID
=
"CategoriesRepeater"
runat
=
"server"
>
<
HeaderTemplate
><
ul
></
HeaderTemplate
>
<
ItemTemplate
>
<
li
><%# Container.DataItem %></
li
>
</
ItemTemplate
>
<
FooterTemplate
></
ul
></
FooterTemplate
>
</
asp:Repeater
>
</
p
>
</
div
>
<
div
>
<
p
>
<
strong
>Tags:</
strong
>
<
asp:Repeater
ID
=
"TagsRepeater"
runat
=
"server"
>
<
HeaderTemplate
><
ul
></
HeaderTemplate
>
<
ItemTemplate
>
<
li
><%# Container.DataItem %></
li
>
</
ItemTemplate
>
<
FooterTemplate
></
ul
></
FooterTemplate
>
</
asp:Repeater
>
</
p
>
</
div
>
Add the following properties to your Widget’s code-behind file:
/// <summary>
/// guid array from designer for our selected tags
/// </summary>
public
Guid[] selectedTags;
/// <summary>
/// Gets or sets the selected tags.
/// </summary>
public
Guid[] SelectedTags
{
get
{
if
(selectedTags ==
null
) selectedTags =
new
Guid[] { };
return
selectedTags;
}
set
{ selectedTags = value; }
}
/// <summary>
/// guid array from designer for our selected categories
/// </summary>
public
Guid[] selectedCategories;
/// <summary>
/// Gets or sets the selected categories.
/// </summary>
/// <value>
/// The selected categories.
/// </value>
public
Guid[] SelectedCategories
{
get
{
if
(selectedCategories ==
null
) selectedCategories =
new
Guid[] { };
return
selectedCategories;
}
set
{ selectedCategories = value; }
}
/// <summary>
/// Intermediary property for passing Tags to and from the designer
/// </summary>
/// <value>
/// The tag value as a comma delimited string.
/// </value>
public
string
TagValue
{
get
{
return
string
.Join(
","
, SelectedTags); }
set
{
var list =
new
List<Guid>();
if
(value !=
null
)
{
var guids = value.Split(
','
);
foreach
(var guid
in
guids)
{
Guid newGuid;
if
(Guid.TryParse(guid,
out
newGuid))
list.Add(newGuid);
}
}
SelectedTags = list.ToArray();
}
}
/// <summary>
/// Intermediary property for passing categories to and from the designer
/// </summary>
/// <value>
/// The category value as a comma-delimited string.
/// </value>
public
string
CategoryValue
{
get
{
return
string
.Join(
","
, SelectedCategories); }
set
{
var list =
new
List<Guid>();
if
(value !=
null
)
{
var guids = value.Split(
','
);
foreach
(var guid
in
guids)
{
Guid newGuid;
if
(Guid.TryParse(guid,
out
newGuid))
list.Add(newGuid);
}
}
SelectedCategories = list.ToArray();
}
}
Add the following Control References to your Widget’s code-behind file:
/// <summary>
/// Reference to the Label control that shows the Message.
/// </summary>
protected
virtual
Repeater TagsRepeater
{
get
{
return
this
.Container.GetControl<Repeater>(
"TagsRepeater"
,
true
);
}
}
/// <summary>
/// Reference to the Label control that shows the Message.
/// </summary>
protected
virtual
Repeater CategoriesRepeater
{
get
{
return
this
.Container.GetControl<Repeater>(
"CategoriesRepeater"
,
true
);
}
}
Add the following method calls to your Widget’s code-behind InitializeControls method:
BindTags();
BindCategories();
Add the following two methods to your Widget’s code-behind:
/// <summary>
/// Binds the selected tags to your tags control
/// </summary>
private
void
BindTags()
{
// retrieve selected tags
var tags =
new
List<
string
>();
foreach
(var tagID
in
SelectedTags)
{
// get tag name
var tag = taxMgr.GetTaxon(tagID);
if
(tag ==
null
)
continue
;
tags.Add(tag.Name);
}
// bind tag names
TagsRepeater.DataSource = tags;
TagsRepeater.DataBind();
}
/// <summary>
/// Binds the selected tags to your categories control
/// </summary>
private
void
BindCategories()
{
// retrieve selected categories
var tags =
new
List<
string
>();
foreach
(var tagID
in
SelectedCategories)
{
// get category name
var tag = taxMgr.GetTaxon(tagID);
if
(tag ==
null
)
continue
;
tags.Add(tag.Name);
}
// bind category names
CategoriesRepeater.DataSource = tags;
CategoriesRepeater.DataBind();
}
This is the first time we will be touching the Designer code as Thunder took care of that for us previously. It’s a little different, because it has different responsibilities, and has an associated javascript file, but in the end it’s just a custom Widget.
Open your custom Designer’s UI file and add the following controls to it:
<%-- Tag section --%>
<
li
>
<
sitefinity:FlatTaxonField
ID
=
"TagsSelector"
runat
=
"server"
DisplayMode
=
"Write"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/FlatTaxon.svc/cb0f3a19-a211-48a7-88ec-77495c0f5374"
TaxonomyMetafieldName
=
"Tags"
AllowMultipleSelection
=
"true"
Expanded
=
"false"
Title
=
"Tags"
/>
</
li
>
<
li
class
=
"sfFormCtrl"
>
<
asp:HiddenField
ID
=
"TagValue"
runat
=
"server"
/>
</
li
>
<%-- Category section --%>
<
li
>
<
label
class
=
"sfTxtLbl"
>Category:</
label
>
<
sitefinity:HierarchicalTaxonField
ID
=
"CategoriesSelector"
runat
=
"server"
DisplayMode
=
"Write"
Expanded
=
"false"
ExpandText
=
"ClickToAddCategories"
ShowDoneSelectingButton
=
"true"
AllowMultipleSelection
=
"true"
BindOnServer
=
"false"
TaxonomyMetafieldName
=
"Category"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc"
/>
</
li
>
<
li
class
=
"sfFormCtrl"
>
<
asp:HiddenField
ID
=
"CategoryValue"
runat
=
"server"
/>
</
li
>
Make sure you have the following Using statements to your Designer’s code-behind file:
Now add the following Control References to the Designer’s code-behind:
/// <summary>
/// Gets the control that is bound to the TagValue property
/// </summary>
protected
virtual
Control TagValue
{
get
{
return
this
.Container.GetControl<Control>(
"TagValue"
,
true
);
}
}
/// <summary>
/// Gets the tag selector control
/// </summary>
protected
FlatTaxonField TagsSelector
{
get
{
return
Container.GetControl<FlatTaxonField>(
"TagsSelector"
,
true
); }
}
/// <summary>
/// Gets the control that is bound to the CategoryValue property
/// </summary>
protected
virtual
Control CategoryValue
{
get
{
return
this
.Container.GetControl<Control>(
"CategoryValue"
,
true
);
}
}
/// <summary>
/// Gets the category selector control
/// </summary>
protected
HierarchicalTaxonField CategoriesSelector
{
get
{
return
Container.GetControl<HierarchicalTaxonField>(
"CategoriesSelector"
,
true
); }
}
Now add two method calls to your Designer’s code-behind InitializeControls method:
// initialize the taxonomy selectors
CategoriesSelector.TaxonomyId = TaxonomyManager.CategoriesTaxonomyId;
TagsSelector.TaxonomyId = TaxonomyManager.TagsTaxonomyId;
Now add the following component and element property descriptors within the IScriptControl implementation Region:
descriptor.AddElementProperty(
"tagValue"
,
this
.TagValue.ClientID);
descriptor.AddComponentProperty(
"TagsSelector"
,
this
.TagsSelector.ClientID);
descriptor.AddElementProperty(
"categoryValue"
,
this
.CategoryValue.ClientID);
descriptor.AddComponentProperty(
"CategoriesSelector"
,
this
.CategoriesSelector.ClientID);
That’s it for the Designer UI and code-behind file. Now we’ll start editing the JavaScript file.
Add the following JavaScript variables:
this
._TagsSelector =
null
;
this
._tagValue =
null
;
this
._CategoriesSelector =
null
;
this
._categoryValue =
null
;
Add the following items to the bottom of the refreshUI function:
/* RefreshUI TagValue */
jQuery(
this
.get_tagValue()).val(controlData.TagValue);
/* RefreshUI CategoryValue */
jQuery(
this
.get_categoryValue()).val(controlData.CategoryValue);
// load tags
var
t =
this
.get_TagsSelector();
var
tags = controlData.TagValue;
if
(tags !=
null
)
t.set_value(controlData.TagValue.split(
","
));
// load categories
var
c =
this
.get_CategoriesSelector();
var
cats = controlData.CategoryValue;
if
(cats !=
null
)
c.set_value(controlData.CategoryValue.split(
","
));
Now add the following items to the bottom of the applyChanges function:
/* ApplyChanges SelectedPageID */
/* ApplyChanges TagValue */
controlData.TagValue = jQuery(
this
.get_tagValue()).val();
/* ApplyChanges CategoryValue */
controlData.CategoryValue = jQuery(
this
.get_categoryValue()).val();
// save selected tags
var
t =
this
.get_TagsSelector();
var
tags = t.get_value();
if
(tags !=
null
)
controlData.TagValue = t.get_value().join();
// save selected categories
var
c =
this
.get_CategoriesSelector();
var
cats = c.get_value();
if
(cats !=
null
)
controlData.CategoryValue = c.get_value().join();
Now add the following properties to the existing Properties section:
/* TagValue properties */
get_tagValue:
function
() {
return
this
._tagValue; },
set_tagValue:
function
(value) {
this
._tagValue = value; },
/* CategoryValue properties */
get_categoryValue:
function
() {
return
this
._categoryValue; },
set_categoryValue:
function
(value) {
this
._categoryValue = value; },
// Categories Selector
get_CategoriesSelector:
function
() {
return
this
._CategoriesSelector; },
set_CategoriesSelector:
function
(value) {
this
._CategoriesSelector = value; },
// Tags Selector
get_TagsSelector:
function
() {
return
this
._TagsSelector; },
set_TagsSelector:
function
(value) {
this
._TagsSelector = value; }
Compile your project one final time and use to your heart’s content!
Hopefully everyone found this a useful exercise and this sheds more insight into how Designers are made and what’s going on under the hood. Once you see the patterns at play here you will no doubt be able to apply your own custom controls and functionality to your Sitefinity Designers.
Though Designers aren’t necessary in all instances when creating custom widgets, they can be a very powerful tool for your business users, because, once setup, they allow the business user to customize their content display properties without the need of a developer. And as we all know a happy business user makes for a happy developer!
Please go to my Github Repository to download the sample project.
View all posts from David Cowart on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.