ASP.NET Duration UserControl

Recently, I had need for a “duration control” that would allow users to view, edit and update a duration in hours, minutes and seconds. The result of the user selection needed to be stored as an integer in a database (as seconds) and then converted back to hour, minutes and seconds for display. In read only mode the duration needed to be displayed as a single label whilst in edit mode I wanted a reasonably foolproof interface for my users.

I decided to create a ASP.NET UserControl that consisted of, in edit mode, a textbox for the hours, and two dropdownlists for the minutes and seconds, whilst in read only mode, a label as shown below:

Read-Only Mode (used on a ASPX page):

Duration Control - View

Duration Control - View

Edit Mode (used on a ASPX page):

Duration Control - Edit

Duration Control - Edit

The code for this usercontrol is shown below:

HTML

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="ctrlDuration.ascx.vb" Inherits="UserControls_ctrlDuration" %>
<asp:Panel ID="pnlDisplay" runat="server">
<asp:Label ID="lblDuration" runat="server" Text=""></asp:Label>
</asp:Panel>
<asp:Panel ID="pnlEdit" runat="server">
Hours:
<asp:TextBox ID="txtHours" runat="server" Text="00"></asp:TextBox>
Minutes:
<asp:DropDownList ID="ddlMinutes" runat="server">
<asp:ListItem Selected="True">0</asp:ListItem>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>6</asp:ListItem>
<asp:ListItem>7</asp:ListItem>
<asp:ListItem>8</asp:ListItem>
<asp:ListItem>9</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>11</asp:ListItem>
<asp:ListItem>12</asp:ListItem>
<asp:ListItem>13</asp:ListItem>
<asp:ListItem>14</asp:ListItem>
<asp:ListItem>15</asp:ListItem>
<asp:ListItem>16</asp:ListItem>
<asp:ListItem>17</asp:ListItem>
<asp:ListItem>18</asp:ListItem>
<asp:ListItem>19</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
<asp:ListItem>21</asp:ListItem>
<asp:ListItem>22</asp:ListItem>
<asp:ListItem>23</asp:ListItem>
<asp:ListItem>24</asp:ListItem>
<asp:ListItem>25</asp:ListItem>
<asp:ListItem>26</asp:ListItem>
<asp:ListItem>27</asp:ListItem>
<asp:ListItem>28</asp:ListItem>
<asp:ListItem>29</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>31</asp:ListItem>
<asp:ListItem>32</asp:ListItem>
<asp:ListItem>33</asp:ListItem>
<asp:ListItem>34</asp:ListItem>
<asp:ListItem>35</asp:ListItem>
<asp:ListItem>36</asp:ListItem>
<asp:ListItem>37</asp:ListItem>
<asp:ListItem>38</asp:ListItem>
<asp:ListItem>39</asp:ListItem>
<asp:ListItem>40</asp:ListItem>
<asp:ListItem>41</asp:ListItem>
<asp:ListItem>42</asp:ListItem>
<asp:ListItem>43</asp:ListItem>
<asp:ListItem>44</asp:ListItem>
<asp:ListItem>45</asp:ListItem>
<asp:ListItem>46</asp:ListItem>
<asp:ListItem>47</asp:ListItem>
<asp:ListItem>48</asp:ListItem>
<asp:ListItem>49</asp:ListItem>
<asp:ListItem>50</asp:ListItem>
<asp:ListItem>51</asp:ListItem>
<asp:ListItem>52</asp:ListItem>
<asp:ListItem>53</asp:ListItem>
<asp:ListItem>54</asp:ListItem>
<asp:ListItem>55</asp:ListItem>
<asp:ListItem>56</asp:ListItem>
<asp:ListItem>57</asp:ListItem>
<asp:ListItem>58</asp:ListItem>
<asp:ListItem>59</asp:ListItem>
</asp:DropDownList>
Seconds:
<asp:DropDownList ID="ddlSeconds" runat="server">
<asp:ListItem Selected="True">0</asp:ListItem>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>6</asp:ListItem>
<asp:ListItem>7</asp:ListItem>
<asp:ListItem>8</asp:ListItem>
<asp:ListItem>9</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>11</asp:ListItem>
<asp:ListItem>12</asp:ListItem>
<asp:ListItem>13</asp:ListItem>
<asp:ListItem>14</asp:ListItem>
<asp:ListItem>15</asp:ListItem>
<asp:ListItem>16</asp:ListItem>
<asp:ListItem>17</asp:ListItem>
<asp:ListItem>18</asp:ListItem>
<asp:ListItem>19</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
<asp:ListItem>21</asp:ListItem>
<asp:ListItem>22</asp:ListItem>
<asp:ListItem>23</asp:ListItem>
<asp:ListItem>24</asp:ListItem>
<asp:ListItem>25</asp:ListItem>
<asp:ListItem>26</asp:ListItem>
<asp:ListItem>27</asp:ListItem>
<asp:ListItem>28</asp:ListItem>
<asp:ListItem>29</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>31</asp:ListItem>
<asp:ListItem>32</asp:ListItem>
<asp:ListItem>33</asp:ListItem>
<asp:ListItem>34</asp:ListItem>
<asp:ListItem>35</asp:ListItem>
<asp:ListItem>36</asp:ListItem>
<asp:ListItem>37</asp:ListItem>
<asp:ListItem>38</asp:ListItem>
<asp:ListItem>39</asp:ListItem>
<asp:ListItem>40</asp:ListItem>
<asp:ListItem>41</asp:ListItem>
<asp:ListItem>42</asp:ListItem>
<asp:ListItem>43</asp:ListItem>
<asp:ListItem>44</asp:ListItem>
<asp:ListItem>45</asp:ListItem>
<asp:ListItem>46</asp:ListItem>
<asp:ListItem>47</asp:ListItem>
<asp:ListItem>48</asp:ListItem>
<asp:ListItem>49</asp:ListItem>
<asp:ListItem>50</asp:ListItem>
<asp:ListItem>51</asp:ListItem>
<asp:ListItem>52</asp:ListItem>
<asp:ListItem>53</asp:ListItem>
<asp:ListItem>54</asp:ListItem>
<asp:ListItem>55</asp:ListItem>
<asp:ListItem>56</asp:ListItem>
<asp:ListItem>57</asp:ListItem>
<asp:ListItem>58</asp:ListItem>
<asp:ListItem>59</asp:ListItem>
</asp:DropDownList>
<asp:CompareValidator ID="cvlDuration" runat="server"
ErrorMessage="Hours must be a whole number" ControlToValidate="txtHours"
Operator="GreaterThanEqual" Type="Integer" ValueToCompare="0">*</asp:CompareValidator>
</asp:Panel>

