Створення фільтра користувача для списку в SharePoint 2010
У цій статті будуть послідовно описані всі кроки, необхідні для написання універсального фільтра, що дозволяє проводити відбір зі стандартних списківSharePoint(спадкоємців відXsltListWebPartабоListWebPart).
Розробка велася наVisualStudio 2010, яка вміє працювати тільки зSharePoint 2010, тому все описане нижче перевірялося на платформіSharePoint Foundation 2010, проте, швидше за все, все буде справедливо і дляSharePoint 2007(WSS 3.0). Процес встановлення та розгортання середовища розробки описувати не буду - це легко шукається в інтернеті, а ось як зробити фільтрацію стандартних списків - тут хороших і зрозумілих прикладів я не знайшов.
Отже, завдання-мінімум: СтворитиWebPart, який міг би фільтрувати списокShared Documentsприблизно ось у такому вигляді:

Тобто. можна вибрати одне з доступних полів списку (не тільки з числа видимих), вказати один з можливих операторів порівняння (одно, не одно, більше, менше і т.д) і власне значення. При натисканні на кнопкуGo– вміст списку повинен відповідати нашій умові.
Розглянемо для початку як взагалі уSharePoint'іWebPart'и можуть взаємодіяти один з одним. Стандартним методом взаємодії двох або більше WebPart 'ів є так званий механізмProvider-Consumer.
На початку визначається якийсь інтерфейс, через який здійснюватиметься зв'язок.
Consumerпублікує так звані точки входу (фактично це методи приймають як параметр який-небудь інтерфейс і позначений атрибутомConnectionConsumer).
public class StringConsumer : WebPart private ITextBoxString _myProvider; private Label_myLabel;
захищене перевизначення void OnPreRender( EventArgs e) EnsureChildControls(); if (_myProv >null ) _myLabel.Text = _myProvider.TextBoxString; >
захищене перевизначення void CreateChildControls() Controls.Clear(); _myLabel = нова мітка; Controls.Add(_myLabel); >
[ConnectionConsumer( "String Consumer" , "StringConsumer" )] public void TextBoxStringConsumer(ITextBoxString provider) _myProv > > >
Providerпублікує вихідні точки (методи, повертаючий інтерфейс і позначений атрибутConnectionProvider).
публічний клас StringProvider : WebPart, ITextBoxString private TextBox _myTextBox;
захищене перевизначення void CreateChildControls() Controls.Clear(); _myTextBox = новий TextBox(); Controls.Add(_myTextBox); Controls.Add(нова кнопка); >
[ConnectionProv >"Provider for String From TextBox" , "TextBoxStringProvider" )] public ITextBoxString TextBoxStringProvider() повертає це ; > >
Зв’язок може бути встановлений, якщо інтерфейси в точці зв’язкуПостачальникіСпоживачспівпадають. Після розміщення відповідних WebPart’ів на сторінці їх можна зв’язати між собою, вибравши в пункт менюConnectionодного зWebPart’ів. Після того, як зв’язок встановлено – після кожного оновлення (рендерингу) сторінки спочатку буде викликано методProvider'а (TextBoxStringProvider) для отримання інтерфейсу, а результат буде передано в методConsumer'а (TextBoxStringConsumer). В принципе все просто. Цей академічний приклад навіть описано в MSDN.
Однак нам потрібно написати власного провайдера до переданогоConsumer’у, а самеWebPart’ам, спадкоємцямвідXsltListWebPartабоListWebPart. Для цього нам потрібно знати, які інтерфейси зв'язку вони підтримують і як з ними можна працювати. Цієї інформації, як не дивно, в інтернеті дуже мало. Як в принципі мало і рішень, що вирішують це завдання (я знайшов два комерційні проекти (KWizCom List Filter Plus і Roxority FilterZen Filter) і жодного з відкритим вихідним кодом).
Ретельний аналіз та реверс-інжиніринг показав, що існує два інтерфейси до яких можна підключитися:ITransformableFilterValuesтаIWebPartParameters.
За допомогою першого інтерфейсуITransformableFilterValuesможна зробити відбір тільки по одному полю списку (можливо і по кількох, але як, я навскідку не знайшов), вибраному при налагодженні зв'язку між нашим провайдером і списком і тільки за повною відповідністю значення поля з значенням, що фільтрується (тобто можна реалізувати тільки операцію “рівно”). Таким чином, цей шлях нам не підходить.
Другий інтерфейсIWebPartParametersцікавий тим, що по-перше, дозволяє легко отримати список усіх полів списку:
public void SetConsumerSchema(PropertyDescriptorCollection schema) Owner.Parameters = schema; >
А по-друге, не вимагає вказівки, за якими полями здійснюється фільтрація. У методіGetParametersDataпередається словник з парами ім'я поля – значення здійснення відбору:
Причому встановлений таким чином фільтр - аналогічний фільтру по колонках, зробленим вручну користувачем (колонки навіть будуть підсвічені іконкою фільтра, що показує, що по даній підлозі відбір). Але, на жаль, цей спосіб нам теж підходить, т.к. дозволяє відібрати дані тільки за строгою рівністю параметрів відбору.
Що ж робити? Виявляєтьсявплинути на іншийWebPartможна й іншим способом – просто змінивши у потрібний момент часу (перед відбором) параметри цьогоWebPart'а.
Відповідним моментом часу буде подіяOnLoad- яка викликається після завантаження сторінки, але до відбору даних. Змінюваним параметром буде Xml-опис списку, який зберігається у властивостіXmlDefinitionдляXlstListWebPartіListViewXmlдляListWebPart. Даний Xml-опис також має CAML-запит, для відбору даних в який можна вставити необхідні нам умови:
protected override void OnLoad(EventArgs e) base .OnLoad(e);
var query = CreateQuery(); if (query == "") return; var part = WebPartManager.WebParts[0] xsltListViewWebPart; if (part == null) return;
var doc = новий 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; >
Ось у принципі те, від чого можна відштовхнутися для подальшої розробки. Написав статтю для себе як пам'ятку, щоб не забути. Сподіваюся, комусь вона буде корисна також.
Хардкорна конфа за С++. Ми запрошуємо лише профі.