Страницы

Tuesday, May 22, 2012

Sharepoint 2010 - Пишем свой Custom lookup picker field with search

Всем привет. Сегодня я расскажу о том, как cделать свой собственный "custom lookup picker field with search". Основная цель данной публикации, показать методику создания Custom Field's в SharePoint 2010. Итак приступим:

Часть 1: Реализация

- Создаем проект SharePoint 2010.

- Добавляем SharePoint mapped folder - "XML".

- В папку "XML" добавляем файл - "fldtypes_LookupFieldWithSearch.xml".

<?xml version="1.0" encoding="utf-8" ?>
<FieldTypes>
    <FieldType>
        <Field Name="Sortable">TRUE</Field>
        <Field Name="Filterable">TRUE</Field>
        <Field Name="ParentType">Lookup</Field>
        <Field Name="UserCreatable">TRUE</Field>
        <Field Name="InternalType">Lookup</Field>
        <Field Name="TypeName">LookupFieldWithSearch</Field>
        <Field Name="TypeDisplayName">Lookup field with search</Field>
        <Field Name="TypeShortDescription">Lookup field with search</Field>
        <Field Name="FieldTypeClass">MyProject.CustomFields.LookupFieldWithSearch.LookupFieldWithSearch, $SharePoint.Project.AssemblyFullName$</Field>
    </FieldType>
</FieldTypes>
Листинг 1 - fldtypes_LookupFieldWithSearch.xml.

- Определим класс LookupFieldWithSearch

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace MyProject.CustomFields.LookupFieldWithSearch
{
   public class LookupFieldWithSearch : SPFieldLookup
   {
       public LookupFieldWithSearch(SPFieldCollection fields, string fieldName)
        : base(fields, fieldName) { }

       public LookupFieldWithSearch(SPFieldCollection fields, string typeName, string displayName)
        : base(fields, typeName, displayName) { }

       public override BaseFieldControl FieldRenderingControl
       {
          get
          {
              BaseFieldControl fieldControl = new LookupFieldWithSearchControl();
              fieldControl.FieldName = InternalName;
              return fieldControl;
          }
       }
   }
}
Листинг 2 - LookupFieldWithSearch.cs.

- У класса LookupFieldWithSearch перегружаем метод FieldRenderingControl (см. листинг 1).

- Определим класс "LookupFieldWithSearchControl" (см. листинг 3).

using System;
using System.Web;
using System.Text;
using System.Linq;
using System.Collections;
using Microsoft.SharePoint;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using Microsoft.SharePoint.WebControls;

namespace MyProject.CustomFields.LookupFieldWithSearch
{
    public class LookupFieldWithSearchControl : BaseFieldControl
    {
        #region Конструктор
        public LookupFieldWithSearchControl()
            : base()
        {
        }
        #endregion

        #region Свойства

        public SPList LookupList
        {
            get
            {
                var web = SPControl.GetContextWeb(base.Context);
                var field = Field as LookupFieldWithSearch;
                var m_lookupList = web.Lists.GetList(new Guid(field.LookupList), true);
                return m_lookupList;
            }
        }
        public override string DisplayTemplateName
        {
            get
            {
                if (ControlMode == SPControlMode.Edit || ControlMode == SPControlMode.New)
                {
                    return "LookupFieldWithSearchEditTemplate";
                }
                else
                {
                    return "LookupFieldWithSearchDisplayTemplate";
                }
            }
            set
            {
                base.DisplayTemplateName = value;
            }
        }
        protected override string DefaultTemplateName
        {
            get
            {
                return DisplayTemplateName;
            }
        }
        public override object Value
        {
            get
            {
                if (ControlMode == SPControlMode.New || ControlMode == SPControlMode.Edit)
                {
                    var field = (LookupFieldWithSearch)base.Field;
                    if (field == null) return null;

                    var m_displayValue = (TextBox)TemplateContainer.FindControl("m_displayValue");
                    var m_hiddenValue = (HiddenField)TemplateContainer.FindControl("m_hiddenValue");
                    var m_hiddenDisplayValue = (HiddenField)TemplateContainer.FindControl("m_hiddenDisplayValue");

                    if (string.IsNullOrEmpty(m_hiddenValue.Value) || m_hiddenValue.Value.Trim() == string.Empty)
                    {
                        return null;
                    }
                    else
                    {
                        return new SPFieldLookupValue(int.Parse(m_hiddenValue.Value), m_hiddenDisplayValue.Value);
                    }
                }
                else
                {
                    return null;
                }
            }
            set
            {
                if (ControlMode == SPControlMode.New || ControlMode == SPControlMode.Edit)
                {
                    var m_displayValue = (TextBox)TemplateContainer.FindControl("m_displayValue");
                    var m_hiddenValue = (HiddenField)TemplateContainer.FindControl("m_hiddenValue");
                    var m_hiddenDisplayValue = (HiddenField)TemplateContainer.FindControl("m_hiddenDisplayValue");

                    var _value = value as SPFieldLookupValue;

                    if (_value != null)
                    {
                        m_displayValue.Text = m_hiddenDisplayValue.Value = _value.LookupValue;
                        m_hiddenValue.Value = _value.LookupId.ToString();
                    }
                    else
                    {
                        m_displayValue.Text = string.Empty;
                        m_hiddenValue.Value = string.Empty;
                        m_hiddenDisplayValue.Value = string.Empty;
                    }
                }
            }
        }

        #endregion

        #region Методы

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if ((ControlMode == SPControlMode.Edit || ControlMode == SPControlMode.New) && Value == null)
            {
                var m_hiddenValue = TemplateContainer.FindControl("m_hiddenValue") as HiddenField;
                var m_displayValue = TemplateContainer.FindControl("m_displayValue") as TextBox;

                var m_hiddenDisplayValue = TemplateContainer.FindControl("m_hiddenDisplayValue") as HiddenField;

                m_displayValue.Text = string.Empty;
                m_hiddenValue.Value = string.Empty;
                m_hiddenDisplayValue.Value = string.Empty;
            }
        }
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            var field = (LookupFieldWithSearch)Field;
            if (field == null) return;