Code-Behind

Partial Class UserControls_ctrlDuration
Inherits System.Web.UI.UserControl
Public WriteOnly Property DisplayOnly() As Boolean
Set(ByVal value As Boolean)
If value Then
pnlDisplay.Visible = True
pnlEdit.Visible = False
Else
pnlDisplay.Visible = False
pnlEdit.Visible = True
End If
End Set
End Property
Public Property Duration() As Integer
Get
Return CInt(txtHours.Text * 3600) + CInt(ddlMinutes.SelectedValue * 60) + CInt(ddlSeconds.SelectedValue)
End Get
Set(ByVal value As Integer)
txtHours.Text = (value 3600)
ddlMinutes.SelectedValue = ((value 60) Mod 60)
ddlSeconds.SelectedValue = (value Mod 60)
lblDuration.Text = (value 3600) & " hours " & ((value 60) Mod 60) & " minutes " & (value Mod 60) & " seconds"
End Set
End Property
End Class

Then in the ASPX page that makes use of this usercontrol we need to register it:

<%@ Register src="ctrlDuration.ascx" tagname="ctrlDuration" tagprefix="uc1" %>

Finally on the page we can use it inside a formview control for example as shown below (omitting all the other datasource controls etc.):

In the ItemTemplate section we would use it in read only mode:

<uc1:ctrlDuration ID="ctrlResponse" runat="server" Duration='<%# Bind("iResponseDuration") %>' DisplayOnly="True" />

