Показаны сообщения с ярлыком Sharepoint 2010. Показать все сообщения
Показаны сообщения с ярлыком Sharepoint 2010. Показать все сообщения

суббота, 1 сентября 2012 г.

Search in XsltListViewWebPart


I had the following problem in our project: my customer has a long list with many text fields. I should give his users a tool for quick navigation in this list, as well as for searching  and editing elements. The best solution was a text filter. When a user enters a text into it, the list automatically is filtered by all columns as follows:
First I added XsltListViewWebPart (XLVWP) with a default view, then I added an input text box with a 'Search' button:
<input type="text" name="searchText" />
<button type="submit">Search</button>

I configured a new ParameterBinding element in the XLVWP to bind it with my text box:
<ParameterBinding Name="SearchText" Location="Form(searchText)" DefaultValue="" />

Into View parameter I have added following query:
<Query>
  <Where>
    <Or>
      <Or>
        <Contains>
          <FieldRef Name="Title"/>
          <Value Type="Text">{SearchText}</Value>
        </Contains>
        <Contains>
          <FieldRef Name="Author"/>
          <Value Type="Text">{SearchText}</Value>
        </Contains>
      </Or>
      <Contains>
        <FieldRef Name="PostCategory"/>
        <Value Type="Text">{SearchText}</Value>
      </Contains>
    </Or>
  </Where>
  <OrderBy>
    <FieldRef Name="PublishedDate" Ascending="FALSE"/>
  </OrderBy>
</Query>

Now that I enter a text into my filter text box and press  'Search' button my list is filtered by Title, Author and Category columns. I can see here 3 important problems:
1.       the user has to press  'Search' button to start filtering instead of simply entering the text
2.       The user has to wait for page reload
3.        When the user first opens this page the list is empty because the filter is empty.
I started fixing these problems one by one. First I added asynchronous update to my list view. Check 'Enable Asynchronous Update' and 'Show Manual Refresh Button' in the properties of XLVWP :

 

Users have got a manual refresh button in the right-hand upper corner of the list:

When they enter a text into the filter text box and press this button, XLVWP is filtered without the page reload.  I found an event receiver in IE developer tools:

javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6eactl02','cancel');return false;

I added it to the onkeyup event handler of my filter text box and then removed  'Search' button:
Search: <input onkeyup="javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6ea$ctl02','cancel');" />

Great, now the list is filtered without page update while the user inputs the text. Ok, but there remains the last problem: an empty list when the user first comes to the page. To solve it I used a calculated field in my list: _TitleToFilter with formula: ="###"&Title. Then I added a default value to the binding parameter: ###
<ParameterBinding Name="SearchText" Location="Form(searchText)" DefaultValue="###" />

In the query I replaced Title column with _TitleToFilter:
<Contains>
  <FieldRef Name="_TitleToFilter"/>
  <Value Type="Text">{SearchText}</Value>
</Contains>