            if (ControlMode == SPControlMode.Display)
            {
                #region Display mode

                var item = field.ParentList.GetItemById(SPContext.Current.ItemId);

                if (item[field.Id] != null)
                {
                    var m_displayFieldValue = TemplateContainer.FindControl("m_displayFieldValue") as Literal;
                    var urlToListItem = LookupList.DefaultDisplayFormUrl;

                    m_displayFieldValue.Text = string.Format("<a href=\"{0}{1}{2}{3}&RootFolder=*\">{4}</a>",
                        SPContext.Current.Site.Url,
                        urlToListItem,
                        "?ID=",
                        (item[field.Id] as SPFieldLookupValue).LookupId,
                        (item[field.Id] as SPFieldLookupValue).LookupValue);
                }

                #endregion
            }

            if (ControlMode == SPControlMode.Edit || ControlMode == SPControlMode.New)
            {
                #region New/Edit mode

                var m_webUrl = TemplateContainer.FindControl("m_webUrl") as EncodedLiteral;
                var m_params = TemplateContainer.FindControl("m_params") as EncodedLiteral;
                var m_displayValue = TemplateContainer.FindControl("m_displayValue") as TextBox;
                var m_hiddenValue = TemplateContainer.FindControl("m_hiddenValue") as HiddenField;
                var m_hiddenDisplayValue = TemplateContainer.FindControl("m_hiddenDisplayValue") as HiddenField;

                var m_fieldPrefix1 = TemplateContainer.FindControl("m_fieldPrefix1") as EncodedLiteral;
                var m_fieldPrefix2 = TemplateContainer.FindControl("m_fieldPrefix2") as EncodedLiteral;
                var m_fieldPrefix3 = TemplateContainer.FindControl("m_fieldPrefix3") as EncodedLiteral;
                var m_fieldPrefix4 = TemplateContainer.FindControl("m_fieldPrefix4") as EncodedLiteral;
                var m_fieldPrefix5 = TemplateContainer.FindControl("m_fieldPrefix5") as EncodedLiteral;
                var m_fieldPrefix6 = TemplateContainer.FindControl("m_fieldPrefix6") as EncodedLiteral;
                var m_fieldPrefix7 = TemplateContainer.FindControl("m_fieldPrefix7") as EncodedLiteral;
                var m_fieldPrefix8 = TemplateContainer.FindControl("m_fieldPrefix8") as EncodedLiteral;
                var m_fieldPrefix9 = TemplateContainer.FindControl("m_fieldPrefix9") as EncodedLiteral;
                var m_fieldPrefix10 = TemplateContainer.FindControl("m_fieldPrefix10") as EncodedLiteral;
                var m_fieldPrefix11 = TemplateContainer.FindControl("m_fieldPrefix11") as EncodedLiteral;
                var m_fieldPrefix12 = TemplateContainer.FindControl("m_fieldPrefix12") as EncodedLiteral;
                var m_fieldPrefix13 = TemplateContainer.FindControl("m_fieldPrefix13") as EncodedLiteral;
                var m_fieldPrefix14 = TemplateContainer.FindControl("m_fieldPrefix14") as EncodedLiteral;
                var m_fieldPrefix15 = TemplateContainer.FindControl("m_fieldPrefix15") as EncodedLiteral;
                var m_fieldPrefix16 = TemplateContainer.FindControl("m_fieldPrefix16") as EncodedLiteral;
                var m_fieldPrefix17 = TemplateContainer.FindControl("m_fieldPrefix17") as EncodedLiteral;


                m_fieldPrefix1.Text = m_fieldPrefix2.Text = m_fieldPrefix3.Text = m_fieldPrefix4.Text = m_fieldPrefix5.Text = m_fieldPrefix6.Text
                    = m_fieldPrefix7.Text = m_fieldPrefix8.Text = m_fieldPrefix9.Text = m_fieldPrefix10.Text = m_fieldPrefix11.Text = m_fieldPrefix12.Text
                        = m_fieldPrefix13.Text = m_fieldPrefix14.Text = m_fieldPrefix15.Text = m_fieldPrefix16.Text = m_fieldPrefix17.Text = field.StaticName;

                var m_hiddenValueClientID = TemplateContainer.FindControl("m_hiddenValueClientID") as EncodedLiteral;
                var m_displayValueClientID = TemplateContainer.FindControl("m_displayValueClientID") as EncodedLiteral;
                var m_hiddenDisplayValueClientID = TemplateContainer.FindControl("m_hiddenDisplayValueClientID") as EncodedLiteral;

                m_webUrl.Text = SPContext.Current.Web.Url;
                m_hiddenValueClientID.Text = m_hiddenValue.ClientID;
                m_displayValueClientID.Text = m_displayValue.ClientID;
                m_hiddenDisplayValueClientID.Text = m_hiddenDisplayValue.ClientID;

                m_params.Text = "?listId=" + field.LookupList;

                #endregion
            }
        }
        public override void Validate()
        {
            base.Validate();
            try
            {
                if ((this.ControlMode == SPControlMode.New || this.ControlMode == SPControlMode.Edit) && this.Field.Required && this.Value == null)
                {
                    this.IsValid = false;
                    throw new Exception("Необходимо задать значение для этого обязательного поля.");
                }
            }
            catch (Exception ex)

            {
                this.ErrorMessage = ex.Message;
            }
        }

        #endregion
    }
}
Листинг 3 - LookupFieldWithSearchControl.cs.

- Перегрузим свойства DisplayTemplateName и DefaultTemplateName, отвечающих за выбор шаблона во время редактирования/создания/отображения значений поля.

- Добавляем в проект SharePoint mapped folder - "CONTROLTEMPLATES".

