Advanced UniGrid example

   —   

This article gives a more advanced example on how to use the UniGrid control which is a listing control used in the user interface of Kentico. It may come in handy when building custom functionality for your site. We will define a basic UniGrid; change dynamically the editing (deleting) button image and finally we will create a custom column for editing the object.

The UniGrid is used to display and to edit the data across the user interface. Everywhere in the user interface you see a table with editing buttons you can be pretty sure, that this table is generated by a UniGrid. The UniGrid needs basically two inputs. The data you want to display and the UniGrid definition. The UniGrid definition can be defined either directly in the designer file or in an XML file.  The designer file approach is recommended and that’s why we will use it in our example.
 
 At first let’s create a UniGrid for testing purposes. You can place it for example into a new custom web part created according to our developer’s guide. At first you need to register the user control.
 

<%@ Register src="~/CMSAdminControls/UI/UniGrid/UniGrid.ascx" tagname="UniGrid" tagprefix="cms" %>

  In this example we will be using the users as testing data. So let’s declare a userGrid UniGrid:
 

<cms:UniGrid ID="userGrid" runat="server"/>


  This simple code places a Unigrid on your custom web part. The UniGrid can be populated in three different ways:

  1. You need to specify the query name in the UniGrid definition if you want to populate the UniGrid with a query. You can create the query in the CMSSiteManager / Development / Document types / <select a document type> / Queries section. You can also create a new document type which will be only a container for custom queries without any custom fields.

<cms:UniGrid ID="userGrid" runat="server" Query="cms.user.selectall"/>

  1. If you want to use the object approach, you need to define the edited object with the ObjectType attribute of the UniGrid:
<cms:UniGrid ID="userGrid" runat="server" ObjectType="cms.user" />
  1. The last approach is to populate the UniGrid in the CodeBehind file of your web part. This approach can be used for example if you need to add additional data to the displayed dataset:
userGrid.DataSource = CMS.SiteProvider.UserInfoProvider.GetAllUsers();

Now let’s check how to define the layout and functionality of a UniGrid. The first approach would be to use an XML file:

<cms:UniGrid ID="userGrid" runat="server" GridName="YourConfigurationFile.xml" />

In this case needs the file to be located in the same directory as the web part itself otherwise you need to specify the path to the file (the tilde sign is supported). There are a lot of examples through the system of such a setup. For example this approach is used in the BadWords module in the file CMSModules\BadWords\BadWords_List.aspx. You can check it out if you would like to use this approach.
 
The next step in our example is to populate the UniGrid by placing the code from the data population approach C into your web part code behind file into the Page_Load method:

protected void Page_Load(object sender, EventArgs e) { userGrid.DataSource = CMS.SiteProvider.UserInfoProvider.GetAllUsers(); }

Let’s say you want only to display two columns, the UserName and the UserID. We populate the data programmatically, so you can use the UniGrid definition directly in the designer file.  So let’s add the mentioned columns to the GridColumns section of the UniGrid definition. Please add the columns so your code will look the following way:

<%@ Register src="~/CMSAdminControls/UI/UniGrid/UniGrid.ascx" tagname="UniGrid" tagprefix="cms" %> <%@ Register Namespace="CMS.UIControls.UniGridConfig" TagPrefix="ug" Assembly="CMS.UIControls" %> <cms:UniGrid ID="userGrid" runat="server" > <GridColumns> <ug:Column Source="UserName" Caption="User name" Wrap="false"> </ug:Column> <ug:Column Source="UserID" Caption="User ID" Wrap="false"> </ug:Column> </GridColumns> </cms:UniGrid>

You can see that we had to add a new assembly so we can define the UniGrid actions. Your UniGrid should now list all the users if you access the page where your web part is placed.
 
In this example we want to be able to delete a selected entry, so we need to add a new UniGrid action.  The first column in the dataset used as an identifier of the edited entry for all UniGrid actions if not specified otherwise by the commandargument property on the Action definition (please see the referenced documentation at the end for details). In our case it’s the UserID. Additionally to distinguish between the actions which are called you need to specify the Name property (this is useful if you have multiple actions defined). This value will be later used in the switch statement.  By adding the GridActions section to the designer file code will look something like this:

