Дополнительные поля фильтра списковых форм

При необходимости в фильтр списковых форм можно добавить помимо стандартных (доступных для выбора через форму подбора колонок, вызываемую по нажатию клавиши Key-F2) дополнительные свойства и/или логику.

Сделать это, однако, можно только для пользовательских (кастомизированных) списковых форм, но не для форм, генерируемых системой по умолчанию. Это ограничение легко обойти, создав в несколько простых шагов пользовательскую копию списковой формы. Например, для плоского справочника достаточно создать новый объект Windows Form, унаследовать его от BaseFlatDictionaryListForm и реализовать интерфейсы IRecordBrowser<T> и IRecordSelector<T>, где Т – тип справочника (подробно процесс описан на начальном этапе создания Списковой формы справочника):

public partial class ArticleListForm : BaseFlatDictionaryListForm,

  IRecordBrowser<Article>, IRecordSelector<Article>

{

  public ArticleListForm()

  {

      InitializeComponent();

  }

}

Добавить свое свойство в фильтр списковой формы можно двумя способами:

1.добавить одно или несколько дополнительных свойств в Пользовательский фильтр. Добавленные таким образом свойства будут изначально доступны всем пользователям. При настройке Пользовательского фильтра через форму подбора колонок, вызываемую по нажатию клавиши Key-F2, их нельзя будет удалить;

2.настроить с нуля Стандартный фильтр, добавив туда требуемые свойства и, при необходимости, стандартные (все или выборочно).

Рассмотрим оба варианта на примере списковой формы справочника Articles. При этом для простоты примера добавим в фильтр не какую-либо сложную логику, а свойство самого справочника.

Для первого примера добавим в Пользовательский фильтр свойство Name. Для этого достаточно реализовать в классе списковой формы следующую логику:

public ArticleListForm()

{

   InitializeComponent();

 

   AddAdditionalFilterControls();

}

 

// Элемент управления, в котором будет вводиться текст фильтра

private TextEdit ArticleNameFilterEdit { get; set; }

 

private void AddAdditionalFilterControls()

{

  // Подпись к полю ввода фильтра

  var ArticleNameFilterLable = new LabelControl

   {

       Text = "Name",

       Dock = DockStyle.Top

   };

 

  // Элемент управления – поле ввода фильтра

   ArticleNameFilterEdit = new TextEdit

   {

       Name = "ArticleNameFilterEdit",

       Dock = DockStyle.Top

   };

 

  // Добавляем подпись и поле ввода в фильтр

   GridPanel.FilterControl.AdditionalFilterControls.Add(ArticleNameFilterLable);

   GridPanel.FilterControl.AdditionalFilterControls.Add(ArticleNameFilterEdit);

   ArticleNameFilterEdit.BringToFront();

   GridPanel.FilterControl.BestSizeAdditionalFilterControls();

}

 

// Применяем фильтр

private void GridPanel_ApplyCustomFilter(object sender, CustomFilterEventArgs args)

{

  if (args.FilterActive && !string.IsNullOrWhiteSpace(ArticleNameFilterEdit.Text))

   {

      var name = ArticleNameFilterEdit.Text;

 

       args.AddFilter<Article>(a => a.name.ToLower().Contains(name));

   }

}

Второй пример предполагает создание нового элемента управления для Стандартного фильтра. Создаем новый элемент User control, наследуем его от CommonControl (из пространства имен Ultima.Client) и реализуем интерфейс ICompositeFilterControl (из пространства имен Ultima.Client.Controls.Filtering):

public partial class MyFilterControl : CommonControl, ICompositeFilterControl

{

  public MyFilterControl()

   {

       InitializeComponent();

   }

}

Реализуем интерфейсы ICompositeFilterControl:

Example_Filter

public partial class MyFilterControl : CommonControl, ICompositeFilterControl

{

  public MyFilterControl()

   {

       InitializeComponent();

   }

 

  public event EventHandler ApplyFilters;

 

  public ClassDescriptors.IClassDescriptor ClassDescriptor

   {

      get

       {

          throw new NotImplementedException();

       }

      set

       {

          throw new NotImplementedException();

       }

   }

 

  public event EventHandler FilterExpressionChanged;

 

  public Task<System.Linq.Expressions.LambdaExpression> GetFilterExpressionAsync()

   {

      throw new NotImplementedException();

   }

 

  public void ResetFilter()

   {

      throw new NotImplementedException();

   }

 

  public void ShowCustomizationForm()

   {

      throw new NotImplementedException();

   }

 

  public bool SupportsCustomization

   {

      get { throw new NotImplementedException(); }

   }

}

Размещаем на фильтре элементы управления, посредством которых будет осуществлять ввод значений фильтра – Name, State и Brand:

Example_Filter2

Реализуем логику фильтра:

public partial class MyFilterControl : CommonControl, ICompositeFilterControl

{

  public MyFilterControl()

   {

       InitializeComponent();

   }

 

  public event EventHandler ApplyFilters;

 

  // Заглушка для события ApplyFilters

  private void OnApplyFilters()

   {

       ApplyFilters.SafeInvoke(this, EventArgs.Empty);

   }

 

  public event EventHandler FilterExpressionChanged;

 

  // Заглушка для события ApplyFilters

  private void OnFilterExpressionChanged()

 {

         FilterExpressionChanged.SafeInvoke(this, EventArgs.Empty);

 }

 

  // В дескрипторе класса указываем наш справочник

  public ClassDescriptors.IClassDescriptor ClassDescriptor

   {

         get { return Article.StaticClassDescriptor; }

         set { /* ignore */ }

 }

 

  // Применяем фильтр

  public async Task<System.Linq.Expressions.LambdaExpression> GetFilterExpressionAsync()

   {

      // Объект фильтра

      var filter = PredicateBuilder.Get<Article>();

 

      // И условия фильтра

      if (!string.IsNullOrWhiteSpace(NameEdit.Text))

       {

          var name = NameEdit.Text;

           filter = filter.And(a => a.Name.ToLower().Contains(name.ToLower()));

       }

 

      if (StateEdit.SelectedList.Any())

       {

          var stateList = StateEdit.SelectedList;

           filter = filter.And(a => stateList.Contains(a.StateID));

       }

 

      if (BrandEdit.SelectedList.Any())

       {

          var brandList = BrandEdit.SelectedList;

           filter = filter.And(a => brandList.Contains(a.BrandID));

       }

 

      return await Task.FromResult(filter);

   }

 

  // Сбрасываем значения фильтра

  public void ResetFilter()

   {

       NameEdit.ResetText();

       StateEdit.ClearSelectedRecords();

       BrandEdit.ClearSelectedRecords();

   }

 

  public void ShowCustomizationForm()

   {

      throw new NotImplementedException();

   }

 

  // Запрещаем настройку фильтра стандартными средствами кастомизации

  public bool SupportsCustomization

   {

         get { return false; }

   }

}

И, наконец, остается указать в классе списковой формы справочника на необходимость использовать созданный фильтр:

public BaseArticleListForm()

{

   InitializeComponent();

 

   GridPanel.DefaultFilterControl = new MyFilterControl();

}

По завершении компилируем проект, копируем созданные библиотеки в папку модуля в клиентском приложении Client/ClientModules/TradeTestSolution, перезагружаем метаданные и открываем фильтр списковой формы справочника Articles:

поле Name, добавленное первым способом в Пользовательский фильтр (User filter), отображается в верхней части фильтра;

поля, добавленные вторым способом в Стандартный фильтр (Default filter), отображаются в нижней его части.

Example_Filter3