- В папке "CONTROLTEMPLATES" - создаем файл "LookupFieldWithSearchTemplate.ascx" (см. листинг 4).

<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="pa" Namespace="MyProject.CustomFields.LookupFieldWithSearch" Assembly="$SharePoint.Project.AssemblyFullName$" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" AutoEventWireup="true" %>
<SharePoint:RenderingTemplate ID="LookupFieldWithSearchDisplayTemplate" runat="server">
    <Template>
        <asp:Literal Mode="PassThrough" runat="server" ID="m_displayFieldValue" />
    </Template>
</SharePoint:RenderingTemplate>
<SharePoint:RenderingTemplate ID="LookupFieldWithSearchEditTemplate" runat="server">
    <Template>
        <script language="javascript" type="text/javascript">

            var <SharePoint:EncodedLiteral ID='m_fieldPrefix10' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />webUrl = "<SharePoint:EncodedLiteral ID='m_webUrl' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />"
                         + "/_layouts/MyProject/LookupFieldWithSearch/SelectItem.aspx"
                         + "<SharePoint:EncodedLiteral ID='m_params' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";
            var <SharePoint:EncodedLiteral ID='m_fieldPrefix1' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />displayValueClientID = "<SharePoint:EncodedLiteral ID='m_displayValueClientID' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";
            var <SharePoint:EncodedLiteral ID='m_fieldPrefix2' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenValueClientID = "<SharePoint:EncodedLiteral ID='m_hiddenValueClientID' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";
            var <SharePoint:EncodedLiteral ID='m_fieldPrefix15' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenDisplayValueClientID = "<SharePoint:EncodedLiteral ID='m_hiddenDisplayValueClientID' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";

            function <SharePoint:EncodedLiteral ID='m_fieldPrefix9' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />openPickerDialog() {
                var options = {
                    title: 'Выбор',
                    width: 600,
                    height: 435,
                    url: <SharePoint:EncodedLiteral ID='m_fieldPrefix13' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />webUrl,
                    dialogReturnValueCallback: Function.createDelegate(null, <SharePoint:EncodedLiteral ID='m_fieldPrefix14' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />pickerDialogClosedCallback)
                };
                SP.UI.ModalDialog.showModalDialog(options);
            }

            function <SharePoint:EncodedLiteral ID='m_fieldPrefix8' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />pickerDialogClosedCallback(result, value) {
                if (result == 1) {
                    document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix3' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenValueClientID).value = value.hiddenValue;
                    document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix4' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />displayValueClientID).value = value.displayValue;
                    document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix17' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenDisplayValueClientID).value = value.displayValue;
                }
            }

            function <SharePoint:EncodedLiteral ID='m_fieldPrefix7' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />clearPicker() {
                document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix5' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenValueClientID).value = "";
                document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix6' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />displayValueClientID).value = "";
                document.getElementById(<SharePoint:EncodedLiteral ID='m_fieldPrefix16' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />hiddenDisplayValueClientID).value = "";
            }
        </script>

        <table cellpadding="0" cellspacing="0" style="border-style: none; border-collapse: collapse; border-width: 0px; width: 100%;">
            <tr>
                <td align="left" valign="middle">
                    <asp:TextBox ID="m_displayValue" runat="server" Enabled="false" />
                    <asp:HiddenField ID="m_hiddenValue" runat="server" />
                    <asp:HiddenField ID="m_hiddenDisplayValue" runat="server" />
                    <img onclick="<SharePoint:EncodedLiteral ID='m_fieldPrefix11' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />openPickerDialog()" alt="Выбрать" src="_layouts/images/MyProject/funnel-pencil.png" style="border-width: 0px; border-style: none; cursor: pointer;" />
                    <img onclick="<SharePoint:EncodedLiteral ID='m_fieldPrefix12' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />clearPicker()" alt="Очистить" src="_layouts/images/MyProject/cross.png" style="border-width: 0px; border-style: none; cursor: pointer;" />
                </td>
            </tr>
        </table>
    </Template>
</SharePoint:RenderingTemplate>
Листинг 4 - LookupFieldWithSearchTemplate.ascx.

- В файле шаблонов (см. листинг 4) определим две области:  "LookupFieldWithSearchDisplayTemplate" и "LookupFieldWithSearchEditTemplate".

- В "LookupFieldWithSearchDisplayTemplate" помещаем литерал с Mode="PassThrough" (Mode="PassThrough" нужно для того, чтобы литерал не экранировал html тэги) (см. листинг 4).

- В  "LookupFieldWithSearchEditTemplate" помещаем несколько элементов управления: TextBox, 2 HiddenField и 2 картинки кнопки (см. рис. 1).



Рисунок 1 - Вид LookupFieldWithSearch в режиме редактирования элемента списка.
Наверное вы уже заметили, что в блоке с JavaScript кодом перед каждой переменной стоят литералы, в которые приходят значение статического имени текущего поля - это нужно для того, чтобы javascript обвязки не путались между полями одного и того же типа, в Edit и New форме (избавляемся от дублирующихся переменных). В блоке JavaScript кода (см. листинг 4) есть переменная webUrl (Обязательно получайте значение webUrl с сервера, JS Client Object Model - приходит ссылка на родительский сайт).
- Добавляем SharePoint mapped folder "Images".

- В "Images" создаем папку "MyProject" и в нее помещаем все нужные картинки, которые будет использовать наше поле (фильтр, крест...).

- Добавляем SharePoint mapped folder "Layouts".

- В папке "Layouts" добавляем файл "MyProject\LookupFieldWithSearch\SelectItem.aspx" (Это страница диалога).

- По нажатию на фильтр (см. рис. 1) будет выполняться JavaScript метод openPickerDialog() (см. листинг 4) - открывающий в диалоговом окне страницу "SelectItem.aspx" (см. листинг 5).

