Get field value during an event handler.


Hi everyone,

Today I will talk about “event handler” and more specifically those used on elements of lists and libraries.

3 types of events are attached to the lists and libraries: Adding, updating and deleting

Each of its events can be retrieved synchronously (at the time of the action) andasynchronous (right after the end of the action).

It’s very easy to spot if a function is synchronous or asynchronous based on its name.

Action

Synchronous

Asynchronous

Adding an item

ItemAdding

ItemAdded

Updating an item

ItemUpdating

ItemUpdated

Deleting an item

ItemDeleting

ItemDeleted

As you can see, synchronized action’s name ends with “ing” and asynchronous with “ed”.

During these actions, we can know the field values and sometimes the previous value of the field. Ways to retrieve data are:

Properties.ListItem[«FieldName »]

Properties.AfterProperties[«FieldName »]

Properties.BeforeProperties[«FieldName »]

And yes, three possibilities depending on the actions and the synchronicity of the eventcan contain either the new value, or the old value, or nothing at all! And where it reallygets tough, it also depends on the type of list, whether the current list is a list or library.

Given this number of possibilities a little summary table seemed necessary, here it is.

For the lists

Event

ListItem

BeforeProperties

AfterProperties

ItemAdding

X

X

New value

ItemAdded

New value

X

New value

ItemUpdating

Old value

X

New value

ItemUpdated

New value

X

New value

ItemDeleting

Old value

X

X

ItemDeleted

X

X

X

For the libraries :

Event

ListItem

BeforeProperties

AfterProperties

ItemAdding

X

X

X

ItemAdded

X

X

New value

ItemUpdating

Old value

Old value

New value

ItemUpdated

New value

Old value

New value

ItemDeleting

Old value

X

X

ItemDeleted

X

X

X

Those informations are very important to consider when planning a development.

Additional information that is important is that during the upload of a document by a user in a library, adding events are triggered after addition of the upload but before the user can fill in themetadata! This filling triggering the updating events.

If you plan to modify the contents of the properties of this document at the event”ItemAdded”, your users may see a message like this when they will safeguard their metadata:

“File ‘mydoc.pdf’ has been modified by ‘user’ the ‘date'”.

To avoid this error, we can define the event “ItemAdded” as synchronous! Indeed, all asynchronous event can be defined as synchronous!

This will allow us to pre-fill the data in our forms before the user does not grasp anything! Given the asynchronous nature of the ItemAdded, if you change the data content, you are not 100% sure that the form displayed to the user include the new data and thus avoid conflicts backup.

For this we must add the synchronization parameter to our xml definition.


<Receiver>
<Name>EventReceiver1ItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>TutoTaxonomy.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>

In our code we add an update of the column “test”


public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
properties.ListItem["test"] = "test synchro";
properties.ListItem.Update();
}

And now, with this little trick our form is pre-filled every time!

Hope this helps!

Christopher.

Allow anonymous access to custom application pages


Hi everyone,

Today I’ll talk about a problem I encountered some time ago. The project had some applicative pages which needed to be deployed on a site where anonymous access is enabled.

Those pages had to be used both by registered users and anonymous users. All the code of the page had been properly implemented (with some runwithelevatedprivilege), however when accessing the page, anonymous users faced an awful  “access denied“.

After some investigation, it turns out that the application pages generated by VisualStudio inherited LayoutsPageBase class. Looking a little in the msdn documentation, we came across this class definition:

« Represents an application page to which access can be limited to users that possess certain rights. ».

So that’s the problem!

After a little research we came across the UnsecuredLayoutsPageBase class and it’s definition.

« Represents the base class for application pages that explicitly do not require the user to have any permissions, such as the access denied page.».

And then, we just changed the base class from LayoutsPageBase to UnsecuredLayoutsPageBase to fix this permission problem!

UPDATE :

I used to apply this code on SharePoint 2007 but recently when I tested on a 2010, it didn’t worked well.

To have your page working on anonymous on 2010 , you must also override the “AllowAnonymousAccess” function like this

 protected override bool AllowAnonymousAccess { get { return true; } } 

Christopher.

Overview of CKSDev tool

Hello everyone,

Today I’ll talk about an add-in that I use every day in my developments, namely the CKSDEV.

It’s a codeplex project that you can find here: http://cksdev.codeplex.com/