<%@ Register src="~/CMSAdminControls/UI/UniGrid/UniGrid.ascx" tagname="UniGrid" tagprefix="cms" %> <%@ Register Namespace="CMS.UIControls.UniGridConfig" TagPrefix="ug" Assembly="CMS.UIControls" %> <cms:UniGrid ID="userGrid" runat="server"> <GridActions> <ug:Action Name="deleteaction" Caption="$General.Delete$" Icon="Delete.png" /> </GridActions> <GridColumns> <ug:Column Source="UserName" Caption="User name" Wrap="false"> </ug:Column> <ug:Column Source="UserID" Caption="User ID" Wrap="false"> </ug:Column> </GridColumns> </cms:UniGrid>

The Source property of within the Column tag defines which columns are used from the supplied dataset. Our next step will be to execute our own code on this action. At first you need to register the OnAction event in the code behind file in the Page_Load method:

userGrid.OnAction += OnAction;

Now we have registered our new OnAction method. The method itself can look the following way:

private void OnAction(string actionName, object actionArgument) { switch (actionName.ToLower()) { case "deleteaction": // Code for deleting the user by the actionArgument which is the passed UserID UserInfoProvider.DeleteUser(ValidationHelper.GetInteger( actionArgument, 0)); // Repopulate the UniGrid with the new data userGrid.DataSource = (CMS.SiteProvider.UserInfoProvider.GetAllUsers()); break; } }

You have probably noticed that the case “deleteaction” corresponds with the Name of the action in the markup file. In our example the UserID is used to delete the given user from the system. The actionArgument variable holds the UserID. If you check your page now, you should see a new delete button for each UniGrid entry. You can try to delete some testing user from the system.
 
An expansion of our example could be to change the image used for the button. This could be used for example because you want to display different buttons for deleting specific users. Let’s say you want to display a different image if the user is a global administrator. In that case you will need to dynamically change the button image during the creation of the UniGrid. For this you need to register the OnExternalDataBound event, where you will dynamically check the UserInfo object and change the icon according to this value. Please add this code into your Page Load method to register a new event:

userGrid.OnExternalDataBound += new CMS.UIControls.OnExternalDataBoundEventHandler(userGrid_OnExternalDataBound);

The corresponding method will have to check for the action type and change the passed down ImageButton control to use a different image, if the user is a global administrator. One approach to get the information, if the User is a global administrator is to use our API. The more efficient approach is to check the data you already have available:

protected object userGrid_OnExternalDataBound(object sender, string sourceName, object parameter) { string id = ((System.Data.DataRowView)parameter)["UserID"].ToString(); }

 
In our dataset we retrieved all the columns for the UserInfo object, so we can use the following code to check if the user is a global administrator:
 
string isAdmin = ((System.Data.DataRowView)parameter)["UserIsGlobalAdministrator"].ToString();

Now let’s use this information to achieve our desired functionality. We need to get a different image URL if the user is an administrator and we need to modify the sender object. In this case this will be an ImageButton control:

protected object userGrid_OnExternalDataBound(object sender, string sourceName, object parameter) { // User ID is used as actions parameter editedItemId string editedItemId = ""; bool isAdmin = false; object param = null; if (parameter is System.Web.UI.WebControls.GridViewRow) { param = ((System.Web.UI.WebControls.GridViewRow)parameter).DataItem; } editedItemId = ((System.Data.DataRowView)(param)).Row["UserId"].ToString(); isAdmin = ValidationHelper.GetBoolean(((System.Data.DataRowView)(param)).Row["UserIsGlobalAdministrator"], false); string sImageUrl = GetImageUrl("Design/Controls/UniGrid/Actions/Delete.png"); if (isAdmin) { sImageUrl = GetImageUrl("Design/Controls/UniGrid/Actions/Edit.png"); } switch (sourceName) { case "deleteaction": ImageButton btn = ((ImageButton)sender); btn.ImageUrl = sImageUrl; return btn; } return parameter; }

Additionally you need to register the ExternalSourceName for this deleteaction from the switch statement in the upper code the following way:

<GridActions> <ug:Action Name="deleteaction" ExternalSourceName="deleteaction" Caption="$General.Delete$" Icon="Delete.png" /> </GridActions>

If you now load the UniGrid then you should get different icons for users which are global administrators.
 
The last example will be how to display the editing buttons also in the last column. By default are the buttons displayed in the first column, but in some cases this isn’t desired. To achieve this we will have to change the UniGrid definition to include an additional column for our actions. To be able to get all the data from the row we will use the ##ALL## macro which is a substitute for all columns in our dataset. The ExternalSourceName is different since we need to create a different control for the action processing:

<%@ Register src="~/CMSAdminControls/UI/UniGrid/UniGrid.ascx" tagname="UniGrid" tagprefix="cms" %> <%@ Register Namespace="CMS.UIControls.UniGridConfig" TagPrefix="ug" Assembly="CMS.UIControls" %> <cms:UniGrid ID="userGrid" runat="server"> <GridActions> <ug:Action Name="deleteaction" ExternalSourceName="deleteaction" Caption="$General.Delete$" Icon="Delete.png" /> </GridActions> <GridColumns> <ug:Column Source="UserName" Caption="User name" Wrap="false"> </ug:Column> <ug:Column Source="UserID" Caption="User ID" Wrap="false"> </ug:Column> <ug:Column source="##ALL##" ExternalSourceName="deleteactioncolumn" caption="Additional Actions" wrap="false"> </ug:Column> </GridColumns> </cms:UniGrid>

Now let’s add a button to this binding event with a custom click event handler. To simulate an action button in a column we will need to define a JavaScript function for the click event. Since we still want to display the first column for deleting the user, we will need to modify the OnExternalDataBound handling method to return a clickable image if the column is used as an action. We will use the different ExternalSourceName to perform the additional processing. Also, the parameter object id of a different type so we will have to check its type to process it correctly. The JavaScript function name for our clickable image needs to be in the following form:

UG_Cmd_<UniGrid control ID>

The parameters of the method are the action name and the ID to identify the object in the UniGrid which is edited (deleted by your code). In our case it can be the UserID which is assigned to the variable editedItemId:

btnImage.Attributes["onclick"] = String.Format("return UG_Cmd_{0}('deleteAction', {1});", userGrid.ClientID, ScriptHelper.GetString(editedItemId));

When putting all those things together, here is the complete code of the OnExternalDataBound method:

protected object userGrid_OnExternalDataBound(object sender, string sourceName, object parameter) { // User ID is used as actions parameter editedItemId string editedItemId = ""; bool isAdmin = false; object param = null; if (parameter is System.Web.UI.WebControls.GridViewRow) { param = ((System.Web.UI.WebControls.GridViewRow)parameter).DataItem; } else if(parameter is System.Data.DataRowView){ param = parameter; } editedItemId = ((System.Data.DataRowView)(param)).Row["UserId"].ToString(); isAdmin = ValidationHelper.GetBoolean(((System.Data.DataRowView)(param)).Row["UserIsGlobalAdministrator"], false); string sImageUrl = GetImageUrl("Design/Controls/UniGrid/Actions/Delete.png"); if (isAdmin) { sImageUrl = GetImageUrl("Design/Controls/UniGrid/Actions/Edit.png"); } switch (sourceName) { case "deleteaction": ImageButton btn = ((ImageButton)sender); btn.ImageUrl = sImageUrl; return btn; case "deleteactioncolumn": Image btnImage = new Image() { ID = "deleteaction", ImageUrl = sImageUrl, Width = 16, Height = 16 }; btnImage.Attributes["onclick"] = String.Format("return UG_Cmd_{0}'{deleteAction}', {1});", userGrid.ClientID, ScriptHelper.GetString(editedItemId)); return btnImage; } return parameter; }

Please use apostrophes instead of &#39; in the code.


Tip #1: If you want to add multiple controls use a Panel control to encapsulate them and return the Panel control, because only one control can be returned.
Tip #2: You can return also a string (for example custom HTML code), not only controls.

Here is the final table generated by the UniGrid:

Corporate-site-456-Mozilla-Firefox_2012-03-09_15-30-38.png

 

 


-bp-

 

 


See also: Developing web parts
Unigrid overwiev

Applies to: Kentico CMS 6.0

 

 

 

Share this article on   LinkedIn