<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
    Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SelectItem.aspx.cs" Inherits="MyProject.Layouts.LookupFieldWithSearch.SelectItem"
    MasterPageFile="~/_layouts/dialog.master" %>

<asp:Content ID="DialogHeader" ContentPlaceHolderID="PlaceHolderDialogHeaderSection" runat="server"></asp:Content>
<asp:Content ID="DialogBodyMain" ContentPlaceHolderID="PlaceHolderDialogBodyMainSection" runat="server">
    <script type="text/javascript">
        var hiddenValueClientID = "<SharePoint:EncodedLiteral ID='m_hiddenValueClientID' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";
        var displayValueClientID = "<SharePoint:EncodedLiteral ID='m_displayValueClientID' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />";
        function commit() {
            if (document.getElementById(hiddenValueClientID).value != "") {
                window.frameElement.commitPopup(getSelectedItem());
            }
            else {
                alert("<SharePoint:EncodedLiteral ID='m_okError' runat='server' EncodeMethod='EcmaScriptStringLiteralEncode' />");
            }
        }

        function getSelectedItem() {
            var item = {
                hiddenValue: document.getElementById(hiddenValueClientID).value,
                displayValue: document.getElementById(displayValueClientID).value
            };

            return item;
        }

    </script>
    <style type="text/css">
        .searchResults
        {
            margin-left: 2px;
            height: 310px;
            width: 99%;
        }
        .displayValue
        {
            margin-left: 2px;
            width: 99%;
        }
        .searchTextBox
        {
            margin-left: 3px;
        }
    </style>
    <table width="570" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-style: none; border-width: 0px; margin: 5px 5px 5px 5px;">
        <tr>
            <td>
                <asp:TextBox ID="m_searchTextBox" runat="server" Width="540px" CssClass="searchTextBox" />
            </td>
            <td align="center" style="width: 25px; vertical-align: middle;">
                <asp:ImageButton ImageUrl="~/_layouts/images/MyProject/magnifier.png" runat="server" OnClick="Search_Click" />
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <asp:ListBox AutoPostBack="true" ID="m_searchResults" runat="server" CssClass="searchResults" OnSelectedIndexChanged="m_searchResults_SelectedIndexChanged">
                </asp:ListBox>
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <asp:TextBox ID="m_displayValue" CssClass="displayValue" runat="server" Enabled="false" />
                <asp:HiddenField ID="m_hiddenValue" runat="server" />
            </td>
        </tr>
    </table>
</asp:Content>
Листинг 5 - SelectItem.aspx.
Обратите внимание, что на странице SelectItem.aspx, MasterPageFile="~/_layouts/dialog.master". Это специализированный MasterPage для диалоговых окон.
Рисунок 2 - Вид диалогового окна.


using System;
using System.Linq;
using System.Web.UI;
using Microsoft.SharePoint;
using PrivateAffairs.Models;
using System.Collections.Generic;
using Microsoft.SharePoint.WebControls;
using MyProject.CustomFields.LookupFieldWithSearch;

namespace MyProject.Layouts.LookupFieldWithSearch
{
    public partial class SelectItem : LayoutsPageBase
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                m_hiddenValueClientID.Text = m_hiddenValue.ClientID;
                m_displayValueClientID.Text = m_displayValue.ClientID;

                ((DialogMaster)this.Page.Master).OkButton.OnClientClick = "commit()";
                m_okError.Text = "Вы не выбрали элемент!";
            }
        }

        private List<LookupItem> m_foundItems
        {
            get
            {
                if (ViewState["m_foundItems"] == null)
                {
                    return null;
                }
                else
                {
                    return (List<LookupItem>)ViewState["m_foundItems"];
                }
            }
            set
            {
                ViewState["m_foundItems"] = value;
            }
        }

        protected void Search_Click(object sender, ImageClickEventArgs e)
        {
            if (m_searchTextBox.Text.Trim() != string.Empty)
            {
                var query = new SPQuery()
                {
                    Query = string.Format("<Where><Contains><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Contains></Where>", m_searchTextBox.Text.Trim()),
                    ViewAttributes = "Scope='Recursive'",
                    QueryThrottleMode = SPQueryThrottleOption.Override
                };
                var list = SPContext.Current.Web.Lists.GetList(new Guid(Request.QueryString["listId"]), true);
                var items = list.GetItems(query);
                if (m_foundItems == null)
                    m_foundItems = new List<LookupItem>();
                m_foundItems.Clear();
                foreach (SPListItem item in items)
                {
                    m_foundItems.Add(new LookupItem() { ID = item.ID, Title = item["Title"].ToString() });
                }
                m_searchResults.DataSource = m_foundItems;
                m_searchResults.DataValueField = "ID";
                m_searchResults.DataTextField = "Title";
                m_searchResults.DataBind();
            }
        }
        protected void m_searchResults_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (m_searchResults.SelectedValue != null && m_foundItems != null)
            {
                var item = (from q in m_foundItems where q.ID == int.Parse(m_searchResults.SelectedValue) select q).FirstOrDefault();
                if (item != null)
                {
                    m_hiddenValue.Value = item.ID.ToString();
                    m_displayValue.Text = item.Title;
                }
            }
        }
    }
}
Листинг 6 - SelectItem.aspx.cs.

- В методе "PageLoad" страницы "SelectItem.aspx", переопределяем клиентский метод нажатия на OK в диалоговом окне (см. листинг 6).
Я не буду описывать все блоки кода. Код не сложный, и в нем все итак тривиально и понятно. Если будут вопросы, вы можете задать их мне в комментариях к этому посту, и я постараюсь ответить на них.
Часть 2: Возможные проблемы и их решения


