Перейти к содержанию

Расширение для представлений, которое добавляет текст или кнопку на панель фильтрации

Расширение для представлений, которое добавляет текст или кнопку на панель фильтрации

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

Для этого используются расширения IWorkplaceViewComponentExtension вместе со связкой ViewModel+View для отображаемого содержимого.

В сборке Tessa.Extensions.Client создадим папку Views и добавим в неё класс расширения TotalSizeWorkplaceComponentExtension.

using Tessa.Platform.Runtime; using Tessa.UI.Views; using Tessa.UI.Views.Content;

namespace Tessa.Extensions.Client.Views { public sealed class TotalSizeWorkplaceComponentExtension : IWorkplaceViewComponentExtension { public TotalSizeWorkplaceComponentExtension(ISession session) { // любые сервисы можно получить из Unity здесь this.session = session; }

private readonly ISession session;

public void Initialize(IWorkplaceViewComponent model) { // добавляем ViewModel с некоторым уникальным именем в область тулбара ContentPlaceAreas.ToolbarPlaces model.ContentFactories.Add( "TotalSizeToolbar", component => new TotalSizeWorkplaceComponentViewModel( this.session, component, ContentPlaceAreas.ToolbarPlaces, ordering: int.MinValue)); }

public void Initialized(IWorkplaceViewComponent model) { }

public void Clone(IWorkplaceViewComponent source, IWorkplaceViewComponent cloned, ICloneableContext context) { } } }

Для расширения пишется регистратор в классе Registrator, который выполняет регистрацию в специальном объекте IWorkplaceExtensionRegistry.

using Microsoft.Practices.Unity; using Tessa.UI.Views.Extensions;

namespace Tessa.Extensions.Client.Views { [Registrator] public sealed class Registrator : RegistratorBase { public override void FinalizeRegistration() { this.UnityContainer .Resolve<IWorkplaceExtensionRegistry>() .Register(typeof(TotalSizeWorkplaceComponentExtension)) ; } } }

Модель представления TotalSizeWorkplaceComponentViewModel получает через конструктор как зависимости, необходимые для базового класса BaseContentItem, так и произвольные зависимости из Unity (здесь это ISession).

Далее в представлении задаётся метод Refresh, определяющий, что происходит при очередном обновлении данных в представлении. В нашей реализации обновляется свойство Text с некоторыми текстом, который зависит от данных представления.

Метод ButtonAction (совместно с командой ButtonCommand) определяет некоторую бизнес-логику при нажатии на кнопку, которая размещается рядом с прочими системными кнопками.

using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Input; using Tessa.Platform.Runtime; using Tessa.Platform.Storage; using Tessa.UI; using Tessa.UI.Views; using Tessa.UI.Views.Content;

namespace Tessa.Extensions.Client.Views { public sealed class TotalSizeWorkplaceComponentViewModel : BaseContentItem { public TotalSizeWorkplaceComponentViewModel( ISession session, IWorkplaceViewComponent component, IEnumerable<IPlaceArea> placeAreas, Func<IPlaceArea, DataTemplate> dataTemplateFunc = null, int ordering = PlacementOrdering.Middle) : base(placeAreas, dataTemplateFunc, ordering) { this.session = session;

this.component = component; this.component.PropertyChanged += this.ComponentPropertyChanged;

this.buttonCommand = new DelegateCommand(this.ButtonAction); }

private readonly ISession session;

private readonly IWorkplaceViewComponent component;

private void Refresh() { IEnumerable<IDictionary<string, object>> data = this.component.Data; if (data == null || this.component.IsDataLoading) { this.Text = null; return; }

const int maxSizeInGb = 30; // значение можно получить из настроек

// пусть текущий размер хранится в представлении в колонке TotalSize для первой возвращаемой строки IDictionary<string, object> firstRow = data.FirstOrDefault(); double totalSizeInMb = firstRow != null ? firstRow.Get<double>("TotalSize") : 0.0;

// в гигабайтах цифра будет меньше на 1000 double totalSizeInGb = totalSizeInMb / 1000.0; int percent = Math.Min((int)(totalSizeInMb / 10.0 / maxSizeInGb), 100);

// используем культуру для форматирования чисел с плавающей запятой из настроек текущего сотрудника string totalSizeText = totalSizeInGb.ToString("F1", this.session.ClientCulture).TrimEnd('0', '.', ','); this.Text = string.Format("Занято {0}% {1} Гб из {2}", percent, totalSizeText, maxSizeInGb); }

private void ComponentPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Data" || e.PropertyName == "IsDataLoading") { this.Refresh(); } }

private void ButtonAction(object parameter) { TessaDialog.ShowMessage("Имя пользователя: " + this.session.User.Name); }

private string text;

public string Text { get { return this.text; } set { if (this.text != value) { this.text = value; this.OnPropertyChanged("Text"); } } }

private readonly ICommand buttonCommand;

public ICommand ButtonCommand { get { return this.buttonCommand; } } } }

Теперь добавим View для созданной ViewModel, разместим её в UserControl с именем TotalSizeWorkplaceComponentView.xaml.

<UserControl x:Class="Tessa.Extensions.Client.Views.TotalSizeWorkplaceComponentView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tuic="clr-namespace:Tessa.UI.Controls;assembly=Tessa.UI" d:DesignHeight="300" d:DesignWidth="300" mc:Ignorable="d">

<StackPanel Orientation="Horizontal"> <TextBlock Margin="10,0" VerticalAlignment="Center" FontSize="16" Foreground="Black" Text="{Binding Text}" />

<Button Height="32" Margin="5,3" Command="{Binding ButtonCommand, Mode=OneTime}" Style="{StaticResource BorderlessButton}" ToolTip="Нажмите на кнопку"> <tuic:AutoDisabledPath Data="{StaticResource Thin228}" Fill="Black" Stretch="Uniform" /> </Button> </StackPanel>

</UserControl>

Связь между View и ViewModel определяется объектом DataTemplate в ResourceDictionary с именем ViewModels.xaml, который уже располагается в папке Resources для сборки Tessa.Extensions.Client.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Tessa.Extensions.Client" xmlns:views="clr-namespace:Tessa.Extensions.Client.Views">

<!-- Здесь располагаются DataTemplate для связи ViewModel и View -->

<DataTemplate DataType="{x:Type views:TotalSizeWorkplaceComponentViewModel}"> <views:TotalSizeWorkplaceComponentView /> </DataTemplate>

</ResourceDictionary>

Далее создадим представление TotalSizeView, которое содержит простой запрос, возвращающий строку с единственной колонкой TotalSize (случайное число от 100 до 30000). Вкладка “Метаинформация” в представлении будет пуста.

declare @min float = 100 declare @max float = 30000

select round(@min + rand() * (@max - @min - 1), 0) as TotalSize

Скомпилируйте расширения Tessa.Extensions.Client, скопируйте их в папку Tessa Admin и перезапустите его. В рабочем месте добавьте представление в дерево, и укажите расширение TotalSizeWorkplaceComponentExtension.

В результате на вкладке “Просмотр” будет доступна надпись и кнопка, при нажатии на которую отображается имя текущего сотрудника. Надпись будет изменяться каждый раз, когда вы обновляете представление, за счёт функции rand() в запросе SQL.

Теперь вы можете скопировать расширения в папку Tessa Client и опубликовать приложение. Если вы корректно настроили роли для представления TotalSizeView, то пользователи получат обновлённую версию приложения вместе с написанными расширениями.

Back to top