Get the port number of your Web Application Zone using server side object model

Although it seems pretty easy to get your web application’s port number from IIS or Central Admin, I didn’t find enough info on how to get the port number from server side object model. I was trying to customize the Authenticate.aspx application page where I had already written code to check users permission in a specific group of another web application. In the end, I was able to get a really simple method which is the following piece of code:

int webPort=SPContext.Current.Site.WebApplication.IisSettings[Microsoft.SharePoint.Administration.SPUrlZone.Default].ServerBindings[0].Port; if (webPort==80) { //check user permission for a site collection which is using the web app on port 80 ... ... }

else { //check the user permission for rest of the web apps SPUtility.EnsureAuthentication(); SPUtility.Redirect(spWeb.Url, SPRedirectFlags.UseSource, Context); }

If you look at the code, you’ll easily understand I was actually trying to check the user permission for the web application which is using Port 80 and you can write your server side code there to check any user’s permission or group permission etc. Notice that, here I am actually checking the default zone’s port number for the web application. If you have multiple zones, or else if you have extended your web application on other zones which have different ports, then you have to specify  one from the below under WebPpplications.IisSettings property:

SPUrlZone.Custom,
SPUrlZOne.Default,
SPURlZone.Internet,
SPUrlZone.Intranet
SPUrlZOne.Extranet

Now, what if you want to create a method, which will get the port number of web application and later on you can call that method from anywhere; you can do that too. The code is given below:

int GetPortNumber(Microsoft.SharePoint.Administration.SPWebApplication application)
{
int result = 0;
Microsoft.SharePoint.Administration.SPIisSettings setting=null;
if (application.IisSettings.TryGetValue(Microsoft.SharePoint.Administration.SPUrlZone.Default, out setting))
{
if (null != setting)
{
var serverBindings = setting.ServerBindings;
if (0 < serverBindings.Count)
{
Microsoft.SharePoint.Administration.SPServerBinding serverBinding = serverBindings[0];
result = serverBinding.Port;
}
}
}
return result;
}
Advertisement

Use SPMetal Extender to overcome LINQ to SharePoint 2010 limitations

Many of us are not aware of the fact that SPMetal only runs against the field type provided by SharePoint Foundation. So, if you use Taxonomy, Publishing HTML, Publishing Image in your list, you will not be able to generate an entity class with these fields by running SPMetal command as it will skip those fields. You can easily overcome this problem by using one plugin from codeplex, “SPMetal Extender”.

LINQ to SharePoint has several limitations; there are various techniques to improve SPMetal code generation. This project provides a Visual Studio extension to allow developers to easily work with list fields that are not covered by SPMetal’s code generation. The extension provides additional functionality to the server explorer to allow the developer to generate code to extend LINQ to SharePoint’s functionality. For example, SPMetal does not generate strongly typed properties for lists fields based on “Publishing HTML” or “Taxonomy” site columns. You can download SPMetal Extender from Here

Create Dynamic Visual Web Part in SharePoint

After a long time, I am going to discuss about creating a  dynamic visual web part in SharePoint 2010. Suppose, a company needs to create a sales forecasting web part on monthly basis. They want to display information in a web part of a page where the data will come from different lists from a web site and hence the result will be shown visually to highlight information based on conditions.

Objective:

Select a month from the drop down list of all months and based on the selected month, the grid view should change automatically. A LINQ will be executed in the background whenever you change a month from the drop down list and the data displayed inside the grid view should match the query results.

Solution:

First of all, Create a  visual studio project and select Visual Web Part from the SharePoint 2010 templates. This automatically adds a Feature with a Default Visual Web Part in your solution. Please note that, while creating a visual web part, you must choose as Farm Solution as the files of visual web parts (ASCX Files) need to be deployed physically inside 14 hive.

You will need to write LINQ to query your SharePoint Lists. So, create a Entity Class using SPMetal.exe. To know more about how to use SPMetal, go here. Create and add the entity class in your project. For my example; I named the class as MyEntity.cs.

To start off with the solution, let us drag and drop a drop down box and create SPGridView object. The ASCX code is given below:

  1. <%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
  2. <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  3. <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  4. <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  5. <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
  6. <%@ Import Namespace="Microsoft.SharePoint" %>
  7. <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  8. <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="VisualWebPart1UserControl.ascx.cs" Inherits="SalesForecastWebPart.VisualWebPart1.VisualWebPart1UserControl" %>
  9.  
  10. <asp:dropdownlist runat="server" ID="MonthList" AutoPostBack="True" onselectedindexchanged="MonthList_SelectedIndexChanged"
  11.     >
  12.     <asp:ListItem Selected="True">–Select Month–</asp:ListItem>
  13.     <asp:ListItem Value="1">January</asp:ListItem>
  14.     <asp:ListItem Value="2">February</asp:ListItem>
  15.     <asp:ListItem Value="3">March</asp:ListItem>
  16.     <asp:ListItem Value="4">April</asp:ListItem>
  17.     <asp:ListItem Value="5">May</asp:ListItem>
  18.     <asp:ListItem Value="6">June</asp:ListItem>
  19.     <asp:ListItem Value="7">July</asp:ListItem>
  20.     <asp:ListItem Value="8">August</asp:ListItem>
  21.     <asp:ListItem Value="9">September</asp:ListItem>
  22.     <asp:ListItem Value="10">October</asp:ListItem>
  23.     <asp:ListItem Value="11">November</asp:ListItem>
  24.     <asp:ListItem Value="12">December</asp:ListItem>
  25. </asp:dropdownlist>
  26.  
  27. &nbsp;&nbsp;<b>Monthly Forecast</b>
  28. <SharePoint:SPGridView ID="spGridView" runat="server"
  29.     AutoGenerateColumns="false"
  30.     onprerender="spGridView_PreRender" ondatabound="spGridView_DataBound" onrowdatabound="spGridView_RowDataBound">
  31.     <HeaderStyle HorizontalAlign="Left" ForeColor="Navy" Font-Bold="true" />
  32.     <Columns>
  33.         <SharePoint:SPBoundField DataField="Title" HeaderText="Consultant">
  34.         </SharePoint:SPBoundField>
  35.           <asp:TemplateField HeaderText="Total ITS Ricoh Lab Revenue">
  36.             <ItemTemplate>
  37.         <asp:Label ID="lblSales" runat="server" Text='<%# String.Format("{0:f2}",DataBinder.Eval(Container.DataItem,"TotalSales")) %>' ></asp:Label>
  38.         </ItemTemplate>
  39.         </asp:TemplateField>
  40.          <asp:TemplateField HeaderText="Total ITS Ricoh Lab Revenue Quota">
  41.             <ItemTemplate>
  42.         <asp:Label ID="lblQuota" runat="server" Text='<%# String.Format("{0:f2}",DataBinder.Eval(Container.DataItem,"ITSRicLabRev")) %>' ></asp:Label>
  43.         </ItemTemplate>
  44.         </asp:TemplateField>
  45.         <asp:TemplateField HeaderText="Status">
  46.             <ItemTemplate>
  47.                 <asp:Label ID="Status" runat="server" Text='<%# Eval("Status") %>' />
  48.             </ItemTemplate>
  49.              
  50.         </asp:TemplateField>
  51.       
  52.         
  53.     </Columns>
  54. </SharePoint:SPGridView>

If you look inside the above code, you will find that I have created four columns inside the grid view to display Employee (Consultant) Name, Total Revenue, Total Revenue Quota and Status and a drop down box containing all months. Now, create the entity object for the lists inside your ascx.cs file. For my scenario, I had to query two different lists and I created the entity and data context object as below:

EntityList<SCTXPSITSVCsForecastingItem> SCTXPSITSVCsForecasting;

EntityList<SolnConsultantsItem> SolnConsultants;

protected void Page_Load(object sender, EventArgs e)

{

    string spWebUrl = SPContext.Current.Web.Url;

    MyEntitiesDataContext dc = new MyEntitiesDataContext(spWebUrl);

    SCTXPSITSVCsForecasting = dc.GetList<SCTXPSITSVCsForecastingItem>(@"SCTX PS / ITSVCs Forecasting");

    SolnConsultants = dc.GetList<SolnConsultantsItem>("SolnConsultants");

 

}

 

From the above code, you can see I created two data context object where my entity class was “MyEntities”. I then called the two lists in the page load event by the GetList() Method.

Inside the SelectedIndexChanged() method of the drop down list for month, I called a method to create the LINQ and passed the selected item as the parameter. If you notice the LINQ below, you will see that I have used a join query to select data from two different lists. For my scenario, I had a list called “SCTXPSITSVCsForecasting” where there was a column named “ITSRicohLaborRevenue” in which all the employees (consultants) monthly forecast had to be summed up. So, I used an aggregated function (SUM) to sum up all the employees monthly forecast and divided by the standard monthly forecast to determine their status (if greater than 90%, “Green”, if greater than or equal to 75%, “Yellow” and if below 75%, “Red”).

  1. protected void MonthList_SelectedIndexChanged(object sender, EventArgs e)
  2.         {
  3.             var selected = MonthList.SelectedValue;
  4.             getConsultants(Convert.ToInt32(selected));
  5.            
  6.         }
  7.         private void getConsultants(int mo)
  8.         {
  9.  
  10.             var ConsultantQuery = from c in SolnConsultants.ToList()
  11.                                   join s in SCTXPSITSVCsForecasting.ToList()
  12.  
  13.                                on c.Title equals s.Consultant
  14.                                   where s.CloseDate.Value.Month.Equals(mo)
  15.                                   group s by new { s.Consultant, c.ITSRicLabRev } into result
  16.                                   select new
  17.                                   {
  18.                                       Title = result.Key.Consultant,
  19.                                       TotalSales = result.Sum(s => s.ITSRicohLaborRevenue),
  20.                                       ITSRicLabRev = result.Key.ITSRicLabRev / 12,
  21.                                       Status = (((result.Sum(s => s.ITSRicohLaborRevenue)) / (result.Key.ITSRicLabRev / 12) * 100) > 90 ? "Green" :
  22.                                       (((result.Sum(s => s.ITSRicohLaborRevenue)) / (result.Key.ITSRicLabRev / 12) * 100) >= 75 ? "Yellow" : "Red"))
  23.  
  24.                                   };
  25.  
  26.             spGridView.DataSource = ConsultantQuery;
  27.             spGridView.DataBind();
  28.  
  29.  
  30.         }

To display the status of the forecast visually, I had to add another method for the grid view on RowDataBound():

  1.  
  2.         protected void spGridView_RowDataBound(object sender, GridViewRowEventArgs e)
  3.         {
  4.             if (e.Row.RowType == DataControlRowType.DataRow)
  5.             {
  6.                 Label lblStatus = e.Row.FindControl("Status") as Label;
  7.                 string status = Convert.ToString(DataBinder.Eval(e.Row.DataItem, "Status"));
  8.                 if (status == "Red")
  9.                 {
  10.                     DataControlFieldCell d = lblStatus.Parent as DataControlFieldCell;
  11.                     // change the backcolor like this
  12.                     d.BackColor = System.Drawing.Color.Red;
  13.                     // change the row color like this
  14.                     e.Row.BackColor = System.Drawing.Color.LightBlue;
  15.                     // change the text color like this
  16.                     lblStatus.ForeColor = System.Drawing.Color.White;
  17.                 }
  18.                 else if (status == "Yellow")
  19.                 {
  20.                     DataControlFieldCell d1 = lblStatus.Parent as DataControlFieldCell;
  21.                     d1.BackColor = System.Drawing.Color.Yellow;
  22.                     e.Row.BackColor = System.Drawing.Color.Aqua;
  23.                     lblStatus.ForeColor = System.Drawing.Color.Blue;
  24.                 }
  25.                 else
  26.                 {
  27.                     DataControlFieldCell d2 = lblStatus.Parent as DataControlFieldCell;
  28.                     d2.BackColor = System.Drawing.Color.Green;
  29.                     e.Row.BackColor = System.Drawing.Color.LightCyan;
  30.                     lblStatus.ForeColor = System.Drawing.Color.WhiteSmoke;
  31.                 }
  32.             }
  33.         }

You can see from the above code that I am changing the Background color , Foreground color of the row based on the Status column’s value.

After deploying and adding the web part on a page, you will get a following screen:

ricohsaleswebpart

ricohsaleswebpart1

You can see from the above screenshots that the gird view is changing dynamically based on the selected month. Following this basic example, you can create more complex dynamic visual web parts in SharePoint.

Resolve issues while updating People Picker Column of a List in Event Handler

Let’s say, you have a List with a People Picker column (Person or Group) on your site. Now, you need to add an event handler for new item add/update. Normally, if you go by the conventional way, you would get the following error:

Invalid data has been used to update the list item . The field you are trying to update may be read only.

I tried to figure out what exactly caused this error while I trying to set a people picker column property in my event handler programmatically. Afterwards, I found out that People Picker Column in a List is a read-only field. By Default, the read-only mode of this field remains true while we try to add/update it for a list item. So, how to overcome this problem? Well, it’s not that difficult. You just need to set the read-only mode of the people picker column to be “False” at first and after that,  update the property of the people picker. After finishing everything, set the read-only mode of the field again to “True”. Here is an example how you can achieve this:


using (SPSite mySite = new SPSite(properties.SiteId))
 {
 using (SPWeb myWeb = mySite.OpenWeb(properties.RelativeWebUrl))
 {
 SPList currentList = myWeb.Lists[properties.ListId];
 SPListItem currentitem = currentList.GetItemById(properties.ListItem.ID);

 myWeb.AllowUnsafeUpdates = true;
 ServerContext ospServerContext = ServerContext.GetContext(mySite);

//Create a new UserProfileManager
 UserProfileManager pManager = new UserProfileManager(ospServerContext);

//Get the User Profile for the current user
 UserProfile uProfile = pManager.GetUserProfile(currentitem["Title"].ToString());

 //Set the read only mode of people picker column to FALSE
 currentitem.Fields["Direct Supervisor"].ReadOnlyField = false;

 if (uProfile["Manager"].Value != null)
 {
 string temp = @""+uProfile["Manager"].Value.ToString();
 string temp1=temp.Replace("\\",@"\");
 //Setting the property of people picker column
 currentitem["Direct Supervisor"] = myWeb.AllUsers[temp1].ID;

 }

//Update List Item
 currentitem.Update();
 //Update List
 currentList.Update();

//Set the read only mode to TRUE
 currentitem.Fields["Direct Supervisor"].ReadOnlyField = true;

//Update the List item again
 currentitem.Update();
 //update the List again
 currentList.Update();

}
}

Inside the above code, you can see that first of all, I set the property of current site’s allowUnsafeUpdates to True which will allow you to change the read-only mode of a field. I have used the User Profile service to set the value of People Picker Control (in this case, the field name is “Direct Supervisor”). After setting the value of the field, I updated the List item and the list before changing back the read-only mode to be “True” again. Afterwards, I updated the current item and current list once again which ensures that the update will take place without any such error which happened previously.


			

Easiest way to setup Form Based Authentication with Active Directory LDAP in Claims Based Web Apps for SharePoint 2010

In this post, I am going to explain the quickest possible way to enable Form Based Authentication with Active Directory which will use the new Claims Based Authentication. First thing to be aware of that, we cannot modify an existing web app which was created using Classic Mode Authentication. If we see any existing web app which uses only Windows Authentication in Classic Mode, we will see that FBA option is disabled there. So, in order to achieve our goal, we need to create a new web application and choose the following Claims Based instead of Classic Mode:

Go to Central Admin –> Application Management –> Manage Web Application –> Click New:

image

When we select Claims Based Authentication from the above figure, a new section opens up in the screen

image

In the above figure, you can see I have enabled both Windows Authentication and Form Based Authentication. I could mention the ASP.NET membership provider name here too. But, As I need to configure few things beforehand, I chose to configure this later. So, we now need to modify three web.config files for the following three applications:

1. Central Administration Site

2. The Web Application which we will be used for Claims Based Authentication.

3. Security Token Web Service Application

Go to IIS 7 of your SharePoint Web Server. Select the Central Administration Website. Right click and select “Explore” which will open up a WIndows Explorer where you will find the web.config file. Open up the configuration file using a notepad or Visual Studio and look for <system..web>.

Right before the beginning of <system.web>, Add the following section:

<connectionStrings>
   <add name="adconn"
        connectionString="LDAP://kfupm.edu.sa:389/dc=kfupm,dc=edu,dc=sa" />
</connectionStrings>
Here, “adconn” is my connection string name and connectionString holds the URL of my LDAP. After <system.web>, add the following:
<membership defaultProvider="admembers">
   <providers>
      <add name="admembers"
           type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
           connectionStringName="adconn"
           enableSearchMethods="true"
           attributeMapUsername="sAMAccountName" />
   </providers>
</membership>
I have given my provider name as admembers and mentioned the connectionStringName which I declared before.
Now, we need to follow the same for rest of the two web.config files.
Now, Right Click the Web App for which we are trying to configure FBA and click “Explore” which will open up a location like “C:\inetpub\wwwroot\wss\VirtualDirectories\[PortNumber]” where you will see the web.config file for your web app. Open this file in the same manner as you did for central admin site and make the same changes:
Now, for the first section it will be the same (connectionStrings section). For the membership part, a membership provider is automatically added when we created this web app selecting Claims Based Authentication. So, we will look for <membership tag and edit the section as following:
<membership defaultProvider="i">
      <providers>
        <add name="i" type="Microsoft.SharePoint.Administration.Claims.SPClaimsAuthMembershipProvider, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
        <add name="admembers" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="adconn" enableSearchMethods="true" attributeMapUsername="sAMAccountName" />
      </providers>
    </membership>

In the above snippet, notice that, I only added an entry for membership provider admembers. The other settings were created when we created the web app. Right after this, you will find another settings for roleManager where you can add any active directory role. As I didn’t create any role for my purpose, I am not going to enter anything in that section here. This completes the second part.

Finally, we need to do the same for Security Token Web Service App’s web.config file. To do this, expand the SharePoint Web Services site from IIS and you will see the SecurityTokenServiceApplication.
image

Right Click and select “Explore” to open up the directory where you will find the web.config file for this application. Here, you will not find any <system.web> sections. So what you need to do is, go to the bottom of this file where you will find </configuration> and right before this tag, add the following:

<connectionStrings>
  <add name="adconn"
       connectionString="LDAP://kfupm.edu.sa:389/dc=kfupm,dc=edu,dc=sa" />
/connectionStrings>
system.web>
<membership defaultProvider="admembers">
  <providers>
     <add name="admembers"
          type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
          connectionStringName="adconn"
          enableSearchMethods="true"
          attributeMapUsername="sAMAccountName" />
  </providers>
/membership>
/system.web>

Do not forget to change the connection string for your Active Directory LDAP.

Now, go to the central admin and go to Application Management –> Manage Web App. Select your web app we created before for Claims Based authentication and choose Authentication Providers from the ribbon. From the pop up window, click on Default under zone and you will get the following window:

image

Note that, I have mentioned the provider name under ASP.NET Membership provider settings. There are other options here as well like to choose whether to use the default sign in page or to use a custom page for Forms Based Authentication. Now, my web application is ready to use both Windows and Form based authentication which will utilize the new Claims Based Authentication technology.

Let us now create a site collection to see for real. Go to Application Management –> SIte Collections –> Create Site Collection page. After setting the name and template for the new site collection, while choosing to  Site Collection Administrator, if you search for users you should get something like the following window:

image

As expected, I now get two instances for one name (one in Active Directory and another one is getting pulled by the new form based configuration to use with Forms Authentication). After creating this site, if I try to sign in the new site collection, I will get the following window:

image

You can see that two different authentications are now coming under the drop down box inside the default sign in page for my web application. If I now choose Forms Authentication, I will get the following window:

image

After that, if I click Sign In, I can successfully login to my new site collection. The following is the screenshot of it:

image

That’s it. In the next post, I will explain how you can create a custom Login Page for this Form Based Authentication and also how to create a custom Login Web Part for the same purpose which will enhance the functionality and provide better user experience in real life scenario.

Create a Dynamic User Control using Silverlight Client Object Model –upload Image, Add, Create, Delete both List and List items and populate current items [Part 2]

In my last post, I talked about how we can create new list, delete a list using Client Object Model. In this part, I am going to talk about how to create new List Items, retrieve those, update and delete items. I will also talk about how to upload an image directly to an Image Column of a list from the same Silverlight user control.

Retrieving List Items:

If you read the previous post, I had one column called “category” from the six fields that I created for the List “TechnoProducts”. In order to populate the drop down box “Category”, in this particular example, I click on “Get List Items” button to populate my user control fields. If there is no existing item in the list, it will only populate the drop down box with some predefined categories that I mentioned while creating the list.

Here, I wanted to show the titles of each List Items once you click “Get List Items” button and then after clicking on each item, I wanted to show the details.

For this to happen, I added the following code inside my event handler for clicking on Get Items Button.

private void txtGetItems_Click(object sender, RoutedEventArgs e)
{
    if (listProducts.Items.IsReadOnly)
        listProducts.ItemsSource = null;
    else
        listProducts.Items.Clear();
    CreateProgressBar("Getting Product Items..");
    GetItems();
}

Here, as you can see first of all, I am clearing the existing values of Products List and then creating a progress bar to let the user know about what is going to happen and then I am calling a method to get the items which is as below:

private void GetItems()
{
    var web = context.Web;
    getList = web.Lists.GetByTitle("TechnoProducts");
    context.Load(getList);

    techItems = getList.GetItems(CamlQuery.CreateAllItemsQuery());
    context.Load(techItems);
    context.ExecuteQueryAsync(getItemSuccess, failedCallback);
}
private void getItemSuccess(object sender, ClientRequestSucceededEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        foreach (var item in techItems)
        {
            item.Tag = item["Title"].ToString();
        }
      
        if (listProducts.Items.IsReadOnly)
            listProducts.ItemsSource = null;
        else
            listProducts.Items.Clear();
      
        listProducts.ItemsSource = techItems;
        cbCategory.ItemsSource = new string[] { "Electronics", "Desktop Pc", "Laptop", "Mobile", "PC Accessories" };
        
    });
}

In this above code, I have called my List and then stored the values of the List items in a ListItemCollection object which I declared globally. Inside the successfulcallback event handler, at first, I have stored the Title of each list item inside the Tag property which I used to display the names inside Products ListBox. I set the data source of my ListBox as this ListItemCollection object and populated the drop down box with the string array.

Displaying the details of each List Item:

After retrieving the list items, I now wanted to show the details of each items. So if there is any list item, I am going to show it inside the user control. I will talk about how I added these items shortly after this.

   private void listProducts_SelectionChanged(object sender, SelectionChangedEventArgs e)
   {
       lblImageStatus.Content = string.Empty;
       if (listProducts.SelectedIndex == -1)
       {
           txtTitle.Text = string.Empty;
           txtModel.Text = string.Empty;
           txtStock.Text = string.Empty;
           txtUnitPrice.Text = string.Empty;
           cbCategory.SelectedIndex = 0;
           chkDiscontinued.IsChecked = false;
           productImg.Source = null;
           txtTitle.Focus();
       }
       else
       {
           var item = (ListItem)listProducts.SelectedItem;
           txtTitle.Text = item["Title"].ToString();
           txtModel.Text = item["ModelNo"].ToString();
           txtStock.Text = item["InStock"].ToString();
           txtUnitPrice.Text = item["UnitPrice"].ToString();
           var chk = (bool)item["Discontinued"];
           chkDiscontinued.IsChecked = chk;
           
           BitmapImage img = new BitmapImage();
           var url=(FieldUrlValue)item["Image"];
           if (url != null)
           {
               string urlvalue = url.Url.ToString();
               productImgUrl = urlvalue;

               Uri uri = new Uri(urlvalue);
               img.UriSource = uri;
           }
           if (img != null)
               productImg.Source = img;
       
           var cbItems = cbCategory.Items;
           for (int i = 0; i < cbItems.Count; i++)
           {
               string cbItem = cbItems[i].ToString();
               if (cbItem == item["Category"].ToString())
               {
                   cbCategory.SelectedIndex = i;
                   break;
               }
           }

       }

   }

I added the code inside the Selection Changed Event handler and what I did was checked the Mode of the current state whether it is in Add Items state or not. If not, then I went with showing each property of a selected list item. Notice that, to show the Image of an specific item, I created a BitmapImage object to get the property and display inside my user control where I set the source as current item’s Image. Lastly, I set the current category for that item which I tried to match using a for loop (There are better ways to do it though!).

This is a screenshot of how it looks like once I selected a List item from the ListBox:

image

Adding a New List Item:

For adding a new item, I wanted to clear out my user control, so I added the following code:

private void txtAddItems_Click(object sender, RoutedEventArgs e)
{
    listProducts.SelectedIndex = -1;
    productImgUrl = string.Empty;
}

And after filling out each field inside the user control and clicking the save button, I get the following screen:

image

After adding an item, I also added a notification message whether or not the item was added in the list. Now, here comes the big part: To save the list item based on its mode whether adding or editing an item.

Saving and Updating a List Item:

Below is the code for event handler of Save button:

private void btnSave_Click(object sender, RoutedEventArgs e)
{
    ListItem item = null;
    if (listProducts.SelectedIndex == -1)
    {                        
        ListItemCreationInformation lci = new ListItemCreationInformation();
        item = getList.AddItem(lci);
    }
    else
    {       
        item = (ListItem)listProducts.SelectedItem;   
    }
   
    if (imgFile != null)
    {
        context.Load(web);
        List picLib = web.Lists.GetByTitle("RadiantProducts");
        context.Load(picLib);

        byte[] bFile = ReadFully(imgFile.OpenRead());
        FileCreationInformation file = new FileCreationInformation();
        file.Content = bFile;
        string picUrl = imgFile.Name.ToString();
        file.Url = "http://www.teamradiant.net/RadiantProducts/&quot; + picUrl;
        productImgUrl = file.Url;
        file.Overwrite = true;
        Microsoft.SharePoint.Client.File newFile = picLib.RootFolder.Files.Add(file);
        context.Load(newFile);
        context.ExecuteQueryAsync(succeedCallback, failedCallback);
    }

    item["Title"] = txtTitle.Text;
    item["Category"] = cbCategory.SelectedItem;
    item["ModelNo"] = txtModel.Text;
    item["UnitPrice"] = txtUnitPrice.Text;
    item["InStock"] = txtStock.Text;
    item["Discontinued"] = chkDiscontinued.IsChecked;
    item["Image"] = productImgUrl;
    item.Update();
    if(listProducts.SelectedIndex == -1)
        CreateProgressBar("Adding Product Item..");
    else
        CreateProgressBar("Updating Product..");
    context.ExecuteQueryAsync(saveSuccess, failedCallback);

}
private void saveSuccess(object sender, ClientRequestSucceededEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        GetItems();
        lblImageStatus.Content = "Saved Successfully";
    });
}