Проблема: Создал шаблон с Display и Edit представлениями, но после delpoy-я они не появляются.
Решение: SharePoint загружает все ".ascx" файлы c шаблонами, парсит их и получает оттуда массив RenderingTemplate's. В процессе запроса, выбирается нужный RenderingTemplate по ID. Если вы назовете свой edit template например так: "SharePoint:RenderingTemplate ID='EditTemplate'", то ваш шаблон может быть перекрыт другим шаблоном. Поэтому старайтесь выбирать для своего шаблона уникальный ID (можно даже Guid для надежности).
 Проблема: При открытии диалогового окна вылетает ошибка.
Решение: Все, что открывается в диалоговом окне, должно быть помечено как SafeControl (SelectItem.aspx тоже нужно пометить как SafeControl).

PS. В данном примере не реализована поддержка MultiLookup.

Tuesday, March 13, 2012

Голосование своими руками в SharePoint 2010

Рис. 1.
Доброго времени суток, дорогие коллеги и просто читатели. Как часто, работая с SharePoint 2010 вам приходится, слышать просьбы, о создании голосований с изображениями? Для тех кому ("хватит это терпеть" (c) Жириновский) надоело, представляю прототип решения для SharePoint 2010 в виде веб части, подключаемой к любой библиотеке документов с изображениями.

Данная веб часть, подключается к любой библиотеке документов, и выводит список изображений для голосования (см. Рис. 1). Результаты голосования записываются в автоматически создаваемое поле SPListItem-a. В представленной версии отсутвует AJAX кнопка, и механизм запоминания проголосовавшего (он сделан на основе coockies, в полной версии).

Отрисовка списка, выполнена по статье сайта artlebedev.ru - "Блоки картинок, с подписями выровненныe по центру".

Еще одним из интересных моментов, является способ получения данных из библиотеки документов, и приведения SPListItemCollection к списку строго типизированных объектов. Методика выполнена в виде Extension методов для SPListItemCollection (см. код ниже).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.SharePoint;

namespace ImageVotingWebPart
{
    public static class SPListItemCollectionExtension
    {
        /// 
        /// Преобразование элементов коллекции  к 
        /// 
        /// Тип результирующей коллекции
        /// 


 Исходная коллекция
        /// 
        public static IEnumerable<T> Select<T>(this SPListItemCollection collection)
            where T : new()
        {
            return collection
                .Cast<SPListItem>()
                .Select(item => item.Cast<T>());
        }

        /// 
        /// Извлечение из SPListItem полей класса  и создание его экземпляра
        /// 
        /// Тип результата
        /// 


 Преобразуемый элемент
        /// Возвращает объект класса  с теми заполнеными полями, которые были в исходном SPListItem
        public static T Cast<T>(this SPListItem item) 
            where T : new()
        {
            var result = new T();
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                try
                {
                    object value = item[property.Name];
                    property.SetValue(result, value, null);
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(exc);
                }
            }

            return result;
        }
    }
}
Модель:
namespace ImageVotingWebPart.ImageVotingWebPart
{
 public class ImageVotingItem
 {
  public int ID { get; set; }
  public string EncodedAbsThumbnailUrl { get; set; }
  public string FileLeafRef { get; set; }
  public string FileRef { get; set; }
  public int Rating { get; set; }
  public string Title { get; set; }

  public string TitleOrName
  {
   get { return !string.IsNullOrEmpty(Title) ? Title : FileLeafRef; }
  }
 }
}
Код веб части:
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
namespace ImageVotingWebPart.ImageVotingWebPart
{
    [ToolboxItemAttribute(false)]
    public class ImageVotingWebPart : WebPart
    {
        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/ImageVotingWebPart/ImageVotingWebPart/ImageVotingWebPartUserControl.ascx";

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            Controls.Add(control);
        }

        [Personalizable]
        [WebBrowsable]
        [WebDisplayName("Адрес библиотеки")]
        public string LibraryUrl { get; set; }
    }
}
Код контрола ImageVotingWebPartUserControl:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using ImageVotingWebPart.Properties;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace ImageVotingWebPart.ImageVotingWebPart
{
    public partial class ImageVotingWebPartUserControl : UserControl
    {
        #region Constants

        private const string c_ratingFieldName = "Rating";
        protected const string c_voteCommandName = "Vote";

        #endregion

        protected ImageVotingWebPart WebPart
        {
            get { return (ImageVotingWebPart)Parent; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (string.IsNullOrEmpty((WebPart.LibraryUrl ?? string.Empty).Trim()))
            {
                return;
            }

            bindImageList();
        }

        protected void OnImageListItemCommand(object sender, RepeaterCommandEventArgs e)
        {
            if (e.CommandName != c_voteCommandName)
            {
                return;
            }

            int id;

            if (!int.TryParse((string)e.CommandArgument, out id))
            {
                return;
            }

            if (SPUtility.ValidateFormDigest())
            {
                SPSecurity.RunWithElevatedPrivileges(() => increaseImageRating(id));
            }
        }

        private void increaseImageRating(int id)
        {
            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);

                        if (!list.Fields.ContainsField(c_ratingFieldName))
                        {
                            list.Fields.Add(c_ratingFieldName, SPFieldType.Integer, true);
                        }
                        else
                        {
                            SPField field = list.Fields.GetField(c_ratingFieldName);

                            if (field.Type != SPFieldType.Integer)
                            {
                                throw new ApplicationException(ImageVotingResources.RatingFieldWrongTypeError);
                            }
                        }

                        SPItem item = list.GetItemById(id);
                        int rating = (int)(item[c_ratingFieldName] ?? 0);
                        item[c_ratingFieldName] = rating + 1;
                        item.Update();
                    }
                }
            }
            catch (Exception e)
            {
                setError(e.Message);
            }
        }

        private void bindImageList()
        {
            string[] usingFields = new[]
            {
                "ID",
                "EncodedAbsThumbnailUrl",
                "FileRef",
                "FileLeafRef",
                c_ratingFieldName,
                "Title",
            };

            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);
                        SPQuery query = new SPQuery();
                        query.ViewFields = string.Join("", usingFields.Select(name => string.Format("<FieldRef Name='{0}' />", name)).ToArray());
                        query.ViewAttributes = "Scope='Recursive'";
                        ImageList.DataSource = list.GetItems(query).Select<ImageVotingItem>();
                        ImageList.DataBind();
                    }
                }
            }
            catch (Exception exc)
            {
                setError(string.Format(ImageVotingResources.LoadImagesError, exc.Message));
            }
        }

        private void setError(string error)
        {
            ErrorLabel.Text = error;
        }
    }
}
Разметка контрола ImageVotingWebPartUserControl.ascx:
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>

