Adding A ProgressBar Column To A RadGrid In A SharePoint WebPart

I really thought there would be a tutorial on this on the Telerik website but I couldn’t find anything relevant. In this post, I am going to show you how to add a progress bar column to a RadGrid or RadGridView. So, my goal is to have something like this:

I am just setting the value in a master/detail view in a straight forward TextBox. I am not using AJAX or anything so some of the wiring to maintain values is simply to deal with PostBacks. Just looks like this:

Firstly, I am going to use the one from CodeProject, which is used in a lot of apps, for example the SnapStream project. You can view the SVN version here:

http://source.snapstream.com/repos/WebAdministration/controls/ProgressBar.cs

Blah Blah blah here is the code.

The ITemplate to wire up to the custom column:

[csharp]
public void InstantiateIn(Control container)
{
UpdatePanel panel = new UpdatePanel();
ProgressBar bar = new ProgressBar();
bar.ID = “bar”;
bar.ForeColor = Color.Black;
bar.Width = Unit.Pixel(150);
bar.FillImageUrl = string.Format(“{0}/Supporting%20Images/bg.gif”, SharePointHelpers.SiteUrl);
bar.BarImageUrl = string.Format(“{0}/Supporting%20Images/indicator.gif”, SharePointHelpers.SiteUrl);
bar.Visible = true;
bar.Enabled = true;
panel.ContentTemplateContainer.Controls.Add(bar);
container.Controls.Add(panel);
}
}
[/csharp]

The template column to add to the grid:

[csharp]
var x = new GridTemplateColumn();
x.AllowFiltering = false;
x.HeaderText = “Percentage Complete”;
x.ItemTemplate = new ProgressBarTemplate();
grid.MasterTableView.Columns.Add(x);
[/csharp]
Wire up the data bound event:
[csharp]
public static void grid_ItemDataBound(object sender, GridItemEventArgs e)
{
var nestedItem = e.Item as GridNestedViewItem;
if (nestedItem != null)
{
Panel panel = (Panel) nestedItem.FindControl(“InnerContainer”);
TextBox tbPercentage = (TextBox) panel.FindControlRecursive(“tbPercentage”);
tbPercentage.Text = DataBinder.Eval(e.Item.DataItem, “Percentage”) != null
? DataBinder.Eval(e.Item.DataItem, “Percentage”).ToString() : “[None]”;

TextBox tb = (TextBox) nestedItem.FindControlRecursive(“tbPercentage”);
GridDataItem parentItem = nestedItem.ParentItem;
ProgressBar bar = (ProgressBar) parentItem.FindControlRecursive(“bar”);
string x = tb.Text;
bar.Percentage = Convert.ToInt32(x);
}
}
[/csharp]

Wire up this prerender event to the grid:

[csharp]
public void grid_PreRender(object sender, EventArgs e)
{
foreach (GridDataItem y in _grid.Items)
{
if (y.ChildItem != null)
{
GridNestedViewItem nestedItem = y.ChildItem;
TextBox tb = (TextBox) nestedItem.FindControlRecursive(“tbPercentage”);
ProgressBar bar = (ProgressBar) y.FindControlRecursive(“bar”);
string x = tb.Text;
bar.Percentage = Convert.ToInt32(x);
}
}
}
[/csharp]

Wire up the item created event:

[csharp]
public static void grid_ItemCreated(object sender, GridItemEventArgs e)
{
var item = e.Item as GridDataItem;
if (item != null)
{
if (item.ChildItem != null)
{
GridNestedViewItem nestedItem = item.ChildItem;
TextBox tb = (TextBox) nestedItem.FindControlRecursive(“tbPercentage”);
ProgressBar bar = (ProgressBar) item.FindControlRecursive(“bar”);
string x = tb.Text;
bar.Percentage = Convert.ToInt32(x);
}
}
}
[/csharp]

Like I said depending on the technology you are using you might not need like half of those events.

Share

Free SharePoint Redirection WebPart Programming SharePoint WebParts With LINQ to Active Directory Part 2

