Sheer UI: 3 - Syntax and Controls

January 05, 2014

The previous articles explained what Sheer UI was and how to do basic integrations into your system but what else can it do? There's a good deal of syntax that is supported and a number of useful controls at your disposal.

Extension Tags

These are custom tags defined in the XamlSharp.config that modify the local page. They're unique only to the "xamlControls". They can provide action elements with logic statements similar to XSL. You also have the ability to reference class libraries to support custom web controls, set variables and attach attribute values to the existing tags. Here's what they look like:

attribute 

Attaches an attribute and value to the parent tag

<x:attribute runat="server" name="type" value="value"/>

include

Includes another control into this control as if they were the same control.

<x:include runat="server" href="Sitecore.Web.Controls.Properties" />

param

Allows you to set parameter values inside a control

<x:param name="Icon" value="Applications/32x32/about.png" />

register

Includes a reference to support controls from non-referenced libraries

<x:register assembly="ComponentArt.Web.UI" namespace="ComponentArt.Web.UI"/>

styleattribute

Adds the css style to a style attribute on a parent tag

<x:styleattribute runat="server" name="text-align" value="{Align}"/>

Style

Sets a style tag with the class and properties

<x:Style x:Key="MyStyle">
      <Setter Property="Foreground" Value="green" />
</x:Style>
<x:style runat="server" key=".MyStyle">
      <Foreground>green</Foreground>
</x:style>

Using

this is similar to the import statement on a page control

<x:using runat="server" namespace="Sitecore.Controls" />

XSL-like control tags

<x:template name="testTemplate">
<x:variable name="Color" value="lime"/>
<x:set-variable name="OutsideScope" value="{Color2}" />
<x:for-each select="/sitecore/system/*">
<x:value-of select="@@name"/>
<x:if test="$Color = 'blue'">

Parameters

Sitecore provides a structure for passing values into an XML control. To do so, you use the attributes on the instance of that control. When the value is retrieved on the control definition, it can only be used inside another attribute. This means if you wanted to pass in some page text, you'd have to use a Literal control to display it. Here's an example demonstrating the control definition retrieving values:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <ChildControl>
          <div class="$ClassName">
               <Literal Text="$TextValue" runat="server"/>
          </div>     
     </ChildControl>
</xamlControls>

And here is how to pass the values in through the instance control's attributes:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <ParentControl>
          <ChildControl ClassName="someClass" TextValue="Some Text"></ChildControl>
     </ParentControl>
</xamlControls> 

