Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

Considering a simple example of a webpart that adds items to a list based on a simple button click:

public partial class MyVisualWebPartUserControl : UserControl
{
    private int cont = 0;

    protected void MyButton_Click(object sender, EventArgs e)
    {
        using (SPSite site = new SPSite("http://localhost"))
        {
            using (SPWeb web = site.OpenWeb())
            {
                SPList list = web.Lists["MyList"];
                SPListItem item = list.Items.Add();
                item["Title"] = "Title " + cont++;
                //......
            }
        }
    }
}

The title of the element, on each click of a button, should be:

Title 0
Title 1
Title 2
Title 3
Title 4
etc

Off course this isn't the case and this is outputted:

Title 0
Title 0
Title 0
Title 0
Title 0
etc

What options do I have to be able to manage such state inside the webpart?

Can I make it "per user" (each user with his own counter)?

share|improve this question
add comment

3 Answers

up vote 1 down vote accepted

When dealing with counters of any kind I like to use different approach. Even there are lots of other possibilities I like to store counter values in separated list (counter list).

In this case counter list will store value pairs: userId, current count.

Here is code:

cont =  GetNextId(SPContext.Current.Web.CurrentUser.ID);

private int GetNextId(int userId)
{
    var listName = "MyCounter";
    var nextId = 1;
    var webUrl = SPContext.Current.Web.Url;
    //Lets run things elevated
    SPSecurity.RunWithElevatedPrivileges(delegate
    {
        using (var site = new SPSite(webUrl))
        {
            using (var web = site.OpenWeb())
            {
                // Important part
                web.AllowUnsafeUpdates = true;
                var counterList = web.Lists.TryGetList(listName);
                if (counterList == null)
                {
                    // List doesn't exist
                    var listGuid = web.Lists.Add(listName, "", 
                                     SPListTemplateType.GenericList);
                    counterList = web.Lists[listGuid];
                    counterList.Fields.Add("counter", SPFieldType.Number, 
                                                        false, false, null);
                    // You can hide list
                    counterList.Hidden = true;
                    counterList.Update();
                    // You can even strip all permissions 
                    // to be sure noone can alter it
                    counterList.BreakRoleInheritance(false);
                }

                // Find user Title == UserId
                var query = new SPQuery();
                query.Query = string.Format(@"<Where><Eq><FieldRef Name=""Title""/>
                             <Value Type=""Text"">{0}</Value></Eq></Where>", userId);
                var items = counterList.GetItems(query);

                if (items.Count == 0)
                {
                    // No user. Let's create one
                    var counterNew = counterList.Items.Add();
                    counterNew["Title"] = userId;
                    counterNew["counter"] = 2;
                    counterNew.Update();
                }
                else
                {
                    // User found. Read current counter value
                    // and increment it by one
                    var result = Convert.ToInt32(items[0]["counter"].ToString());
                    var item = counterList.GetItemById(items[0].ID);
                    item["counter"] = result + 1;
                    item.Update();
                    nextId = result;
                }
                web.AllowUnsafeUpdates = false;
            }
        }
    });

    return nextId;
}

Is it overkill? Yes it is, but from past experience I can say it is bulletproof solution... almost.

share|improve this answer
    
"...from past experience...". What are some gotchas or issues you can go into if using for example a (standard) solution like Sébastien Levert suggested? –  JohnDoDo Oct 6 '11 at 7:16
    
eg. performance: Let's say you have list with 5000+ items. Running query against it is much more slower then keeping counter(s) in separate list. Next: Possibility of receiving two identical ids is lower in my example. Don't get me wrong, there is nothing wrong with @SébastienLevert suggestion expecially for simple scenarios. –  Vedran Rasol Oct 6 '11 at 7:29
    
I'm going for this solution eventually because it's global (just found out that if I add the webpart to more pages the counter starts at zero for each, i.e. the state is related to the page). Also, if I access the counter from a child user control I must go up in the parent webpart for the counter and also call its SetPersonalizationDirty method which isn't easily accessible from (non inheritance) child controls. Thanks! –  JohnDoDo Oct 6 '11 at 9:47
1  
If you are using more then one web part you can store default counter list name in web part custom property and then each web part can have unique counter list. –  Vedran Rasol Oct 6 '11 at 9:51
    
Yeah! That's another great idea. Thanks again! –  JohnDoDo Oct 6 '11 at 9:53
add comment

You can use the WebBrowsable properties of a property in a web part. Like the following :

[WebBrowsable(true),
WebDisplayName("Items count"),
WebDescription("Items count"),
Personalizable(PersonalizationScope.User)]
public int Count { get; set; }

EDIT :

In the case (see the comments below) where you don't want to check-in/check-out the complete page, you could use some logic to query the list based on a simple CAML query, get the items count by user in the list, and adding +1 to that count ?

share|improve this answer
    
Then you can use this.SetPersonalizationDirty(); to save the property's value after you update it. –  Kit Menke Oct 5 '11 at 14:50
    
Actually... I'm not sure he wants to do this because if the page requires check in/out then you'd have to edit the entire page? –  Kit Menke Oct 5 '11 at 14:53
    
That makes lots of sense... It clearly depends on his needs... But that element could affect the solution. –  Sébastien Levert Oct 5 '11 at 15:11
    
You need to be careful if using CAML query and items count like Sébastien suggested. If you for example delete one item in list (not last one you added) you will have duplicate numbers. You can avoid it by storing count number in separate field in each item. Then you can query looking for biggest one and add +1 to it. This approach however has some other issues. –  Vedran Rasol Oct 5 '11 at 19:57
    
+1 for letting me know about those attributes. But I eventually accepted @Vedran Rasol answer for the reasons stated in my comment to his answer. Thanks! –  JohnDoDo Oct 6 '11 at 9:50
add comment

Not sure how well this'll work, but could you save the value of your cont variable in the ViewState bag each time you increment it?

share|improve this answer
    
I think he wants it to be available across multiple visits to the webpart. The viewstate will only be available during a single visit of the page. Am I wrong on that one ? –  Sébastien Levert Oct 5 '11 at 16:00
    
In that case, I'm certain just writing to the Web Part Property would do it. Properties aren't persisted to the List Item of the Page, so it shouldn't need checking out. –  James Love Oct 5 '11 at 16:13
    
Hmm.. I was trying this because I was curious and I keep getting the error The file is not checked out. You must first check out this document before making changes. when I use this.SetPersonalizationDirty(); in my web part. Edit: Looks like this is because it is a publishing page rnowik.com/… –  Kit Menke Oct 5 '11 at 16:21
    
Gotta love the Publishing Infrastructure for it's "quirks". –  James Love Oct 5 '11 at 16:37
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.