As you can see here, I checked the mode whether I am adding or updating an Item. If it is in Add mode, I created a ListItemCreationInformation object and adding this info to my existing List getList which I got from calling GetItems method and assigned it to a new List item object.

To add a new image, I declared a FileInfo variable imgFile which I used to check the mode of adding or updating. To add an image directly to my List TechnoProducts, I have created an Picture Library List named RadiantProducts. As you might know that, in SharePoint, we can only add an existing Image URL to an Image Column of a list; so, behind the scene, first of all, I added the selected image using an Open File Window, read the file as Bytes and after that, I set the URL of this image using the name of the Image File. Then, I stored the image directly to the root folder of the Picture Library. Here is the code to read the image as a binary file:

public static byte[] ReadFully(Stream input)
  {
      byte[] buffer = new byte[16 * 1024];
      using (MemoryStream ms = new MemoryStream())
      {
          int read;
          while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
          {
              ms.Write(buffer, 0, read);
          }
          return ms.ToArray();
      }
  }

After that, it is pretty simple as I just assigned the current property of the List item with the values from the user control and updated it. Notice that, for the image I now can set the Image Url as I stored it beforehand.

Here is the code for selecting an image which opens up a Open File Dialog window:

private void btnSelect_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog ofd = new OpenFileDialog();
    ofd.Filter = "JPEG Files (*.jpg)|*.jpg|PNG Files (*.png)|*.png";
    ofd.Multiselect = false;
    ofd.ShowDialog();
    imgFile = ofd.File as FileInfo;
    if (imgFile != null)
    {
        image = new BitmapImage();
        image.SetSource(imgFile.OpenRead());
        productImg.Source = image;
    }
    
}