In the first part of this series, I illustrated the initial building of the primary WebPart class and the workhorse method that taps into Active Directory using LINQ to Active Directory to retrieve the current user’s department. Now, we have to use the returned department string to query down into a helper redirection SharePoint list and use a LINQ query to compare the two strings. We can grab the corresponding value out of the URL column, and they using standard redirection techniques push the user to the destination.

The PushUserToDivisionSite method is responsible for the actual redirection of the user. Firstly, a new SPSite & SPWeb object will be instantiated using the Url property, then a SPList constructed using the List property. To build the Where construct a SPList.Items.Cast<SPListItem>().Where is used performing an Equals on the department of the user and the value of the Title column. This should actually be a String.Compare so that you could bypass any case sensitivity problems, but I am just noticing that now. So you might want to fix that. An Enumerable.FirstOrDefault is used to return the desired SPListItem, and the URL value brought out. This value is then used in a trivial Page.Response.Redirect.

[csharp]

private void PushUserToDivisionSite()
{
using (var site = new SPSite(Url))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[List];
var results = list.Items.Cast().Where(item => Equals(item[“Title”].ToString(), GetCurrentUserDepartment()));
if (results.Count() > 0)
{
SPListItem hydratedItem = results.FirstOrDefault();
string url = hydratedItem[UrlProperty].ToString();
if (!string.IsNullOrEmpty(url))
{
Page.Response.Redirect(url, true);
}
else
{
HandleException(new Exception(“Redirection Did Not Occur. Please Check The URL Property Of The List Being Used.”));
}
}
}
}
}

[/csharp]

The CreateChildControls method of the WebPart doesn’t do much besides call relevant helper methods. Since the GetCurrentUserDepartment method will either return the department or a constant string value as specified in the global fields, that can simplify be leveraged in a switch. So, either the department is returned, or the constant matches and a new Exception is thrown using the HandleException method which doesn’t do much but emit a literal control.

[csharp]