I stumbled upon it almost a year ago and now I can’t do without it! Indeed it adds very useful functionalities to Visual Studio for SharePoint developers.

I’m not going to present all the possibilities of this tool for the simple reason that I do not know them all! I’ll tell you about four big features includes in this tools.

1) « quick deploy » menu :

 

If you right click on your project, you may notice a new menu: “Quick Deploy (CKSDev). “By clicking on this menu you will see a lot of useful links, among them you will find:

« Copy to SharePoint Root » : will copy all files into the 14 hive (the link is present on all files in the solution).

« Copy to GAC/BIN » : allows to update the project DLL in the GAC.

« Attach to All SharePoint processes » : allows to attach to the process to debug SharePoint code.

2) Import Field and Import Content Type

For this tutorial I ‘ve created via the interface of SharePoint a content type named  “CKSDemo” based on the standard content type of lists: Item. I’ve added a column of type”Multiple line of text”, a “Choice”, a “Date and Time”, a “Lookup” and a”Person or Group”

 

In Visual Studio, add a connection to our SharePoint site.

 

Browse to find our content type “CKSDemo”

 

We see a link “Import Content Type” which, when clicked, generates the xml of the content type!

If we now browse to the fields of this content type, we can also import them via the “Import Field” button.

 

Link which, when clicked, generates the xml of the field definition!

3) SharePoint Reference tab :

A small addition very valuable when you want to add a reference to a SharePoint dll, no need to go through all the “. NET” and “recent” tabs , a “SharePoint” tab with the dll has been added!

 

4) New item’s template

When you add an item to your project, new templates are present, the one’s whose name ends with “(CKSDev)”

 

Example with “Branding” which, when selected, creates a series of items in our project.

 

As you can see the following items are automatically generated:

  1. A “CSS” module containing the CSS file and uploading it in the “Style Library”.
  2. A “MasterPages” module containing the MASTER file and the uploading it in the gallery of masterpages
  3. A SiteFeatureReceiver applying customization on all sites of the collection and the site feature associated.
  4. A WebEventReceiver applying customization on new sites and the web feature associated.

That it, as I said I did not talk about everything but you have an overview of the benefits related to this add-in! A big thanks to the development team behind this project!

Christopher

Only Content controls are allowed directly in a content page that contains Content controls


Hello everyone,

Today I’ll talk about an error that often happens when developing custom page layouts.

If you have a page using a custom page layout and that when you browse this page you see the error “Only Content controls are allowed directly in a content page that contains Content controls.“, check your page layout.

There are great chances that the tag “<asp:Content></asp:Content>” became “<asp:content></asp:content> “.

This happens when you use the Visual Studio very useful shortcut to format your code(CTRL + K, D),  it replaces the “C” by a “c” in the tag “<asp:Content>“.

It’s easy to find when you know it but when you seek for the error’s reason, you don’t necessarily think about that …

Hope this help.

Christopher.

Filter a list dynamically


Hello everyone,

Today I’ll talk about a request I had more than once lately, whether it’s with my clients or on msdn forums where the issue came up several times : How to filter a list based a parameter entered by the user?

For that we have the default “TextFilter” webpart, but this webpart has two flaws:

First you need the Enterprise version of SharePoint 2010, then this webpart only use the “equal to” and not the “contains” (when all the requests I have personally been asked a “contains”).

I’ll show you a code snippet to add used in a webpart to give your users the ability to filter your lists.

The idea is to load a “ListViewWebPart” of the targeted list.

 
string listUrl = "Lists/maliste" ; 
SPList list = web.GetList(SPUrlUtility.CombineUrl(web.Url, listUrl)); 
ListViewWebPart lvwp = new ListViewWebPart(); 
lvwp.ListName = list.ID.ToString("B").ToUpper(); 
lvwp.ViewGuid = list.Views[YourCustomViewName].ID.ToString("B").ToUpper(); 
this.Controls.Add(lvwp); 

With this code snippet, you will display the list on the url “listUrl” with the view whose name is equal to “YourCustomViewName”

If you want to display the default view, replace the line

 lvwp.ViewGuid = list.Views[YourCustomViewName].ID.ToString("B").ToUpper(); 

by

 lvwp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper(); 

For the filter, we will use two TextBox, one for the column “LinkTitle” and one for the column “FirstName” and a filter button. The trick will be to create a CAML Query and injecting it in the selected view.