In this above code, it will just open up a window to select a File of JPG/PNG type and after selecting it will display in the Image control as I set the source after reading the file. After saving a list item, I called the previously defined GetItems method to retrieve the updated List where it will also show if there is any newly added item.

Deleting a List Item:

Following is the code by which I can delete a selected list item after clicking on “Delete Item”:

private void btnDeleteItem_Click(object sender, RoutedEventArgs e)
{
    var web = context.Web;
    var item1 = (ListItem)listProducts.SelectedItem;
    string delTitle = item1["Title"].ToString();
    var delList = web.Lists.GetByTitle("TechnoProducts");
    var query=new CamlQuery();
    query.ViewXml = "<View>" +
        "<Query>" +
        "<Where><Eq>" +
        "<FieldRef Name='Title'/>" +
        "<Value Type='Text'>" + delTitle + "</Value>" +
        "</Eq></Where>" +
        "</Query>" +
        "<ViewFields>" +
        "<FieldRef Name='ID'/>" +
        "<FieldRef Name='Title'/>" +
        "</ViewFields>" +
        "</View>";
     

     techItems = delList.GetItems(query);
     context.Load(techItems);
     context.ExecuteQueryAsync(delCallSuccess, failedCallback);
   
}

 

In this above code, I am getting the current web from current client context. Then, I created a variable to get Selected List Item from the List Box. After that, I stored the Title of that item in a string variable which I used inside the CAML query later. The CAML query will retrieve ID and Title of an item from the List which matches the Title of the selected item. I then stored in inside a globally defined ListItemCollection which I used before and then called ExecuteQueryAsync . Following is the code for the successful callback of this action:

