среда, 13 июля 2011 г.

Консольные приложения (14): Использование функции-фильтра

В предыдущих сообщениях мы уже рассмотрели 2 из 3 типов фильтров, которые используются при отображении элементов данных в дереве:
Осталось рассмотреть еще один вид фильтра: FunctionFilter (функция-фильтр).

В двух словах: вы можете написать С1 функцию (на C#), которая будет работать как фильтр для элементов заданного типа.

Рассмотрим пример.

У нас будут два простых глобальных типа данных:
  • один будет использоваться для названий жанров кино (напр., фантастика, триллер и т.п.), 
  • другой будет использоваться для названий фильмов. Для фильмов мы также будем указывать год выпуска и жанры, к которым он относится.
Нам нужно будет отображать фильмы, сгруппировав их по жанрам.


Нюанс здесь заключается в том, что однозначно фильм отнести к одному-единственному жанру часто трудно и обычно указывают несколько жанров. Это означает, что один и тот же фильм должен появляться у нас в рамках нескольких жанров (групп).

Вот здесь нам и пригодится функция-фильтр. Итак,
  1. ...создадим тип данных для жанров - Demo.Genre - с одним полем: Name (строка, 64 символа). (Надпись, ярлык, можно указать на русском как "Название").
  2. также создадим тип данных для фильмов - Demo.Movie - с тремя полями:
    • Title (строка, 64 символа) - ярлык: "Название"
    • Year (целое число, или тоже строка) - ярлык: "Год"
    • Genre (строка, неограниченная длина) - ярлык: "Жанр"
Последнее поле Genre - особенное. Для него мы заменим виджет:
  1. Выберите это поле в редакторе типа данных.
  2. Перейдите на вкладку "Расширенные".
  3. Щелкните в "Типе виджета"
  4. Выберите и удалите виджет текстового поля (Composite.Widgets.String.TextBox)
  5. Добавьте виджет мульти-селектора идентификаторов данных (Composite.Widgets.String.DataIdMultiSelector)
  6. В функции виджета выберите параметер "Data type to select from" (т.е. тип данных для выбора элементов)
  7. И выберите для него наш типа данных жанра (Genre).
  8. Нажмите ОК и сохраните тип данных.
Для того, чтобы в нашем дереве что-то появилось, добавьте сразу несколько жанров и несколько фильмов. Обратите внимание, что при добавлении фильма у вас будут представлены жанры как опции и можно выбрать несколько жанров.

Создание функции-фильтра

Теперь создадим функцию-фильтр. Проще всего создать встроенную C#-функцию прямо в админке (но можно создать и внешнюю C#-функцию и подключить ее в админке).
  1. В разделе "Функции", выбираем "C# функции" и нажимаем "Добавить встроенную C# функцию".
  2. В окне "Настройки" указываем:
    • Название: IdListFilter
    • Пространство имен: Demo
    • Шаблон: Пустой
  3. В открывшемся редакторе функции, на вкладке "Код", удаляем код по умолчанию и заменяем на свой:

    using System;
    using System.Linq.Expressions;

    namespace Demo
    {
      public static class InlineMethodFunction
      {
        public static Expression<Func<Demo.Movie, bool>> IdListFilter(Guid ParentId)
        {
          Expression<Func<Demo.Movie, bool>> filter = f => f.Genre.Contains(ParentId.ToString());
          return filter;
        }
      }
    }

  4. Теперь на вкладке "Входные параметры", добавляем параметр, указанный в нашем коде:
    • Имя параметра: ParentId
    • Тип параметра: Guid
  5. Сохраняем функцию.
Для фильтра мы используем лямбда-выражение для нашего типа Demo.Movie, и используем входной параметр (идентификатор родительского типа, в нашем случае это будет идентификатор элемента типа Demo.Genre), чтобы проверить, есть ли он среди идентификаторов указанных в поле Genre элемента типа Demo.Movie.

Использование функции-фильтра

Чтобы теперь указать эту функцию в качестве фильтра для элементов данных в дереве:
  1. В XML-элементе DataElements, добавим знакомый нам Filters
  2. В нем добавим FunctionFilter
  3. И уже в FunctionFilter добавим нашу функцию, как обычно добавляется стандартная С1-функция в разметке.
<DataElements Type="Demo.Movie" Label="${C1:Data:Demo.Movie:Title} (${C1:Data:Demo.Movie:Year})">
  <Filters>
    <FunctionFilter>
      <function name="Demo.IdListFilter" xmlns="http://www.composite.net/ns/function/1.0">
        <param name="ParentId" value="${C1:Data:Demo.Genre:Id}"/>
      </function>
    </FunctionFilter>
  </Filters>
</DataElements>

Для функции мы соответственно указываем ее имя (name), а также параметр (param). В качестве значения параметра мы используем динамическое поле.

В конце концов у вас должно получится примерно такое определение дерева для консольного приложения:

<?xml version="1.0" encoding="utf-8" ?>
<ElementStructure xmlns="http://www.composite.net/ns/management/trees/treemarkup/1.0">
  <ElementStructure.AutoAttachments>
    <NamedParent Name="Content" Position="Top"/>
  </ElementStructure.AutoAttachments>
  <ElementRoot>
    <Children>
      <Element Id="MoviesRoot" Label="Фильмы">
        <Children>
          <DataElements Type="Demo.Genre" Icon="pagetype-pagetype-rootfolder">
            <Children>
              <DataElements Type="Demo.Movie" Icon="pagetype-pagetype" Label="${C1:Data:Demo.Movie:Title} (${C1:Data:Demo.Movie:Year})">
                <Filters>
                  <FunctionFilter>
                    <function name="Demo.IdListFilter" xmlns="http://www.composite.net/ns/function/1.0">
                      <param name="ParentId" value="${C1:Data:Demo.Genre:Id}"/>
                    </function>
                  </FunctionFilter>
                </Filters>
              </DataElements>
            </Children>
          </DataElements>
        </Children>
      </Element>
    </Children>
  </ElementRoot>
</ElementStructure>

Заметьте, что в нем отсутствуют стандартные действия для добавления, редактирования и удаления элементов. Об этом я уже писал и вы сможете сделать это самостоятельно.


Ну и вы, наверняка, помните, что весь этот и подобный функционал можно упаковать в модуль расширения (т.е. экспортировать) и устанавливать на других сайтах на основе C1 (импортировать), распространяя его платно или бесплатно :)


(продолжение следует...)

Комментариев нет:

Отправить комментарий