<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ImageVotingWebPartUserControl.ascx.cs"

    Inherits="ImageVotingWebPart.ImageVotingWebPart.ImageVotingWebPartUserControl" %>

<%@ Import Namespace="ImageVotingWebPart.Properties" %>

<style type="text/css">

    .thumbnail

    {

        /* Убираем подчеркивание у элемента ins,

который был использован для совместимости со старыми версиями Internet Explorer */

        text-decoration: none;

        /* Следующее правило для Firefox 2 */

        display: -moz-inline-box;

        /* а это для остальных */

        display: inline-block;

        vertical-align: top;

        /* Отступы между блоками */

        margin: 2em 0 0 5px;

    }

    .thumbnail .r

    {

        background-color: #D6E4FE;

        height: 164px;

        width: 164px;

        text-align: center;

        /*layout magic*/

        display: table;

        /*# - is hack for ie7*/

        #position: relative;

        overflow: hidden;

    }

    .centered

    {

        /*# - is hack for ie7*/

        #position: absolute;

        #top: 50%;

        #left: 50%;

        display: table-cell;

        vertical-align: middle;

    }

</style>

<asp:Label ID="ErrorLabel" runat="server" ForeColor="Red" /><br />

<asp:Repeater ID="ImageList" runat="server" OnItemCommand="OnImageListItemCommand">

    <ItemTemplate>

        <ins class="thumbnail">

            <div class="r">

                <div class="centered">

                    <div style=" #position: relative; #top: -50%; #left: -50%;">

                        <a href='<%# Eval("FileRef") %>' target="_blank" title='<%# Eval("TitleOrName") %>'>

                            <img alt='' src='<%# Eval("EncodedAbsThumbnailUrl") %>' style="border-style: none;"

                                title='<%# Eval("TitleOrName") %>' />

                        </a>

                    </div>

                </div>

            </div>

            <br />

            <table width="164px">

                <col width="80%" />

                <col />

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal2" runat="server" Text='<%# Eval("TitleOrName") %>' />

                    </td>

                </tr>

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal3" runat="server" Text='<%# ImageVotingResources.RatingLabelText %>' />:&nbsp;

                        <asp:Literal ID="Literal4" runat="server" Text='<%# Eval("Rating") %>' />

                        <asp:ImageButton ID="ImageButton1" runat="server" CommandName='<%# c_voteCommandName %>'

                            CommandArgument='<%# Eval("ID") %>' ImageUrl="~/_layouts/images/ImageVotingWebPart/vote.png"

                            Width="20" Height="20" Style="vertical-align: middle;" Title='<%# ImageVotingResources.VoteButtonText %>' />

                    </td>

                </tr>

            </table>

        </ins>

    </ItemTemplate>

</asp:Repeater>

Пост написан, в ответ на многочисленные просьбы пользователей форума www.gotdotnet.ru, на тему - "КАК МНЕ СДЕЛАТЬ ГОЛОСОВАНИЕ ДЛЯ КАРТИНОК В SHAREPOINT"?

Wednesday, February 15, 2012

Silverlight, ASP.NET MVC - Продвинутый TreeView


Получил задание - реализовать групповое присвоение свойств, организационным структурам (департаментам, отделам). Немного подумав, над тем как, это будет выглядеть, я пришел к выводу, что лучше всего сделать дерево, в котором видно, само подразделение и его статус. Статусы я решил подсвечивать квадратиком (закрашенная плитка). "Погуглив", подходящих контролов типа TreeView для ASP.NET MVC 3, которые бы меня устраивали, я не нашел. Решил все реализовывать на Silverlight.

ASP.NET MVC (часть)

Модели данных:

public class StructureTreeViewItem
{
   public int ID { get; set; }
   public int? ParentID { get; set; }
   public string Name { get; set; }
   public int TypeID { get; set; }
}
public class StructureType
{
   public int ID { get; set; }
   public string Name { get; set; }
}
Контроллер
public JsonResult GetNodes()
{
    using (var db = new ...Entities())
    {
        var s = (...
                    where ....
                    select new StructureTreeViewItem()
                    {
                        ID = structs.StructureID,
                        ParentID = structs.StructureParentID,
                        Name = structsLangs.StructureName,
                        TypeID = structs.StructureTypeId
                    }).ToList();

        return Json(s, JsonRequestBehavior.AllowGet);
    }
}

public JsonResult GetStructureTypes()
{
    using (var db = new ...Entities())
    {
        var structureTypesList = new List<structuretype>();

        db.StructureTypes
            .OrderBy(q => q.id)
                .ToList()
                    .ForEach(q => structureTypesList
                        .Add(new StructureType() { ID = q.id, Name = q.Name }));

        return Json(structureTypesList, JsonRequestBehavior.AllowGet);
    }
}

public EmptyResult UpdateStructures(string structures)
{
    if (!string.IsNullOrWhiteSpace(structures))
    {
        using (var db = new ...Entities())
        {
            var treeViewStructures = new List<structuretreeviewitem>();
            structures.Split(';').ToList().ForEach(s =>
            {
                var treeViewStructureDict = s.Split('=');
                int structureId, structureTypeId;
                if (treeViewStructureDict.Length == 2
                    && int.TryParse(treeViewStructureDict[0], out structureId)
                    && int.TryParse(treeViewStructureDict[1], out structureTypeId))
                {
                    var dbSctructure = db.Structure.FirstOrDefault(q => q.StructureID == structureId);
                    if (dbSctructure != null && db.StructureTypes.FirstOrDefault(q => q.id == structureTypeId) != null)
                    {
                        dbSctructure.StructureTypeId = structureTypeId;
                        db.SaveChanges();
                    }
                }
            });
        }
    }
    return new EmptyResult();
}

