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

Отображение диалога при завершении задания

Отображение диалога при завершении задания

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

Диалог выбора должности разделим по паттерну MVVM на составляющие:

  • модель Model - список должностей в виде массива строк;

  • модель представления View Model - список должностей с указанием выбранной должности и команды ICommand для выбора должности из UI;

  • представление View - UserControl, содержащий UI для диалога.

Создадим модель представления PositionDialogViewModel, разместим класс в папке Tessa.Extensions.Client/ViewModels.

using System.Collections.Generic; using System.Collections.ObjectModel; using System.Windows.Input; using Tessa.UI; using Tessa.UI.Controls;

public sealed class PositionDialogViewModel : ViewModel<EmptyModel> { public PositionDialogViewModel(IEnumerable<string> positions) { this.positions = new ObservableCollection<string>(positions); this.elementClickCommand = new DelegateCommand(this.ElementClickAction); }

private readonly ObservableCollection<string> positions;

public ObservableCollection<string> Positions { get { return this.positions; } }

private string selectedPosition;

public string SelectedPosition { get { return this.selectedPosition; } set { this.selectedPosition = value; this.OnPropertyChanged("SelectedPosition"); } }

private readonly ICommand elementClickCommand;

public ICommand ElementClickCommand { get { return this.elementClickCommand; } }

private void ElementClickAction(object obj) { this.SelectedPosition = (string)((AttachedEventParameter)obj).CommandParameter; } }

Визуальное отображение диалога разместим в классе PositionDialogView типа UserControl, в папке Tessa.Extensions.Client/Views.

<!-- PositionDialogView.xaml: UserControl --> <!-- xmlns:tuic="clr-namespace:Tessa.UI.Controls;assembly=Tessa.UI" --> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ItemsControl ItemsSource="{Binding Positions, Mode=OneTime}"> <ItemsControl.ItemTemplate> <DataTemplate> <Border tuic:AttachedCommands.ClickCommand="{Binding DataContext.ElementClickCommand, RelativeSource= {RelativeSource AncestorType=ItemsControl}, Mode=OneTime}" tuic:AttachedCommands.CommandParameter="{Binding Mode=OneTime}" BorderBrush="{StaticResource FileButtonBrush}" MouseLeftButtonDown="ButtonOk_OnClick">

<Border.Background> <SolidColorBrush Color="{StaticResource FileNormalBorderColor}" /> </Border.Background>

<Border.Triggers> <EventTrigger RoutedEvent="Border.MouseEnter"> <BeginStoryboard> <Storyboard> <ColorAnimation Duration="0:0:0.2" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="{StaticResource FileMouseOverBorderColor}" /> </Storyboard> </BeginStoryboard> </EventTrigger>

<EventTrigger RoutedEvent="Border.MouseLeave"> <BeginStoryboard> <Storyboard> <ColorAnimation Duration="0:0:0.2" FillBehavior="Stop" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="{StaticResource FileNormalBorderColor}" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Border.Triggers>

<TextBlock HorizontalAlignment="Center" Padding="5" Text="{Binding Converter={StaticResource LocalizableStringConverter}, Mode=OneTime}" /> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </ScrollViewer>

В code behind PositionDialogView.xaml.cs определим обработчик ButtonOk_OnClick, обрабатывающий закрытие диалога по клику на одной из должностей.

public partial class PositionDialogView { public PositionDialogView() { this.InitializeComponent(); }

private void ButtonOk_OnClick(object sender, RoutedEventArgs e) { Window window = Window.GetWindow(this); if (window != null) { window.DialogResult = true; window.Close(); } } }

Связь между PositionDialogView и PositionDialogViewModel опишем в виде DataTemplate, который расположим в словаре ресурсов Tessa.Extensions.Client/Themes/Generic.xaml.

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

<ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/Tessa.Extensions.Default.Client;component/Themes/Generic.xaml" /> </ResourceDictionary.MergedDictionaries>

<DataTemplate DataType="{x:Type viewModels:PositionDialogViewModel}"> <views:PositionDialogView /> </DataTemplate>

</ResourceDictionary>

При завершении задания согласования с вариантами “Согласовать” или “Не согласовать” будет срабатывать клиентское расширение на завершение задания CardStoreTaskExtension, которое может отобразить диалог в потоке UI, а затем, в зависимости от результатов диалога, либо сохранить выбранную должность для передачи на сервер, либо отменить сохранение и отобразить пользователю сообщение.

using System.Linq; using System.Windows; using System.Windows.Input; using Tessa.Cards.Extensions; using Tessa.Extensions.Default.Shared; using Tessa.Platform; using Tessa.Platform.Validation; using Tessa.Themes; using Tessa.UI; using Tessa.UI.Windows;

public sealed class SelectApprovalPositionExtension : CardStoreTaskExtension { public override void StoreTaskBeforeRequest(ICardStoreTaskExtensionContext context) { // отслеживаем завершение заданий с вариантами "Согласовать" и "Не согласовать"

if (context.IsCompletion && context.TaskType.ID == DefaultTaskTypes.KrApproveTypeID && (context.CompletionOption.ID == DefaultCompletionOptions.Approve || context.CompletionOption.ID == DefaultCompletionOptions.Disapprove)) { // пусть должность - это некоторая строка, в т.ч. строка локализации

// получаем список должностей для текущего пользователя // (можем получить его с сервера, например, обратившись к представлению) string[] positions = new[] { "Заместитель генерального директора", "Руководитель отдела разработки", "$Workplaces_Administrator" } .OrderByLocalized(x => x) .ToArray();

// сохранение выполняется асинхронно, поэтому обращаемся за синхронным выполнением в потоке UI DispatcherHelper.InvokeInUI(() => { // создаём модель представления ViewModel с указанием модели - списка должностей var vm = new PositionDialogViewModel(positions);

// создаём стилизованное окно, контентом которого является модель представления vm var window = new TessaWindow { Content = vm, Title = "Выберите должность", Background = ThemeManager.Current.Theme.CreateSolidColorBrush(ThemeProperty.FileTypesBackground), WindowStartupLocation = WindowStartupLocation.CenterOwner, MinWidth = 360.0, SizeToContent = SizeToContent.WidthAndHeight, CanMinimize = false, CanResize = false, CloseKey = new KeyGesture(Key.Escape), };

window.ResolveOwnerAsActiveWindow(); window.RestrictSizeToWorkingArea(restrictWidth: false);

// отображаем диалог выбора должности bool? dialogResult = window.ShowDialog();

// если должность не выбрана, то пишем ошибку валидации, // которая прерывает процесс сохранения и отображает пользователю сообщение if (dialogResult != true || vm.SelectedPosition == null) { ValidationSequence .Begin(context.ValidationResult) .SetObjectName(this) .ErrorText("Завершение задания отменено.") .End();

return; }

// устанавливаем выбранную должно в Info объекта задания, которое можно будет обработать на сервере string position = vm.SelectedPosition; context.Task.Info["Position"] = position; }); } } }

Регистрация расширения выполняется со стороны клиента стандартным образом.

extensionContainer.RegisterExtension<ICardStoreTaskExtension, SelectApprovalPositionExtension>(x => x .WithOrder(ExtensionStage.AfterPlatform, 1) .WithSingleton<SelectApprovalPositionExtension>()) ;

Back to top