Another example, using a different syntax to query for the parameters, passes in the Title and Background values for the Sitecore.Controls.HtmlPage. On the Sitecore.Controls.HtmlPage definition they are referenced using the like so (I've shortened it for the example):

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <Sitecore.Controls.HtmlPage>
          <x:param name="Title" value="Sitecore" />                                
          <x:param name="Background" />
          <html>                          
               <html:Head runat="server">          
                    <html:Title runat="server" Text="{Title}" />
               </html:Head>
               <HtmlBody runat="server">
                   <x:styleattribute runat="server" name="overflow" value="{Overflow}" />
               </HtmlBody>
          </html>
     </Sitecore.Controls.HtmlPage>
</xamlControls>

And here is an example of using these parameters to the instance control:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <Sitecore.Controls.HtmlPage runat="server" Title="Page Title" Background="url(/sitecore/shell/themes/backgrounds/building.jpg)"/>
</xamlControls>

Attribute Evaluators / Expressions

Expressions are similar to the <% %> .NET syntax. The expression within will be evaluated at runtime. In the control, the expression is inserted directly into the code, so any valid C# code can be used. Similar to the parameters though, this can only be used inside the value of an attribute.

Here's the example from the original documentation on how to use this:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <ColoredText>
          <Border Style='color:${StringUtil.GetString($Color, "red")}'>
               <Literal Text="$Text"/>
          </Border>
     <ColoredText>
</xamlControls>

And here is how you would create an instance of this control and pass in the parameters:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <TextSample>
          <ColoredText Color="blue" Text="Blue Text"/>
          <ColoredText Color="yellow" Text="Yellow Text"/>
     </TextSample>
</xamlControls>

Attribute Assigners

There are attributes that can be used on controls that aren't properties of that control. They are supported by Sitecore's compiler to allow you greater flexibility with the controls you're working with. The syntax for using them on a "control" is slightly different than using then on a "xamlControls" item and I'm not sure all supported attributes are the same for both types.

On a "control" you'd setup the namespace and reference it like so:

<control xmlns:def="Definition">   
     <SampleControl>
          <Literal def:AttributeName="value"/>
     </SampleControl>
</control>

With "xamlControls" you'd reference them like:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <SampleControl>
          <Literal x:AttributeName="value" runat="server" />
      </SampleControl>
</xamlControls>

I recommend using the same conventions provided in previous examples for consistency. I don't know all available attributes but here's a few samples:

x:ID

The ID property is used to help manage unique control ID's. Let's say you had a SampleControl that looked like this:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <SampleControl>
          <Literal ID="ltlOut"/>
     </SampleControl>
</xamlControls>

If you were to include this control twice on another control like this:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <InstanceControl>
          <SampleControl/>
          <SampleControl/>
     </InstanceControl>
</xamlControls>

This will cause an exception to be thrown: Multiple controls with the same ID 'ltlOut' were found. FindControl requires that controls have unique IDs.

To fix this you'll need to add the prefix to the ID attribute which will allow the rendered ID to be unique to the control. The result would look like this:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <SampleControl>
          <Literal x:ID="ltlOut"/>
     </SampleControl>
</xamlControls>

x:inherits

The inherits attribute allows you to define which class is used to represent the control. This can be used on controls who already have a class file defined allowing you to override it. The "xamlControls" in the previous example used this like so:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <XamlHelloWorld x:inherits="MyNamespace.XamlHelloWorld,MyAssembly">
</xamlControls>

x:Placeholder

This attribute tells the rendering engine which placeholder control it should be added to as a child control. A common example would be when you use the Sitecore.Controls.HtmlPage. On this file you'll see this placeholder inside the <html:head>

<Placeholder runat="server" key="Stylesheets"/>

and when you use this page you'd reference it like so:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <Sitecore.Controls.HtmlPage runat="server">
          <Stylesheet runat="server" Src="style.css" x:placeholder="Stylesheets" />
     </Sitecore.Controls.HtmlPage>
</xamlControls>

Controls

With the controls provided by Sitecore out of the box there's two general categories of controls: There's structural (like tables, divs and menus) and there's functional (like form inputs).

Border 

This outputs a div around an whatever it is wrapping.

GridPanel 

This will output any child controls inside a table structure. If you set the number of columns it will fill each table division with a control until the next line.

Scrollbox 

This outputs your controls into a div whose height can be specified and sets the overflow to scroll.

Ribbon

Placing a ribbon on your page requires a few extra step. There's, of course, more than one way to do this but here's the way I recommend. I'm registering the <Ribbon> tag by using the <x:register> tag to identify the library it exists in. I'm also including the css file to make sure its displayed correctly. The XML looks like this:

<xamlControls xmlns:x="http://www.sitecore.net/xaml">
     <XamlRibbonExample x:inherits="MyLibrary.RibbonExample,MyAssembly">
          <Sitecore.Controls.HtmlPage runat="server">
               <x:register assembly="Sitecore.Kernel" namespace="Sitecore.Web.UI.WebControls.Ribbons"/>
               <link rel="stylesheet" href="/sitecore/shell/Themes/Standard/Default/Ribbon.css" /> 
               <Ribbon ID="Ribbon" runat="server" />
          </Sitecore.Controls.HtmlPage>
      <XamlRibbonExample>
<xamlControls>

The class will implement the IHasCommandContext interface to tell Sitecore that there is a CommandContext to use. The CommandContext is used to set the Uri of the item in the core database that represents the ribbon. In this example, I'm just using the same ribbon from the package designer. The class would look like:

using System;
using System.Web;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.XamlSharp.Xaml;

namespace testsite.library {
     public class RibbonExample : XamlMainControl, IHasCommandContext {
         
          protected override void OnLoad(EventArgs e) {
               Assert.ArgumentNotNull((object)e, "e");
               base.OnLoad(e);
          }

          CommandContext IHasCommandContext.GetCommandContext() {
               Item itemNotNull = Client.CoreDatabase.GetItem("/sitecore/content/Applications/Tools/Installer/Designer/Ribbon");
               CommandContext commandContext = new CommandContext();
               commandContext.RibbonSourceUri = itemNotNull.Uri;
               return commandContext;
          }
     }
}

There are other notable controls that I won't provide examples for such as the Edit (TextBox), Combobox (DropDownList) but they are powerful controls that allow you to build some really slick applications.

Exceptions

When you're working through development you may experience different exceptions or issues that aren't clear what the problem is. Here's a few I caught while working:

Parameter name: parent Sitecore.Web.UI.Sheer.ClientPage.AddControl Value cannot be null. Parameter name: parent

If you're getting this exception, you might not be setting a value on a control that should be set. I've hit this a few times using the TreePicker when I didn't set the DataContext Folder property. The DataContext usually needs to have the GetFromQueryString() method called, the Root and Folder properties set.

The resource cannot be found 

This can occur if you're trying to view the control and you're not logged in or there is a compiler error with your xml control. Sometimes if you try to call another control, you might be able to get a different exception that is closer to identifying the issue.

There are still a number of other topics which I didn't have time to research and write about such as persistence (short and long term storage), events and messaging and, of course, how to build a wizard. If you plan on trying to build an application using Sheer, peruse the tutorial information I mentioned in the first article and even how look over how Sitecore builds the applications within itself and you should be able to follow the breadcrumbs and put the pieces together.