protected override void CreateChildControls()
{
if (!_error)
{

try
{
base.CreateChildControls();
string department = GetCurrentUserDepartment();
switch (department)
{
case _breakStatement:
HandleException(new Exception(“It Appears Your Active Directory Account Has No Department Set!”));
break;
default:
PushUserToDivisionSite();
break;
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}

[/csharp]

And that’s it. So going through this again, I think there are two big things that LINQ to Active Directory provides that are crucial to recognize, regardless of whether you like the actual implementation of the concept.

  1. Providing a layer of abstraction over Active Directory functions promotes standardization and ease of use.
  2. Being able to use LINQ querying constructs is extremely helpful when trying to keep code succinct and providing homogenous querying mechanisms.
Share

Free SharePoint Redirection WebPart – Programming SharePoint WebParts With LINQ to Active Directory – Part 1

The real power of LINQ lies in the ambiguity to query collections of all Types, as long as they implement the IQueryable interface. Since Active Directory is essentially a collection of users, one should be able to use a LINQ-esque fashion to manipulate objects out of that particular store as well.

This is where LINQ to AD is awesome! It allows a wrapper to exist around System.DirectoryServices to allow more contemporary, succinct language constructs to be used while developing AD DS integrated tooling. Since I do a lot of security software that exploits AD DS, I wanted to give it a shot!

And it’s pretty impressive. As you can see in the code being explained, there are some quirks, but it’s a pretty cool approach that I believe I am going to use more frequently because I enjoy the layer of abstraction and really, really enjoy being able to use the LINQ code approach.

I have to break this up into two posts because it is exhaustively long. The second post has the download link.

So, for a test project let’s go through building a Redirection WebPart that will satisfy a couple requirements:

  1. Get a reference to the current user
  2. Query into AD DS, find that user, and retrieve the department property
  3. Using the department property, query into a pre-existing SPList, match it, and retrieve a corresponding URL
  4. Redirect the current context to that returned URL

First things first, you have to do download the LINQ to Active Directory project.

Firstly, the appropriate references have to be made, notably to the LINQ to Active Directory project as a referenced project or a compiled assembly. As well, the Active DS Type Library must be referenced so that the IADs interface is exposed. You will see why this is important shortly, but needless to say if not referenced you will get a somewhat cryptic error about an “unspecified error” occurring which is a pain in the ass.

This is what my project structure looks like following:

10-27-2009 1-43-04 PM

As you can see, there are two references out of some pretty orthodox ones being made. The Bds.DirectoryServices.Linq project is referenced, along with the ActiveDs assembly like discussed previously.

The WebPart consists of only two classes. The first one, User, is meant to structure a User object with the properties that should be queried during the AD iterations. For my particular requirement I am only interested in getting the account name and the corresponding department entry. The class and property decorations are provided to specify targets,  DirectoryAttribute for example correlates to the underlying LDAP property to query.

[csharp]

[DirectorySchema(“user”, typeof(IADsUser))]
public class User
{
[DirectoryAttribute(“sAMAccountName”)]
public string AccountName { get; set; }

[DirectoryAttribute(“Department”, DirectoryAttributeType.ActiveDs)]
public string Department { get; set; }
}

[/csharp] 

I am going to throw on some generic properties now into the WebPart class to keep some of the more mutable values dynamic. Just four of them, one a URL string to hyrdate a SPSite object where the SPList exists, a string to represent which SPList to get, a third string to allow the column name to be specified, and a fourth string for the AD DS domain name.

[csharp]

        [Personalizable, WebBrowsable(true),
         WebDescription(“The Url Where The Redirection List Exists”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“URL Of Redirection List Site”)]
        public string Url { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“Redirection List”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“Redirection List”)]
        public string List { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“URL List Property”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“Field Name That Contains URL Values”)]
        public string UrlProperty { get; set; }

        [Personalizable, WebBrowsable(true),
         WebDescription(“Active Directory Domain Name To Use”),
         Category(“Global Mode Configuration”),
         WebPartStorage(Storage.Shared),
         DefaultValue(“”),
         WebDisplayName(“AD DS Domain name”)]
        public string DomainName { get; set; }

[/csharp]

Getting the users department from AD using LINQ to Active Directory is a breeze as illustrated in the GetCurrentUserDepartment method below. The _entry global field is a DirectoryEntry object which is intialized OnLoad to the domain string literal specified as a WebPart property (the DomainName auto-property), using some brief string massaging to make it an agreeable DirectoryEntry constructor parameter format-wise.

[csharp]
protected override void OnLoad(EventArgs e)
{
if (!_error)
{
try
{
base.OnLoad(e);
if (!string.IsNullOrEmpty(DomainName))
{
_entry = new DirectoryEntry(string.Format(“LDAP://{0}”, DomainName));
}
else
{
HandleException(new Exception(“Please Set A Domain Name!”));
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}

[/csharp]

The second SearchScope parameter will specify the tranversal rules for the query, since I want to recur the whole substree I am going to use SearchScope.Subtree.

Following, the current SPContext is going to be used to get a the current SPUser.Login name, which passed into a regular expression to clean it as to match the account name return from Active Directory. An IQueryable<User> collection is built by executing a LINQ Where clause against the previously created DirectorySource object, matching the current SharePoint user to their record in Active Directory. Following, this collection is pushed to a List<User> typed collection using AddRange with the department set off the User.Department property. If no department is returned, it is set to a global string literal that represents a broken (no return) query, used later in conditional statements. Probably should use a String.Compare instead of the equality operator but you get the idea.

[csharp]
private static string GetCurrentUserDepartment()
{
string department = string.Empty;
SPSecurity.RunWithElevatedPrivileges(delegate
{
var usersDs = new DirectorySource(_entry,SearchScope.Subtree);
var cleanUserName = Regex.Replace(SPContext.Current.Web.CurrentUser.LoginName,”.*\\\\(.*)”, “$1”, RegexOptions.None);
IQueryable users = usersDs.Where(usr => usr.AccountName == cleanUserName);
// put in to fix LINQ to AD Bug
var userList = new List();
userList.AddRange(users);
if (userList.Count > 0)
{
User curUser = userList.FirstOrDefault();
department = curUser.Department;
}
else
{
department = _breakStatement;
}
});
return department;
}

[/csharp]

Next Section >> Part II – Building The Remainder Of The WebPart Class

Share