
Creating a Sheer UI application using XAML control to list products
Extensible Application Markup Language (XAML) is a declarative markup language used to create user interfaces. There are two types of XAML control frameworks available in Sitecore, XML control and xamlControls. In this recipe, we will create the Sheer UI application using XML control and, based on this, you will be able to create an application using xamlControls as well. You can get some basic information about both from https://goo.gl/4WWeVw.
Sitecore has built a new framework, SPEAK, which is a replacement for the XAML/Sheer UI, so we will not focus much on it. However, it's good to know how it works so that it can be helpful in customizing existing applications. Here, we will create an application to list all products as well as delete selected products. You will learn how to use different controls, invoke methods, handle messages, and create events in XML control.
How to do it…
We will first create an XML layout for the Sheer UI application:
- Create an XML file named
ProductList.xml
in the\sitecore\shell\Applications\Cookbook\
folder, and place the following contents in it:<?xml version="1.0" encoding="utf-8" ?> <control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense"> <ProductListing> <FormDialog Icon="People/16x16/clock_run.png" Header="Product List" Text="View all the products"> <CodeBeside Type="SitecoreCookbook.XAML.ProductListPage,SitecoreCookbook"/> <Stylesheet Src="/sitecore/shell/Applications/Content Manager/Dialogs/Properties/Properties.css" /> <GridPanel ID="Viewer" Height="315" class="scBackground" vAlign="top" Width="100%" Cellpadding="5"> </GridPanel> </FormDialog> </ProductListing> </control>
- Now, find the
<GridPanel>
node in the preceding code, and placeListView
to list products as follows:<Border align="center"> <Scrollbox ID="Summary" Width="100%" Height="300" Style="padding:14px 4px 0px 4px; border-width:1px; border-style:solid"> <Listview ID="ProductList" View="Details" Width="100%" Background="white" MultiSelect="true"> <ListviewHeader> <ListviewHeaderItem Name="Title" Header="Title" /> <ListviewHeaderItem Name="Price" Header="Price" /> <ListviewHeaderItem Name="Id" Header="Item Id" /> </ListviewHeader> </Listview> </Scrollbox> </Border>
- Now, we will place two button controls (to delete the selected product) in the
<GridPanel>
node. One button will get invoked by a method namedDeleteProducts
and the other by a message namedproduct:delete
:<Border align="left"> <Button Header="Delete with method" Click="DeleteProducts" /> <Button Header="Delete with message" Click="product:delete" /> </Border>
- Now we will do the coding part to list products. In the
SitecoreCookbook
project, create aProductListPage
class in theXAML
folder, and inherit it fromSitecore.Web.UI.Pages.DialogForm
:public class ProductListPage : DialogForm { protected GridPanel Viewer; protected Button btnDelete; protected Listview ProductList; }
- Create a
LoadProducts()
method in the class to list all products and add them to the current page as an object ofListViewItem
:private void LoadProducts() { string ProductsPath = "/sitecore/Content/Home/Products/Phones"; Item products = Factory.GetDatabase("master").GetItem(ProductsPath); foreach (Item product in products.Children) { ListviewItem productItem = new ListviewItem(); Context.ClientPage.AddControl(ProductList, productItem); productItem.ID = Control.GetUniqueID("I"); productItem.Header = product["Title"]; productItem.ColumnValues["Id"] = product.ID.ToString(); productItem.ColumnValues["Title"] = product["Title"]; productItem.ColumnValues["Price"] = product["Price"]; } }
- Now, override the
OnLoad()
event of theDialogForm
class to invoke the precedingLoadProducts()
method. Here,ClientPage.IsEvent
works the same as Webform'sPage.IsPostBack
. So, products will be loaded on the first request of the dialog:protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (!Context.ClientPage.IsEvent) { LoadProducts(); this.OK.Visible = false; this.Cancel.Value = "Close"; } }
- Build the project and open our XAML application using the following URL:
http://sitecorecookbook/sitecore/shell/default.aspx?xmlcontrol=ProductListing
You can find a list of products, as shown in the following image:
XAML provides different types of events that we can achieve using invoking methods or passing messages to the event. Here, we will use both approaches to delete selected products.
- If you check, as per the code that we placed in the XML file in step 3, the first button is expecting the
DeleteProducts()
method, which is defined in the button'sClick
property. Here, we will define this method to delete selected products:protected void DeleteProducts() { if (ProductList.SelectedItems.Length > 0) { foreach (ListviewItem productItem in ProductList.SelectedItems) { Item product = Factory.GetDatabase("master").GetItem(new ID(productItem.ColumnValues["Id"].ToString())); product.Recycle(); } ProductList.Controls.Clear(); LoadProducts(); ProductList.Refresh(); SheerResponse.Alert("Selected products are deleted!"); } else SheerResponse.Alert("No product selected!"); }
- The second button is expecting the application to handle the
product:delete
message on its click, which is again defined in theClick
property of the button. We can handle this message by overriding theHandleMes
sage()
method as follows:public override void HandleMessage(Message message) { if (message.Name == "product:delete") DeleteProducts(); }
- Run this application again and select one or more products. Click on any button to delete the product.
How it works…
We created a XAML dialog using an XML file. In the XML, we defined a <ProductListing>
tag after the <control>
tag, which describes the name of the control. The control name is the identity of the execution of this application and should be unique across the Sitecore solution. While building the solution, the XML file also gets compiled, and while accessing the XAML application, it finds the XML file where the requested control name is defined.
We can create multiple controls like this, but Sitecore recommends creating one control per XML file. Sitecore stores its controls in two locations: /sitecore/shell/Applications
and /sitecore/shell/Controls
. If you want to override any existing Sitecore control, you can place the overridden XML file in the /sitecore/shell/override
folder to separate it from Sitecore's original copy. If you want to place your custom XML file outside of these directories, you have to register it in <controlSources>
in the \App_Config\Sitecore.config
file as follows:
<source mode="on" namespace="SitecoreCookbook.XAML" folder="/SitecoreCookbook" deep="true"/>
In the XML file, the Type
attribute in <CodeBeside>
expects assembly details, which means where the server-side code will get executed.
Steps 7 and 8 show you how to invoke a method and pass a message to delete selected products. Both the method and message can be used in any event of buttons or controls, like we used it in button's Click
event:
<Button Header="Delete with method" Click="Delete Products" /> <Button Header="Pass with message" Click="product:delete" />
There's more…
You learned how to invoke a method and pass a message to respond to user interaction. We can use events such as Button.OnClick += new EventHandler(BesideButton_OnClick);
in the same way.
Instead of the HandleMessage()
method, we can also handle a message with a custom method by just adding the HandleMessageAttribute
attribute to this method, passing a relevant message to its constructor such as the following:
[HandleMessage("product:delete")] public void DeleteProductMethod(Message message){}
State management is also possible in XAML to persist some values. We can achieve this using the ServerSettings
property in the ClientPage
class. We can use Context.ClientPage.ServerProperties["PersistentValue"]
to persist information.
The xamlControls needs the <xamlControls>
node unlike the XML controls, which use the <controls>
node. It also needs a file extension, such as .xaml.xml
, unlike .xml
for XML controls.
In XML controls, we create a control name such as <ProductListing>
, while in xamlControls, we have to use <SitecoreCookbook.XAML.ProductListing>
as a control name.
XML controls can be viewed at the following URL:
http://sitecorecookbook/sitecore/shell/default.aspx?xmlcontrol=<ControlName>
xamlControls can be viewed at the following URL:
http://sitecorecookbook/sitecore/shell/~/xaml/<ControlName>.aspx
The structure of xamlControls is slightly different from XML controls, as follows:
<xamlControls xmlns:x="http://www.sitecore.net/xaml"> <SitecoreCookbook.XAML.ProductsListing x:inherits="SitecoreCookbook.XAML.ProductListPage, SitecoreCookbook"> </SitecoreCookbook.XAML.SitecoreSitesList> </xamlControls>
We can also create a wizard using XAML controls such as a template creation wizard. You can read more about this at https://goo.gl/pL1npo.