Silverlight (часть)

Модели

public class StructureType : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged<t>(Expression<func<t>> raiser)
    {
        var e = PropertyChanged;
        if (e != null)
        {
            var propName = ((MemberExpression)raiser.Body).Member.Name;
            e(this, new PropertyChangedEventArgs(propName));
        }
    }

    private int m_id;
    public int ID 
    {
        get
        {
            return m_id;
        }
        set
        {
            m_id = value;
            RaisePropertyChanged(() => this.ID);
        }
    }

    private string m_name;
    public string Name 
    {
        get
        {
            return m_name;
        }
        set
        {
            m_name = value;
            RaisePropertyChanged(() => this.Name);
        }
    }

    private SolidColorBrush m_color;
    public SolidColorBrush Color
    {
        get
        {
            return m_color;
        }
        set
        {
            m_color = value;
            RaisePropertyChanged(() => this.Color);
        }
    }
}
public class StructureItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged<t>(Expression<func<t>> raiser)
    {
        var e = PropertyChanged;
        if (e != null)
        {
            var propName = ((MemberExpression)raiser.Body).Member.Name;
            e(this, new PropertyChangedEventArgs(propName));
        }
    }

    public StructureItem()
    {
        m_checked = false;
        m_childItems = new ObservableCollection<structureitem>();
    }

    private int m_id;
    public int ID
    {
        get
        {
            return m_id;
        }
        set
        {
            m_id = value;
            RaisePropertyChanged(() => this.ID);
        }
    }

    private int? m_parentId;
    public int? ParentID
    {
        get
        {
            return m_parentId;
        }
        set
        {
            m_parentId = value;
            RaisePropertyChanged(() => this.ParentID);
        }
    }

    private string m_name;
    public string Name
    {
        get
        {
            return m_name;
        }
        set
        {
            m_name = value;
            RaisePropertyChanged(() => this.Name);
        }
    }

    private bool m_checked;
    public bool Checked 
    {
        get
        {
            return m_checked;
        }
        set
        {
            m_checked = value;
            RaisePropertyChanged(() => this.Checked);
        }
    }

    private StructureType m_structureType;
    public StructureType StructureType
    {
        get
        {
            return m_structureType;
        }
        set
        {
            m_structureType = value;
            RaisePropertyChanged(() => this.StructureType);
        }
    }

    ObservableCollection<structureitem> m_childItems;
    public ObservableCollection<structureitem> ChildItems 
    {
        get
        {
            return m_childItems;
        }
        set
        {
            m_childItems = value;
            RaisePropertyChanged(() => this.ChildItems);
        }
    }
}
При загрузке Silverlight приложение, подтягивает данные, о типах структурных подразделений. После чего, они привязываются к ComboBox-у, а затем загружаются сами структурные подразделения. После загрузки структурных подразделений, происходит их сортировка в иерархическую коллецию с помощью рекурсии.
public MainPage()
{
    InitializeComponent();
    onEndLoad();
}
private void disableUi()
{
    Dispatcher.BeginInvoke(() =>
    {
        // Бизи индикатор
        bi.IsBusy = true;
    });
}
private void enableUi()
{
    Dispatcher.BeginInvoke(() =>
    {
        // Отключение бизи индикатора
        bi.IsBusy = false;
    });
}
private void onEndLoad()
{
    disableUi();

    var webClient = new WebClient();
    webClient.OpenReadCompleted += onGetStructureTypesCompleted;
    webClient.OpenReadAsync(new Uri("/Core/GetStructureTypes", UriKind.Relative));
}
private void onGetStructureTypesCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error != null)
    {
        if (e.Error.Message != null)
        {
            MessageBox.Show(e.Error.Message);
        }

        enableUi();
        return;
    }

    var items = (JsonArray)JsonValue.Load(e.Result);
            
    var typedItems = new ObservableCollection<structuretype>();

    foreach (var item in items)
    {
        var structureType = new StructureType();
        structureType.ID = int.Parse(item["ID"].ToString());
        structureType.Name = item["Name"].ToString().Replace("\\\"", "\"").Trim('"').Trim();

        // Тут подбираем цвета для каждого из типов структур
        switch (structureType.Name)
        {
            case "Сервисное":
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x72, 0x72));
                break;
            case "Зарабатывающее":
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0x40, 0xC7, 0x40));
                break;
            default:
                structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xB4, 0xB4, 0xB4));
                break;
        }

        typedItems.Add(structureType);
    }

    cbStructureTypes.ItemsSource = typedItems;

    var serviceUri = new Uri("/Core/GetNodes", UriKind.Relative);
    var webClient = new WebClient();
    webClient.OpenReadCompleted += onGetNodesCompleted;
    webClient.OpenReadAsync(serviceUri);
}
private void onGetNodesCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error != null)
    {
        if (e.Error.Message != null)
        {
            MessageBox.Show(e.Error.Message);
        }

        enableUi();
        return;
    }

    var notSortedItems = new ObservableCollection<structureitem>();
    var structureTypes = cbStructureTypes.ItemsSource as ObservableCollection<structuretype>;

    var nodes = (JsonArray)JsonValue.Load(e.Result);

    foreach (var node in nodes)
    {
        var structureItem = new StructureItem();

        structureItem.ID = int.Parse(node["ID"].ToString());
        structureItem.Name = node["Name"].ToString().Replace("\\\"", "\"").Replace("\"\"", "\"");

        var typeId = int.Parse(node["TypeID"].ToString());
        foreach (var sType in structureTypes)
        {
            if (sType.ID == typeId)
            {
                structureItem.StructureType = sType;
            }
        }

        structureItem.ParentID =
            node["ParentID"] == null || string.IsNullOrWhiteSpace(node["ParentID"].ToString()) ?
            0 : int.Parse(node["ParentID"].ToString());

        notSortedItems.Add(structureItem);
    }

    var sortedItems = new ObservableCollection<structureitem>();

    notSortedItems.ToList().ForEach(q =>
        {
            if (q.ParentID.HasValue && q.ParentID.Value == 0)
            {
                sortedItems.Add(storeTree(q, notSortedItems));
            }
        });

    twStructures.ItemsSource = sortedItems;

    enableUi();
}
private StructureItem storeTree(StructureItem item, ObservableCollection<structureitem> allItems)
{
    var childItems = allItems.Where(q => q.ParentID == item.ID).ToList();

    if (childItems.Count > 0)
    {
        childItems.ForEach(q =>
        {
            item.ChildItems.Add(storeTree(q, allItems));
        });
    }

    return item;
}
// Обработчик события нажатия на чекбокс в дереве
private void checkBox_Click(object sender, RoutedEventArgs e)
{
    if (m_selectedItems == null)
        m_selectedItems = new List<structureitem>();

    var currentCheckBox = sender as CheckBox;
    if (currentCheckBox.IsChecked.HasValue && currentCheckBox.IsChecked.Value)
    {
        m_selectedItems.Add(currentCheckBox.Tag as StructureItem);
    }
    else
    {
        m_selectedItems.Remove(currentCheckBox.Tag as StructureItem);
    }
}
private void bSave_Click(object sender, RoutedEventArgs e)
{
    if (cbStructureTypes.SelectedItem != null && m_selectedItems != null && m_selectedItems.Count > 0)
    {
        disableUi();
        m_selectedItems.ForEach(q =>
            {
                q.StructureType = cbStructureTypes.SelectedItem as StructureType;
                q.Checked = false;
            });

        sendToSite();
        m_selectedItems.Clear();
        enableUi();
    }
}
// Вызов метода апдейт
private void sendToSite()
{
    var webClient = new WebClient();
    webClient.Headers["Content-type"] = "application/x-www-form-urlencoded";
    var parametersString = new StringBuilder();
    m_selectedItems.ForEach(s =>
    {
        if (parametersString.ToString() != string.Empty)
            parametersString.Append(";");
        parametersString.AppendFormat("{0}={1}", s.ID, s.StructureType.ID);
    });
    webClient.UploadStringAsync(new Uri("/Core/UpdateStructures/", UriKind.Relative), "POST", string.Format("structures={0}", parametersString.ToString()));
}
Разметка XAML
<Grid.Resources>

    <sdk:HierarchicalDataTemplate x:Key="tvTemplate" ItemsSource="{Binding Path=ChildItems}" >

        <StackPanel Orientation="Horizontal">

            <CheckBox Tag="{Binding}" IsChecked="{Binding Path=Checked, Mode=TwoWay}" Click="checkBox_Click" />

            <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=StructureType.Color}"/>

            <TextBlock Text="{Binding Path=Name}" />

        </StackPanel>

    </sdk:HierarchicalDataTemplate>