Now that the filter is empty, the sequence of three sharps (###) is used as a filter pattern. And all items have this substring in their _TitleToFilter column.

Ok, but a new problem occured: when the user clears the filter text box, the list becomes empty. The default value does not apply because the filter sends a postback parameter but with an empty value. So I added a new hidden field to send the filter value to my XLVWP and fill this field with javascript while the user enters the text into the filter:

<input type="hidden" name="searchText" id="searchText" />
Search: 
<input onkeyup="document.getElementById('searchText').value = this.value == '' ? '###' : this.value; javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6ea$ctl02','cancel');" />

Now it works perfectly. There is no need for the manual refresh button now. To remove it form XLVWP you can just uncheck 'Show Manual Refresh Button' in its properties.

вторник, 14 февраля 2012 г.

Sharepoint 2010 Forms Templates Sharepoint 2010 Forms Templates


In this article I'll tell you how to change standard templates for Edit, Display and New Forms for List Items and Documents in Sharepoint 2010. You can make it with Sharepoint Designer or InfoPath for partucular lists. This is described here:

But what if you need to change all forms of templates in the farms with your custom form through wsp-package? While I was investigating this question I found many suggestions to change file 14\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx:

This template is used for default list edit, display and to create form of list items:
<SharePoint:RenderingTemplate id="ListForm" runat="server">
  <Template>
    …
  </Template>
</SharePoint:RenderingTemplate>
 
And this one is used for documents:
<SharePoint:RenderingTemplate id="DocumentLibraryForm" runat="server">
  <Template>
    …
  </Template>
</SharePoint:RenderingTemplate>

Yes, if you change these templates and make iisreset you'll get what you want — a new template for all forms.  So, what if we don't want to change the system files? Okey, let's look at the control, that manages templates in CONTROLTEMPLATES folder: SPControlTemplateManager. Method GetTemplateByName(String) of this class returns the template of RenderingTemplate control by its ID. All templates are loaded to the static HASH-table after the first call of this method. That is why we should make iisreset after changing DefaultTemplates.ascx file. So let's see how it works:

private static Hashtable GetTemplateCollection()
{
    if ((s_templateTable == null) && (HttpContext.Current != null))
    {
        lock (InternalSyncObject)
        {
            if (s_templateTable == null)
            {
                Hashtable templateTable = new Hashtable();
                FileInfo[] files = new DirectoryInfo(HttpContext.Current.Server.MapPath(systemTemplateLocation)).GetFiles("*.ascx");
                string controlTemplateFile = systemTemplateLocation + defaultTemplateFile;
                string str3 = systemTemplateLocation + mobileDefaultTemplateFile;
                LoadControlTemplate(templateTable, controlTemplateFile);
                LoadControlTemplate(templateTable, str3);
                foreach (FileInfo info2 in files)
                {
                    if (!(info2.Name == defaultTemplateFile) && !(info2.Name == mobileDefaultTemplateFile))
                    {
                        controlTemplateFile = systemTemplateLocation + info2.Name;
                        LoadControlTemplate(templateTable, controlTemplateFile);
                    }
                }
                s_templateTable = templateTable;
            }
        }
    }
    return s_templateTable;
}

Variables values:
systemTemplateLocation = "/_controltemplates/";
defaultTemplateFile = "DefaultTemplates.ascx";

As you see, first, this control loads all templates from DefaultTemplates.ascx file. Then it gets all files form CONTROLTEMPLATES directory and loads all templates from them. So if it finds templates with the same name as defined in DefaultTemplates.ascx it replaces them. Okey, let’s check it. Create a new file in CONTROLTEMPLATE directory and add a template to it:
<SharePoint:RenderingTemplate id="ListForm" runat="server">
  <Template>
    <h1>Custom form!</h1>
  </Template>
</SharePoint:RenderingTemplate>

Make iisreset, navigate  some list and try to create a new item. You'll see our title instead of form. Okey, it  works perfectly for standard templates, but what if we want to change the template for only one Web Application or SiteCollection or change the template for DocumentSet display form, which stores its template in file DocSetTemplates.ascx. Let's look at ListFormWebPart. This class has property TemplateName which is used to get a template name by current context (Edit, Create or Display):
public string TemplateName
{
    get
    {
        if (this.templateName == null)
        {
            this.EnsureListAndForm();
            if (this.ItemContext != null)
            {
                SPContentType contentType = this.ItemContext.ContentType;
                if (contentType != null)
                {
                    switch (this.pageType)
                    {
                        case PAGETYPE.PAGE_DISPLAYFORM:
                            this.templateName = contentType.DisplayFormTemplateName;
                            break;

                        case PAGETYPE.PAGE_EDITFORM:
                            this.templateName = contentType.EditFormTemplateName;
                            break;

                        case PAGETYPE.PAGE_NEWFORM:
                            this.templateName = contentType.NewFormTemplateName;
                            break;
                    }
                    if ((this.templateName != null) && (this.templateName.Length > 0))
                    {
                        return this.templateName;
                    }
                }
            }
            if (this.form != null)
            {
                this.templateName = this.form.TemplateName;
            }
            else
            {
                this.templateName = "ListForm";
            }
        }
        return this.templateName;
    }
    set
    {
        this.templateName = value;
    }
}

To get the name of the templates it uses properties of ContentType: DisplayFormTemplateName, EditFormTemplateName, NewFormTemplateName. So, we can create a new template in CONTROLTEMPLATES directory and set its name into all content types we need with powershell, for example. If we remove our template nothing happens, because if the template is not found it uses a default ListForm template.