private void delCallSuccess(object sender, ClientRequestSucceededEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        foreach (ListItem item in techItems)
        {
            item.DeleteObject();
        }
        context.ExecuteQueryAsync(deleteItemSuccess, failedCallback);
    });
}
private void deleteItemSuccess(object sender, ClientRequestSucceededEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        GetItems();
        lblImageStatus.Content = "Item Deleted Successfully";
    });
}

In this above code, I deleted the item using DeleteObject method from SharePoint’s Client Object Model API. As I am only deleting one item, the for loop here will only get executed once. After that, I have another callback where for the successful handler, I am calling GetItems  method to show the updated List Items with a message that the item was deleted successfully. Here is the screenshot:

image

Here, I am deleting the product Samsung and right after that I get the following screen I don’t see this item anymore inside my List Box.

image

As you can see, after deleting the item, I can see the updated items Title inside the List Box.

That’s all for today. Next time, I will talk about how you can use Client Object Model using Javascript/ECMAscript. Thanks for reading and I will always appreciate if you can come up with a better solution and technique.

Create a Dynamic User Control using Silverlight Client Object Model –upload Image, Add, Create, Delete both List and List items and populate current items [Part 1]

Today, I am going to discuss about using the Silverlight Client Object Model in SharePoint 2010. You can actually develop scintillating user controls using Silverlight in SharePoint 2010. I am going to discuss the development work in two phases. In this part, I am going to show you how you can create and delete a List.

