This article will consistently describe all the steps required to write a universal filter that allows you to select from standard
SharePoint lists (inheritors from
XsltListWebPart or
ListWebPart ).
Development was conducted on
VisualStudio 2010 , which can work only with
SharePoint 2010 , so everything described below was tested on the
SharePoint Foundation 2010 platform, however, most likely, everything will be true for
SharePoint 2007 (
WSS 3.0 ).
I will not describe the process of installing and deploying the development environment - it is easy to search on the Internet, but I have not found good examples to understand how to filter standard lists.
So, the minimum task: Create a
WebPart , which could filter the list of
Shared Documents approximately like this:
')

Those. You can choose one of the available list fields (not only from the number of visible ones), specify one of the possible comparison operators (equal, not equal, greater, less, etc.) and the actual value. When you click on the
Go button - the contents of the list must satisfy our condition.
Consider for a start how generally in
SharePoint 'e
WebPart ' s can interact with each other. The standard method of interaction between two or more
WebParts ' s is the so-called
Provider -
Consumer mechanism.
At the beginning of a certain interface is determined through which communication will be carried out.
Interface examplepublic interface ITextBoxString
{
string TextBoxString { get ; set ; }
}
Consumer publishes the so-called entry points (in fact, these are methods that take an interface as a parameter and are marked with the
ConnectionConsumer attribute).
Consumer examplepublic class StringConsumer : WebPart
{
private ITextBoxString _myProvider;
private Label _myLabel;
protected override void OnPreRender( EventArgs e)
{
EnsureChildControls();
if (_myProvider != null )
_myLabel.Text = _myProvider.TextBoxString;
}
protected override void CreateChildControls()
{
Controls.Clear();
_myLabel = new Label{Text = "Default text" };
Controls.Add(_myLabel);
}
[ConnectionConsumer( "String Consumer" , "StringConsumer" )]
public void TextBoxStringConsumer(ITextBoxString provider)
{
_myProvider = provider;
}
}
Provider publishes outgoing points (methods that return an interface and are marked with the
ConnectionProvider attribute).
Provider Examplepublic class StringProvider : WebPart, ITextBoxString
{
private TextBox _myTextBox;
[Personalizable]
public string TextBoxString { get { return _myTextBox.Text; } set { _myTextBox.Text = value ; } }
protected override void CreateChildControls()
{
Controls.Clear();
_myTextBox = new TextBox();
Controls.Add(_myTextBox);
Controls.Add( new Button {Text = "Change Text" });
}
[ConnectionProvider( "Provider for String From TextBox" , "TextBoxStringProvider" )]
public ITextBoxString TextBoxStringProvider()
{
return this ;
}
}
Communication can be established if the interfaces at the
Provider and
Consumer communication points match. At the same time, after placing the corresponding WebPart's on the page, they can be linked together by selecting one of the
WebPart 's in the
Connection menu item. After the connection is established - after each update (rendering) of the page, the
Provider method will be called first (
TextBoxStringProvider ) to get the interface and the result will be passed to the
Consumer method (
TextBoxStringConsumer ). In principle, everything is simple. This academic example is even described in MSDN.
However, we need to write our own provider to a predefined
consumer , namely
WebPart , inherited from
XsltListWebPart or
ListWebPart . To do this, we need to know which communication interfaces they support and how we can work with them. This information, oddly enough, on the Internet is extremely small. As in principle, there are few solutions that solve this problem (I found two commercial projects (
KWizCom List Filter Plus and
Roxority FilterZen Filter ) and not one with open source).
The painstaking analysis and reverse engineering showed that there are two interfaces to which you can connect:
ITransformableFilterValues and
IWebPartParameters .
Using the first
ITransformableFilterValues interface,
you can select only one list field (possibly several, but, I didn’t find it at random), selected when establishing a connection between our provider and the list and only according to the full compliance of the field value with the filtered value (t. e. you can only implement the operation "equal"). Thus, this path does not suit us.
The second interface
IWebPartParameters is interesting because, firstly, it makes it easy to get a list of all the list fields:
IWebPartParameterspublic void SetConsumerSchema(PropertyDescriptorCollection schema)
{
Owner.Parameters = schema;
}
And secondly, it does not require indication of which / which fields are filtered. In the
GetParametersData method, the
dictionary is transmitted with pairs of the field name - the value for the selection:
IWebPartParameterspublic void GetParametersData(ParametersCallback callback)
{
var objParameters = new StateBag();
if (Owner._searchBox != null && ! string .IsNullOrEmpty(Owner._searchBox.Text))
{
objParameters.Add(Owner._comboBox.SelectedItem.Value, Owner._searchBox.Text);
}
callback(objParameters);
}
Moreover, the filter installed in this way is similar to the filter for the columns made manually by the user (the columns will even be highlighted with a filter icon indicating that a selection has been made for this floor). But, alas, this method does not suit us either, because allows you to select data only on strict equality of the selection parameters.
What to do? It turns out that another
WebPart can be influenced in another way - simply by changing the parameters of this
WebPart 'at a suitable point in time (before selection).
The appropriate time point is the
OnLoad event - which is triggered after the page loads, but before the data is selected. The variable parameter will be the Xml description of the list, which is stored in the
XmlDefinition property for
XlstListWebPart and
ListViewXml for
ListWebPart . This Xml-description, among other things, has a CAML-request, for the selection of data in which you can insert the necessary conditions:
Code Sampleprivate string CreateQuery()
{
if (_searchBox != null && ! string .IsNullOrEmpty(_searchBox.Text))
return String .Format( "<{1}><FieldRef Name='{0}'/><Value Type='Text'>{2}</Value></{1}>" ,
_comboBox.SelectedItem.Value, _searchType.SelectedItem.Value, _searchBox.Text);
else
return "" ;
}
protected override void OnLoad( EventArgs e)
{
base .OnLoad(e);
var query = CreateQuery();
if (query == "" ) return ;
var part = WebPartManager.WebParts[0] as XsltListViewWebPart;
if (part == null ) return ;
var doc = new XmlDocument ();
doc.LoadXml(part.XmlDefinition);
var queryNode = doc.SelectSingleNode( "//Query" );
if (queryNode == null ) return ;
var whereNode = queryNode.SelectSingleNode( "Where" );
if (whereNode != null ) queryNode.RemoveChild(whereNode);
var newNode = doc.CreateNode( XmlNodeType .Element, "Where" , String .Empty);
newNode.InnerXml = query;
queryNode.AppendChild(newNode);
part.XmlDefinition = doc.OuterXml;
}
That's basically what you can push off from for further development. Wrote an article for yourself as a memo, so as not to forget. I hope someone will find it useful too.