</Grid.Resources>

<sdk:TreeView Margin="12,41,12,12"

                Name="twStructures"

                BorderBrush="#FFB1B1B1"

                ItemsSource="{Binding}"

                ItemTemplate="{StaticResource tvTemplate}"

                SelectedValuePath="ID"/>

<ComboBox Height="23" Margin="12,12,0,0" Name="cbStructureTypes" VerticalAlignment="Top" ItemsSource="{Binding}" HorizontalAlignment="Left" Width="219">

    <ComboBox.ItemTemplate>

        <DataTemplate>

            <StackPanel Orientation="Horizontal">

                <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=Color}"/>

            <TextBlock Text="{Binding Path=Name}" />

            </StackPanel>

        </DataTemplate>               

    </ComboBox.ItemTemplate>

</ComboBox>

<Button Content="Сохранить" Height="23" Margin="237,12,0,0" Name="bSave" VerticalAlignment="Top" HorizontalAlignment="Left" Width="75" Click="bSave_Click" />

<toolkit:BusyIndicator BusyContent="Loading please wait..." Name="bi" />

Интересно, кто нибудь, умеет правильно вызывать методы контроллеров с параметрами из Silverlight?

Tuesday, February 14, 2012

SPSecurity.RunWithElevatedPrivileges в Sharepoint 2010

Недавно мне попался в руки проект, в котором я принимал участие в роли bugfixer-а. Мне попался стандартный блок кода с повышением привилегий при работе списком в EventReciever-е списка:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite site = properties.OpenSite())
    {
        using (SPWeb web = site.OpenWeb())
        {
С виду этот блок кода выглядел, нормальным, но он не работал. Не долго думая, я включил рефлектор и решил посмотреть, что же тут не так? Данные из рефлектора:
public SPSite OpenSite() 
{ 
    if ((this.m_site == null) && (this.WebUrl != null)) 
    { 
        if (base.EventUserToken != null) 
        { 
            this.m_site = new SPSite(this.WebUrl, base.EventUserToken); 
        } 
        else
        { 
            this.m_site = new SPSite(this.WebUrl); 
        } 
        this.m_siteCreatedByThis = true; 
    } 
    return this.m_site; 
}
public SPWeb OpenWeb()
{
    this.OpenSite();
    if (this.m_site != null)
    {
        return this.m_site.OpenWeb(this.RelativeWebUrl);
    }
    return null;
}

Оказалось, что сайт, открывается всегда с определенным EventUserToken, который в контексте EventReciever всегда был != null.

Будьте осторожны, при использовании методов OpenSite(), OpenWeb() внутри List EventReciever-ов.