Alright, to begin with, let me clarify few things: Silverlight Client Object Model almost works in the same way as .NET Managed mode except in few areas, one of them is executing the request. In .NET Managed mode, the actual request and response happens synchronously whereas in Silverlight, it is Asynchronous. Therefore, we have to use ExecuteQueryAsync instead of ExecuteQuery. ExecuteQueryAsync must have two event handlers as parameters: one for a successful callback and another for a failed callback. Another big difference is that in .NET Managed mode, you code set the Authentication mode to default, form based, windows. But, here you do not have such option. We have two options to test our Silverlight user control in COM:

First method: To upload the build "XAP” file in SharePoint’s document library and then add a Silverlight Web Part and define the URL there.

Second method: To test the user control right from the VS2010, we need to create an XML file inside the Web Application Root (inside Intepub\wwwroot\…[Your Port No. for Web App] Directory] and name that file as ClientAccessPolicy.xml as by default SharePoint does not allow cross domain access [When you test your user control using VS2010, it uses localhost to test and it is considered as Cross Domain against your SharePoint web app].

Let me begin with explaining the demo:

First of all, I created a new Silverlight Project from Visual Studio 2010. While creating the project, VS will prompt you whether you want to add an ASP.NET test web page to test your user control; choose ‘Yes’ and always remember to select ASP.NET 3.5 version as SharePoint does not support framework 4 as of now. After that, your solution explorer should look like this:

image

You can see that I named my project as “DynamicListSilver”. The upper one shows my XAML file and at the bottom you can see the Test Page with a web.config which was automatically created by VS.

Now, I have designed my User Control as below:

image

Here, at the top, you can see some buttons which are self-explanatory. Beneath those buttons, I have one List Box to show my current List items and a dynamic Progress Bar. At the right side, the details of a particular list item will be shown once an item is selected from the List Box. Below the Product Image label, there is a status bar label which will show the messages from successful and failed callback and also display the actual happening of a list item and the list. Beneath that, right above the Save button, I have added an Image control which will be displayed for the current Item if any image exists for that and after selecting an Image locally to upload using “Select” button which will open up a File Browser Dialogue.

Here is the code for this particular user control:

<UserControl x:Class="DynamicListSilver.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
    mc:Ignorable="d"
    d:DesignHeight="483" d:DesignWidth="604" xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input" Loaded="UserControl_Loaded">

    <Grid x:Name="LayoutRoot" Background="White" Height="488" Width="577">
        <StackPanel Height="485" HorizontalAlignment="Left" Margin="10,0,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="561"></StackPanel>
        <Canvas Name="canvas1" Margin="10,0,0,12" HorizontalAlignment="Left" Width="555">
            <Button Content="Get List Items" Height="23" Name="txtGetItems" Width="87" IsTabStop="True" VerticalContentAlignment="Bottom" HorizontalContentAlignment="Center" IsEnabled="True" IsHitTestVisible="True" Canvas.Left="81" Canvas.Top="0" Click="txtGetItems_Click" />
            <Button Content="Create List" Height="23" Name="txtCreate" Width="75" Canvas.Left="0" Canvas.Top="0" Click="txtCreate_Click" />
            <Button Canvas.Left="383" Canvas.Top="0" Content="Delete List" Height="23" Name="btnDelete" Width="75" Click="btnDelete_Click" />
            <ListBox Canvas.Left="6" Canvas.Top="29" Height="401" Name="listProducts" ItemsSource="{Binding}" Width="162" SelectionChanged="listProducts_SelectionChanged" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Tag}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>

            </ListBox>
            <dataInput:Label Canvas.Left="259" Canvas.Top="29" Height="26" Name="label1" Width="139" Content="List item Details" FontWeight="ExtraBlack" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="62" Height="24" Name="label2" Width="66" Content="Title" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="92" Height="24" Name="label3" Width="66" Content="Category" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="122" Height="24" Name="label4" Width="66" Content="Unit Price" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="155" Height="24" Name="label5" Width="66" Content="Stock" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="214" Height="24" Name="label6" Width="74" Content="Discontinued" />
            <TextBox Canvas.Left="259" Canvas.Top="62" Height="23" Name="txtTitle" Width="189" />
            <ComboBox Canvas.Left="261" Canvas.Top="92" Height="23" Name="cbCategory" Width="137" />
            <TextBox Canvas.Left="261" Canvas.Top="122" Height="23" Name="txtUnitPrice" Width="120" />
            <TextBox Canvas.Left="261" Canvas.Top="151" Height="23" Name="txtStock" Width="89" />
            <CheckBox Canvas.Left="261" Canvas.Top="214" Content="Yes/No" Height="24" Name="chkDiscontinued" Width="69" />
            <Button Canvas.Left="261" Canvas.Top="436" Content="Save" Height="23" Name="btnSave" Width="75" Click="btnSave_Click" />
            <Button Canvas.Left="174" Canvas.Top="0" Content="Add Item" Height="23" Name="txtAddItems" Width="104" Click="txtAddItems_Click" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="241" Height="20" Name="label7" Width="84" Content="Product Image" />
            <Button Canvas.Left="261" Canvas.Top="241" Content="Select.." Height="23" Name="btnSelect" Width="75" Click="btnSelect_Click" />
            <Image Canvas.Left="259" Canvas.Top="293" Height="137" Name="productImg" Stretch="Fill" Width="237" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="185" Height="24" Name="label8" Width="74" Content="Model No." />
            <TextBox Canvas.Left="261" Canvas.Top="185" Height="23" Name="txtModel" Width="120" />
            <dataInput:Label Canvas.Left="174" Canvas.Top="267" Content="" Height="20" Name="lblImageStatus" Width="375" />
            <Button Canvas.Left="283" Canvas.Top="0" Content="Delete Item" Height="23" Name="btnDeleteItem" Width="94" Click="btnDeleteItem_Click" />
        </Canvas>
    </Grid>
</UserControl>

Let me now go through with each segment of this solution:

CREATING A LIST

To create a list in Client Object Model, there is few differences than Server Object Model as we define the List Columns and fields inside XML directly. In this demo, on my User Control Load event, I added the following code the load the current Client Context:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            context = ClientContext.Current;
            if (context == null)
                context = new ClientContext("http://www.teamradiant.net/&quot;); ;
            web = context.Web;
        }