Whilst in the EditItemTemplate and InsertItemTemplate we would use it in edit mode:

<uc1:ctrlDuration ID="ctrlResponse" runat="server" Duration='<%# Bind("iResponseDuration") %>' DisplayOnly="False" />

Hope it may be of use. As always comments (positive and negative) always welcome.

Assist your procurement department with suggestions

If you are using the purchasing modules of Service Desk Express then the inventory catalogue data is frequently the responsibility of the procurement function. One of the areas that many procurement functions struggle with is determining if an asset is tracked by it’s asset and/or serial number. Frequently, whilst there knowledge of procurement might be second to none, their understanding of the what does and does not constitute an asset or valuable/attractive item in business terms is not so clear. So how can we help?

Normally, the procurement function are also given the task of managing the inventory categories and this is the first thing I would suggest you change. Give ownership of this module to the asset management gurus, who do understand what is deemed an asset or valuable/attractive item.

Using the DB Admin Tool create two boolean fields in the Inventory Category module: Track Asset Suggestion and Track Serial Suggestion as shown below:

Track AssetSerial Suggestion DB Changes

Add these fields to the Inventory Category form and get the asset management gurus who now own this module to “suggest” whether inventory catalogue items in a given category should generally be Track Asset and/or Track Serial.

Finally, create a client side business rule in the Inventory Catalog module that prefills the Track Asset and Track Serial fields based on a change of Inventory Category as shown below:

IC – Prefill Track Asset and Track Serial Based on Suggestion By Category

Conditions:

On Data Change of field Category Description with {TR,Category Description} NotEqual <Leave Blank>

Actions:

Populate Current Form – Inventory Catalog – Inventory Catalog setting:

  • Track Asset/Tag # = {DB,Inventory Categories,Track Asset Suggestion,”Sequence” = {TR,Seq.Category} DB}
  • Track Serial # = {DB,Inventory Categories,Track Serial Suggestion,”Sequence” = {TR,Seq.Category} DB}

Assigns:

Inventory Catalog form.

Now, when procurement are creating an inventory catalogue item and select the appropriate inventory category, the Track Asset/Tag # and Track Serial # boxes will be automatically prefilled based on the suggestion for the category selected. If procurement then wants to overrule the suggestion they can but, they then take responsibility for it.

Orphaned Configurations and CSBRs

In some organisations, orphaned configurations are an issue as it is through the assignment of the configuration to the end-user that assets are managed. This post seeks to a) demonstrate the use of MATH statements as a way of checking for the existance of child records for a given parent record, and b) using this technique, warn a support staff member that the current configuration is orphaned, such that it can be reassigned.

Let’s create the basic CSBR:

On Save of Configuration, Display Message:

"The current configuration is NOT assigned to a client. Please right-click on the Configuration Clients tab at the bottom of this screen, select Assign Client To Configuration, and then select the appropriate client from the list."

Make sure that you DON’T check the box titled “Exit Rule if ‘Ok’ clicked” as, if ticked, it would prevent you saving the configuration at all! This makes creating them a little bit of an issue!

Assign the CSBR to the Configuration form and save it. If we now open the Configuration form find a record and click save the popup message will appear, irrespective of whether the configuration is orphaned or not. So how do we check for an orphaned configuration?

The secret lies in a SQL statement:

SELECT COUNT(*) FROM "_SMDBA_"."_CONFIGDET_" WHERE "_CONFIG_" = {TR,Sequence}

The _CONFIGDET_ table holds the records for the linking of clients to configurations and these are deleted when clients are unassigned from configurations. Hence, by simply checking if the number of records in that table for the selected Configuration = 0 then we can establish if the configuration is orphaned or not.

So the final piece of the puzzle is adding this condition into our CSBR such that it only fires when a configuration is orphaned. Open the CSBR and add a condition as follows:

Expression 1: {MATH,(SELECT COUNT(*) FROM "_SMDBA_"."_CONFIGDET_" WHERE "_CONFIG_" = {TR,Sequence}) MT}; Comparison Operator: Equals; Expression 2: 0 (as in the number zero)

Save the CSBR and the task is complete.