Warning, if the selected view is already filtered, the existing filter will be replaced. In other words if you have a filtered list of column C, if you use the following code snippet, it will not be filtered on column C but on columns A and B.

Let’s start by creating our request.

 
string query = string.Empty; 
string tempAdding = string.Empty; 
if (!string.IsNullOrEmpty(tbA.Text)) 
{ 
query = <Contains><FieldRef Name='A' /><Value Type='Text'>" + tbA.Text + "</Value></Contains>; 
} 
if (!string.IsNullOrEmpty(tbB.Text)) 
{ 
tempAdding = <Contains><FieldRef Name='B' /><Value Type='Text'>" + tbB.Text + "</Value></Contains>; 
query = (query.Length > 0 ? "<And>" + query + tempAdding + "</And>" : tempAdding); 
} 

Now we will inject the query! For this we will go through the xml view to find the tag “Where” where we will replace the existing query with the new.

 
XmlDocument doc = new XmlDocument(); 
doc.LoadXml(lvwp.ListViewXml); 
XmlNode queryNode = doc.SelectSingleNode("//Query"); 
XmlNode whereNode = queryNode.SelectSingleNode("Where"); 
if (whereNode != null) queryNode.RemoveChild(whereNode); 
XmlNode newNode = doc.CreateNode(XmlNodeType.Element, "Where", String.Empty); 
newNode.InnerXml = query.ToString(); 
queryNode.AppendChild(newNode); 
lvwp.ListViewXml = doc.OuterXml; 

We just have to put it all in one update panel and there you are with a custom filter webpart!

Feel free to go further and put the url of the list, the view and fields in the webpart’s parameters for a filter webpart as generic as possible!

Finally here is the complete code of my webpart.

 

 public class WebPart1 : System.Web.UI.WebControls.WebParts.WebPart
    {
        TextBox tbA;
        TextBox tbB;
        Button filterButton;
        UpdatePanel mainUpdatePanel;
        ListViewWebPart lvwp;
        SPList list;

        protected override void CreateChildControls()
        {

            mainUpdatePanel = new UpdatePanel();
            mainUpdatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;

            tbA = new TextBox();
            tbB = new TextBox();

            list = SPContext.Current.Web.GetList(SPUrlUtility.CombineUrl(SPContext.Current.Web.Url, "Lists/FilterList"));
            lvwp = new ListViewWebPart();
            lvwp.ListName = list.ID.ToString("B").ToUpper();
            lvwp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();
            lvwp.ChromeType = PartChromeType.None;



            filterButton = new Button();
            filterButton.Text = "Filter";
            filterButton.Click += new EventHandler(filterButton_Click);



            Controls.Add(new LiteralControl(" Nom : "));
            Controls.Add(tbA);
            Controls.Add(new LiteralControl(" Prenom : "));
            Controls.Add(tbB);
            mainUpdatePanel.ContentTemplateContainer.Controls.Add(filterButton);
            mainUpdatePanel.ContentTemplateContainer.Controls.Add(lvwp);


            this.Controls.Add(mainUpdatePanel);
        }

        private void filterButton_Click(object sender, EventArgs e)
        {
            string query = string.Empty;
            string tempAdding = string.Empty;
            if (!string.IsNullOrEmpty(tbA.Text))
            {
                query = "<Contains><FieldRef Name='LinkTitle' /><Value Type='Text'>" + tbA.Text + "</Value></Contains>";
            }
            if (!string.IsNullOrEmpty(tbB.Text))
            {
                tempAdding = "<Contains><FieldRef Name='Prenom' /><Value Type='Text'>" + tbB.Text + "</Value></Contains>";
                query = (query.Length > 0 ? "<And>" + query + tempAdding + "</And>" : tempAdding);
            }

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(lvwp.ListViewXml);
            XmlNode queryNode = doc.SelectSingleNode("//Query");
            XmlNode whereNode = queryNode.SelectSingleNode("Where");

            if (whereNode != null) queryNode.RemoveChild(whereNode);
            XmlNode newNode = doc.CreateNode(XmlNodeType.Element, "Where", String.Empty);
            newNode.InnerXml = query.ToString();
            queryNode.AppendChild(newNode);
            lvwp.ListViewXml = doc.OuterXml;

        }

Christopher.