Obviously, context and web objects were declared globally to access it from any method. As you can see, the code here is exactly as we write in .NET Managed mode. Now, when I wanted to create the list in the event handler of the Button “Create List” as below:

Creating List Snippet
listProducts.Items.Clear();
CreateProgressBar("List is getting created");

var lci = new ListCreationInformation();
lci.Title = "TechnoProducts";
lci.Description = "A List of Electronics & Techno Products";
lci.QuickLaunchOption = QuickLaunchOptions.On;
lci.TemplateType = (int)ListTemplateType.GenericList;
list=web.Lists.Add(lci);
var fields = list.Fields;

var titleField = fields.GetByTitle("Title");
titleField.Title = "ProductName";
titleField.Update();

As you can see from the above code that first of all, I cleared the List Box (on the left side). Then, I created a Progress Bar which will be populated dynamically inside the list box.

When you create a custom List in SharePoint, by default, it creates the “Title” field automatically. So here, I changed the column name “Title” to “ProductName”. Now, updating the field won’t actually happen unless we call the executequeryasync method. I have not done that yet as I need to some more fields for my List.

Here is the code for the progress bar:

public void CreateProgressBar(string progressContent)
{
    
    if (listProducts.Items.IsReadOnly)
        listProducts.ItemsSource = null;
    else
        listProducts.Items.Clear();

    listProgress = new ProgressBar();
    listProgress.Width = 100;
    listProgress.Height = 15;
    listProgress.Value = 10;
    listProgress.Maximum = 100;
    listProgress.Background = new SolidColorBrush(Colors.Gray);
    listProgress.Foreground = new SolidColorBrush(Colors.White);
    _timer.Duration = TimeSpan.FromMilliseconds(10);
    _timer.Completed += new EventHandler(_timer_Completed);
    _timer.Begin();

    Label lblProgress = new Label();
    lblProgress.Content = progressContent;
    listProducts.Items.Add(lblProgress);
    listProducts.Items.Add(listProgress);
}

And, here is the code for the Timer Event Handler:

void _timer_Completed(object sender, EventArgs e)
      {
          if (listProgress.Value < listProgress.Maximum)
          {
              listProgress.Value++;
              _timer.Begin();
          }
      }

As I used the progress bar on various occasions, I declared the following objects globally:

Storyboard _timer = new Storyboard();
 ProgressBar listProgress;

Next, I need to add the other columns or fields for my list. Now, to do that I will also need to pass a schema for each field that I add. As the schema is pretty big and hard to memorize, what I did was created a copy of exact same list in SharePoint web front end and got the values for schema using Server Explorer. [Open Server Explorer-> Go to your Web App-> Site Collection –> Site –> Fields and from the properties window, get the schema xml]

Then, I added the fields as below:

var categoryField = fields.AddFieldAsXml(@"<Field Type='Choice' DisplayName='Category' Required='TRUE' EnforceUniqueValues='FALSE' Indexed='FALSE' Format='Dropdown' FillInChoice='FALSE' StaticName='Category' Name='Category'><Default>Electronics</Default><CHOICES><CHOICE>Electronics</CHOICE><CHOICE>Desktop PC</CHOICE><CHOICE>Laptop</CHOICE><CHOICE>Mobile</CHOICE><CHOICE>PC Accessories</CHOICE></CHOICES></Field>", true, AddFieldOptions.DefaultValue);
var unitPriceField = fields.AddFieldAsXml(@"<Field Type='Currency' DisplayName='UnitPrice' Required='TRUE' EnforceUniqueValues='FALSE' Indexed='FALSE' Decimals='2' LCID='1033' StaticName='UnitPrice' Name='UnitPrice' />", true, AddFieldOptions.DefaultValue);
var stockField = fields.AddFieldAsXml(@"<Field Type='Number' DisplayName='InStock' Required='FALSE' EnforceUniqueValues='FALSE' Indexed='FALSE' Decimals='0' StaticName='InStock' Name='InStock' />", true, AddFieldOptions.DefaultValue);
var modelField = fields.AddFieldAsXml(@"<Field Type='Text' DisplayName='ModelNo' Required='TRUE' EnforceUniqueValues='FALSE' Indexed='FALSE' MaxLength='255' StaticName='ModelNo' Name='ModelNo' />", true, AddFieldOptions.DefaultValue);
var discontinuedField = fields.AddFieldAsXml(@"<Field Type='Boolean' DisplayName='Discontinued' EnforceUniqueValues='FALSE' Indexed='FALSE' StaticName='Discontinued' Name='Discontinued'><Default>0</Default></Field>", true, AddFieldOptions.DefaultValue);
var imageField = fields.AddFieldAsXml(@"<Field Type='URL' DisplayName='Image' Required='FALSE' EnforceUniqueValues='FALSE' Indexed='FALSE' Format='Image' StaticName='Image' Name='Image' />", true, AddFieldOptions.DefaultValue);
context.ExecuteQueryAsync(listcreatesuccess, failedCallback);

Now, when you add the Schema XML from the existing fields of a List there are some extra things which you should delete (generated IDs) and keep stuff clean which looks similar to the above code. I have added 6 more fields in this list which are: Category (Drop Down Choice Type), Unit Price (Currency type), InStock (Number type), ModelNo (Number Type), Discontinued (Yes/No Checkbox type) and an Image (Image type) which will hold the Image url for list item.

As you can see at the bottom of the code, I have called ExecuteQueryAsync which will call the server asynchronously and also passed two event handlers as parameters. Below is the code for successful callback event handler:

private void listcreatesuccess(object sender, ClientRequestSucceededEventArgs e)
 {
     this.Dispatcher.BeginInvoke(() =>
     {

         listProducts.Items.Clear();
         var msg = string.Format("{0} List was created at: {1}", list.Title, DateTime.Now.ToLongTimeString());
         lblImageStatus.Content = msg;
     });
 }

and here is for the failed callback:

private void failedCallback(object sender, ClientRequestFailedEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        var msg = "Error: " + e.Exception.ToString();
        listProducts.Items.Clear();
        listProducts.ItemsSource = null;
        listProducts.Items.Add(msg);

    });
}

That’s it. Now, if you click on “Create List”, your list should be created and you should see the progress bar and a message whether or not the list was created successfully with current time.

listcreated

Above one is a screenshot while creating the list and after completion, a message will be displayed if the creation was successful.

Let me now go through with deleting this list when I will click on “Delete List”.

The code for deleting this list is much simpler than creating it. Here it is:

private void btnDelete_Click(object sender, RoutedEventArgs e)
{
    CreateProgressBar("Deleting List…");
    context.Load(web, w => w.Title);
    context.Load(web.Lists);
    deleteList = web.Lists.GetByTitle("TechnoProducts");
    deleteList.DeleteObject();
    context.ExecuteQueryAsync(listdeletesuccess, listdeletefailure);
}
private void listdeletesuccess(object sender, ClientRequestSucceededEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        listProducts.Items.Clear();
        var msg = string.Format("List was Deleted at: {0}", DateTime.Now.ToLongTimeString());
        lblImageStatus.Content = msg;
    });
}
private void listdeletefailure(object sender, ClientRequestFailedEventArgs e)
{
    this.Dispatcher.BeginInvoke(() =>
    {
        listProducts.Items.Clear();
        var msg = "Error: " + e.Exception.ToString();
        lblImageStatus.Content = msg;

    });
}

As you can see I just called the List using web.Lists.GetByTitle  and then deleted the object from the List deleteList which I declared previously.

Here is a screenshot for deleting this list:

deletelist

You can see the message here after the successful callback for deleting the List. That’s it for this part. In the next part, I am going to discuss about adding, modifying and deleting a List item and also adding an Image directly to this List.  [To Be Continued…]

Dealing with .NET Managed Code in Clinet Object Model

In SharePoint 2010, one of the major changes that Microsoft has brought in development is the Client Object Model which facilitates the developers to use SharePoint 2010 Client APIs and build client based applications such as Windows Application, Silverlight and JavaScript client apps. Today, I am going to show you a nice example of using Client Object Model in .NET Managed code.

For this, first of all, I have populated few tables (Product, Category) from Microsoft Northwind Database as SharePoint lists on my site. In this example, I will deal with two of those tables which are: Product and Category. These are the screenshots of the tables:

image

image

Note that, I have a Lookup Column named ‘Category’ in Product List which is coming from ‘CategoryName’ column of Category List.

After that, I have created a Windows Application in Visual Studio 2010 and designed the following UI:

image

You can clearly see that my intention is to get all the categories and populate them in a Combo box and Products List Box will display all the products for that particular Category and the selected Product will display Product Details inside the Product Details Grid View. I also want to enable selecting multiple products which will make my Grid View more Dynamic so that the number of products you will select from the Product List Box, the same number of Product Details information will be there in the Grid View.

To achieve my goal, I have added the following code inside my Form Load method to initialize the items of Category Combo Box:

Form Load
  context = new ClientContext("http://www.teamradiant.net/&quot;); //Creating New Client Context
  context.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication; //Setting the Authentication Mode
  FormsAuthenticationLoginInfo formLoginInfo=new FormsAuthenticationLoginInfo("morshed","mypassword"); //Providing credentials for Forms Auth
  context.FormsAuthenticationLoginInfo = formLoginInfo; //Passing the credentials to Current Client Context
  web = context.Web;
                  
  var list = web.Lists.GetByTitle("Category"); //Getting the 'Category' List
  var query = new CamlQuery(); //Create a new CAML query
  query.ViewXml = "<View>" +
                 "<ViewFields>" +
                 "<FieldRef Name='CategoryName' />" +
                 "<FieldRef Name='Title' />" +
                 "</ViewFields>" +
                 "</View>"; //CAML Query details
  var items = list.GetItems(query); //Receiving the items from CAML query
  context.Load(items); //Loading the items in Current Context
  context.ExecuteQuery(); //Calling the Client.svc to send the request to SharePoint server and receive the response

  var listData = new List<DictionaryEntry>(); //Creating a Dictionary Data Structure to hold the list items
  foreach (var item in items)
  {
      var data = new DictionaryEntry(item["CategoryName"], item["Title"]); //setting every list item's column value to Key and Value.
     
      listData.Add(data); //Adding the data inside the Dictionary Data Structure object
  }
  CategoryBox.DisplayMember = "Value"; //Set the Display name of Combo Box items to item["Title"]
  CategoryBox.ValueMember = "Key"; //Set the Value of Combo box items to item to item["Key"]
  CategoryBox.DataSource = listData; //Setting the Data Source of Combo Box

You can see from the above code that I am querying for two columns from Category List which are: CategoryName and Title. ‘CategoryName’ is the Lookup column  that I need to pass to the Product List to get the related products for the selected Category.

Now, I add the following code for selected index changed method of the Combo Box:

SelectedIndexChange (ComboBox)
 if (CategoryBox.SelectedIndex == -1)
     return; //If Nothing is selected
 var category = CategoryBox.SelectedValue; //Getting the Selected Value from Category Combo Box

 var list = web.Lists.GetByTitle("Product"); //Getting the 'Product' list from the current site
 var query = new CamlQuery();
 query.ViewXml = "<View>" +
                 "<Query>" +
                 "<Where><Eq>" +
                 "<FieldRef Name='Category'/>" +
                 "<Value Type='Lookup'>" + category+"</Value>" +
                 "</Eq></Where>" +
                 "</Query>" +
                 "<ViewFields>" +
                 "<FieldRef Name='ProductID'/>" +
                 "<FieldRef Name='ProductName'/>" +
                 "</ViewFields>" +
                 "</View>"; //CAML Query details to get 'ProductID' and 'ProductName' for the selected Category
 

 var items = list.GetItems(query); //Get all items from the CAML query

 context.Load(items); //Loading items
 context.ExecuteQuery(); //Calling Client.svc to send the request to the SharePoint server and get the response

 var listData = new List<DictionaryEntry>(); //Creating a Dictionary Data Structure to hold the list items
 foreach (var item in items)
 {
     var data = new DictionaryEntry(item["ProductID"], item["ProductName"]); //setting every list item's column value to Key and Value.
     listData.Add(data); //Adding the data inside the Dictionary Data Structure object
 }
 ProductListBox.DisplayMember = "Value"; //Set the Display name of List Box items to item["Title"]
 ProductListBox.ValueMember = "Key"; //Set the Value of List box items to item to item["Key"]
 ProductListBox.DataSource = listData; ////Setting the Data Source of the List box

Note that, in Client Object Model, unlike the Server Side Object, the actual Loading of list items or data does not happen until we call ExecuteQuery()  which is  synchronous for .NET Managed Code.

Now, To show multiple selected items from the Products list inside the Products Grid View, I have created the following Class:

public class Products
    {
        public string ProductID { get; set; }
        public string ProductName { get; set; }
        public string SupplierID { get; set; }
        public string QuantityPerUnit { get; set; }
    }

To show the selected Products Details in the grid view, I have added the following code block inside the Selected Index Changed method of the Products  List box:

SelectedIndexChange (ListBox)
  1. if (ProductListBox.SelectedIndex == -1)
  2.     return; //If Nothing is selected inside Product List box
  3. var product = ProductListBox.SelectedValue; //get the single selected value of Product List box
  4. string strInProduct = "";
  5.  
  6. var total = ProductListBox.Items.Count; //Total Items inside Product List Box
  7. for(int i=0; i<total ;i++)
  8. {
  9.      if(ProductListBox.SelectedItems.Contains(ProductListBox.Items[i]))
  10.     {
  11.         var product1 = new DictionaryEntry(); //Create a new Dictionary Data Object to hold multiple selected items
  12.         product1=(DictionaryEntry)ProductListBox.Items[i]; //Setting each selected item with Keys and Values
  13.         strInProduct += "<Value Type='Number'>" + product1.Key + "</Value>"; //Modifying the CAML query clause inside 'IN' to check a range of items
  14.     }
  15. }
  16.  
  17. var list = web.Lists.GetByTitle("Product"); //Calling the 'Product' list of current site
  18. var query = new CamlQuery();
  19. query.ViewXml = "<View>" +
  20.                 "<Query>" +
  21.                 "<Where><In>" +
  22.                 "<FieldRef Name='ProductID'/><Values>" +
  23.                 strInProduct +
  24.                 "</Values></In></Where>" +
  25.                 "</Query>" +
  26.                 "<ViewFields>" +
  27.                 "<FieldRef Name='ProductID'/>" +
  28.                 "<FieldRef Name='ProductName'/>" +
  29.                 "<FieldRef Name='SupplierID'/>"+
  30.                 "<FieldRef Name='QuantityPerUnit'/>"+
  31.                 "</ViewFields>" +
  32.                 "</View>"; //Selecting 'ProductID', 'ProductName', 'SupplierID' and 'QuantityPerUnit' for each Product which are IN the range of selected items
  33.  
  34. var items = list.GetItems(query); //Getting the items from CAML query result
  35.  
  36. context.Load(items); //Loading the items in Current Context
  37. context.ExecuteQuery(); //Calling the Client.svc to send request to SharePoint server and get the response
  38.  
  39. List<Products> spProducts = new List<Products>(); //Create a List of 'Products' class type which is already defined
  40. foreach (ListItem item in items)
  41. {
  42.     spProducts.Add(new Products
  43.     {
  44.         ProductID = item["ProductID"].ToString(),
  45.         ProductName=item["ProductName"].ToString(),
  46.         SupplierID=item["SupplierID"].ToString(),
  47.         QuantityPerUnit=item["QuantityPerUnit"].ToString() //Setting each property of 'spProducts' list which is 'Products' class type   
  48.     });
  49. }
  50. gvProductDetails.DataSource = spProducts; //Setting the Data Source for the Grid View with 'spProducts' list

That’s basically it. I have commented about each line of code explaining the details for your understanding.

Now, I will be able to run this application from any machine as this app is using Client Object Model. As long as you use two DLLs in your application (Microsoft.SharePoint.Client & Microsoft.SharePoint.Client.Runtime), you can get the advantage of Client Object Model APIs from any application. Here is a screenshot of the application I discussed until now:

image

As you can see that all the Products for a particular category is showing up inside the list box and the details of those products are being shown inside the Grid View.

Various ways of Customizing the Mobile redirection feature in SharePoint 2010

In SharePoint 2010, there is a great in-built feature for mobile redirection which redirects the users accessing your SharePoint site from any mobile device. Unfortunately, the page where SharePoint redirects is not pretty to look at and also in most cases at enterprise levels, people would not want the users to show the “All Site Content Page” which is the default behavior of mobile redirection in SharePoint 2010. Now, SharePoint 2010 uses Bi-level redirection for mobile device. Those are:

  • IsMobileDevice property inside your web application’s Compat.browser file (Located at C:\inetpub\wwwroot\wss\VirtualDirectories\[Port No. for your web app]\App_Browsers) which is by default set to true.
  • A Hidden Feature called MobilityRedirect which can be activated using PowerShell command:
stsadmn -o activatefeature -name MobilityRedirect -url http://[your site url]

Now, Above methods will result in the redirection of your site for mobile devices which you can also test out from the desktop browser by appending /m/ after your site URL or by appending ?mobile=1. Now, These two will redirect you to two different pages of Mobile View. The first one (/m/) will redirect you to the default.aspx page located at m folder under the All Files of your site and the second one will redirect you to one of the pages (e.g; mblwp.aspx, mblwiki.aspx depending on your site definition and your home page) located inside the following folder:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\MOBILE

Let’s say that you don’t want your users to be redirect to the All Site Content Page which is located inside the above location. To do this, there are some tricks:

Firstly, you can grab the URL where the page is being redirected for mobile pages (?mobile=1) from most of the mobile devices and write a conditional statement to redirect the page to your customized homepage.

Secondly, you can write a custom HTTP handler and fool the SharePoint to change its behavior for your web application. (Remember, this will have effect on all the sites and site collections under that particular web application)

Thirdly, you can turn off the IsMobileDevice property inside the browser settings by setting its value to false.

Finally, you can turn off the hidden feature MobilityRedirect

First Method:

Now, First method is the quickest way to change the default behavior of your SharePoint site’s mobile redirection. To do this go the exact location where your site is being redirected which you can grab from the redirected URL from the mobile device or appending ?mobile=1 after your URL. Go inside the 14 hive\TEMPLATE\LAYOUTS\MOBILE and find out that particular aspx page. You will find the following method at the top of the page inside a script:

protected override Microsoft.SharePoint.SPBasePermissions RightsRequired

Change the code inside this method like the following:

get
    {
        string DevUrl = System.Web.HttpContext.Current.Request.Url.OriginalString.ToString();

        if(DevUrl=="http://dev-s2010:8800/" || DevUrl=="http://dev-s2010:8800/_layouts/mobile/mblwp.aspx?Url=%2Fdefault%2Easpx&mobile=1" || DevUrl=="http://dev-s2010:8800/_layouts/mobile/mblwp.aspx?Url=%2Fdefault%2Easpx")
        {
        Response.Redirect(http://www.customurl.com);   // your Custom URL for Mobile Redirection    
        }

        return base.RightsRequired | Microsoft.SharePoint.SPBasePermissions.ViewPages;
    }

Where http://dev-s2010 is my Site URL. In my case, I was being redirected to mblwp.aspx page.

After doing this, your site will be automatically redirected to the site mentioned inside redirection command. Now, remember, this cannot be a normal SharePoint site URL which will again redirect your user to the All Site Content Page.

Second Method:

As because the SharePoint mobile redirect is done at BeginRequest event (which is the first event in the Page Life Cycle of an ASP.NET page) handler in the SPRequestModule HTTP Module which is part of SP2010. You can write a custom HTTP module to mislead SharePoint making it think that you are using a non-mobile browser like below:

public class MobileRequestHttpModule : IHttpModule
{
    private HttpBrowserCapabilities browserCapabilities;
 
    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
    }
 
    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = sender as HttpApplication;
        browserCapabilities = application.Request.Browser;
        application.Request.Browser = new MobileBrowserCapabilities(browserCapabilities);
    }
 
    void context_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpApplication application = sender as HttpApplication;
        application.Request.Browser = browserCapabilities;
    }
 
    public void Dispose()
    {
    }
}

Using this module we can hook up to the BeginRequest event and temporarily substitute the browser information with our own. The substitute is defined by the MobileBrowserCapabilities class and contains the most of the original information about the current browser except for the one:

public class MobileBrowserCapabilities : HttpBrowserCapabilities
{
    public override bool IsMobileDevice
    {
        get
        {
            return false;
        }
    }
 
    public MobileBrowserCapabilities() { }
 
    public MobileBrowserCapabilities(HttpBrowserCapabilities browserCaps) : this()
    {
        if (browserCaps.Adapters != null && browserCaps.Adapters.Count > 0)
        {
            foreach (object key in browserCaps.Adapters.Keys)
            {
                Adapters[key] = browserCaps.Adapters[key];
            }
        }
 
        Capabilities = browserCaps.Capabilities;
        HtmlTextWriter = browserCaps.HtmlTextWriter;
    }
}

Now, this will also work for all the sites and site collections for that particular web application. So, if you want the default mobile redirection feature for any of your site and site collections, you won’t be able to do that as it works at web application level. To read about more about this particular technique, follow This Article mentioned by Waldek Mastykarz.

Third Method:

If you want to disable the mobile redirection for your site at the web application level (For all sites and site collections of that Web App), there is another way by which you can permanently disable the Mobile Redirection which is to change the property of IsMobileDevice  inside Compat.browser inside the following directory:

C:\inetpub\wwwroot\wss\VirtualDirectories\[ Port No of Web App]\App_Browsers

Find the following line under every browser settings category:

<capability name="isMobileDevice"                    value="true" />

Change the value to value=”false” for every section of browser types.

This is work right away without resetting the IIS.

Fourth method:

If you want to disable the hidden feature for MobilityRedirect, you can do so by running the following command in PowerShell:

stsadmn -o deactivatefeature -name MobilityRedirect -url http://[your site url]

This method will probably not work for the Internet Facing sites as it will still redirect the users to the All Site Content Page.

Now, there is another way for redirection recommended by MSDN which is only possible for the homepage of your site. But for that to happen, one needs to type in upto default.aspx or append /m/ after the URL. The article can be found here ( I have already tested this method and it works).

So far, these are the techniques I have discovered Mobile Redirection for a SharePoint site. I would appreciate if someone has a different or better solution. Till then, we have to be satisfied with these inconvenient methods provided by Microsoft. Good Luck!