Использование и реализация UriLinkAPI и событий UriOpening
Использование и реализация API для обработки ссылок¶
Desktop-клиент¶
Пример реализации пользовательского обработчика ссылок IUriLinkHandler
для desktop-клиента:
namespace Tessa.Extensions.Client
{
// Класс TestUriLinkHandler наследуем от StandardUriLinkHandler, чтобы иметь доступ к стандартной функциональности обработки ссылок.
public class TestUriLinkHandler : StandardUriLinkHandler
{
public override async ValueTask OpenAsync(Uri uri, UriLinkHandlerEventType eventType, CancellationToken cancellationToken = default)
{
switch (eventType)
{
case UriLinkHandlerEventType.HtmlPreview:
// Действия в случае возникновения события из предпросмотра html.
break;
case UriLinkHandlerEventType.HyperLinkLabel:
// Действия в случае возникновения события из контрола "Метка".
break;
// В остальных случаях ссылка обработается стандартным обработчиком.
case UriLinkHandlerEventType.FormattedText:
case UriLinkHandlerEventType.Default:
default:
await base.OpenAsync(uri, eventType, cancellationToken);
break;
}
}
}
}
Регистрация пользовательского обработчика в DI:
namespace Tessa.Extensions.Client
{
[Registrator]
public sealed class TestUriLinkHandlerRegistrator : RegistratorBase
{
public override void FinalizeRegistration()
{
this.UnityContainer
.RegisterType<IUriLinkHandler, TestUriLinkHandler>(new ContainerControlledLifetimeManager());
}
}
}
Web-клиент¶
Т.к. в web-клиенте отсутствует механизм DI, для создания необходимой инфраструктуры UriLinkAPI
используется глобальная фабрика, которая позволяет переопределить пользовательский обработчик ссылок.
Пример реализации пользовательского обработчика ссылок IUriLinkHandler
для web-клиента:
import { IUriLinkHandler } from 'tessa/ui/uriLinks/interfaces';
import { StandardUriLinkHandler } from 'tessa/ui/uriLinks/standardUriLinkHandler';
import { UriLinkHandlerEventType } from 'tessa/ui/uriLinks/uriLinkHandlerEventType';
export class CustomUriLinkHandler extends StandardUriLinkHandler implements IUriLinkHandler {
async openAsync(uriString: string, eventType: UriLinkHandlerEventType): Promise<void> {
switch (eventType) {
case UriLinkHandlerEventType.HtmlPreview:
// Действия в случае возникновения события из предпросмотра html.
break;
case UriLinkHandlerEventType.HyperLinkLabel:
// Действия в случае возникновения события из контрола "Метка".
break;
// В остальных случаях ссылка обработается стандартным обработчиком.
case UriLinkHandlerEventType.FormatedText:
case UriLinkHandlerEventType.Default:
default:
await super.openAsync(uriString, eventType);
break;
}
}
}
Пример расширения, которое переопределяет обработчик ссылок в глобальной фабрике:
import { ApplicationExtension } from 'tessa';
import { CustomUriLinkHandler } from './customUriLInkHandler';
import { UriLinkDependenciesFactory } from 'tessa/ui/uriLinks/uriLinkDependenciesFactory';
import { IApplicationExtensionContext } from 'tessa/applicationExtensionContext';
export class CustomUriLinkHandlerApplicationExtension extends ApplicationExtension {
public initialize(_context: IApplicationExtensionContext): void {
UriLinkDependenciesFactory.instance.setCustomUriLinkHandler(new CustomUriLinkHandler());
}
}
Регистрация расширения, которое переопределяет обработчик ссылок.
import { ExtensionContainer, ExtensionStage } from 'tessa/extensions';
import { CustomUriLinkHandlerApplicationExtension } from './CustomUriLinkHandlerApplicationExtension';
ExtensionContainer.instance.registerExtension({
extension: CustomUriLinkHandlerApplicationExtension,
stage: ExtensionStage.Initialize,
singleton: true,
order: 1
});
Имплементация API для обработки ссылок в других местах, не предусмотренных стандартным решением¶
Note
Примеры сильно упрощены, и служат для того, чтобы показать простоту имплементации и необходимые шаги для этого. Вероятнее всего в реальности фабрику зависимостей будет получать объект где-то по цепочке выше, затем создавать зависимости и саму вью-модель, передавая в нее эти зависимости.
Desktop-клиент¶
Пример вью-модели с имплементацией UriLinkAPI:
public class CustomViewModel : ViewModel<EmptyModel>
{
private readonly IUriLinkDependenciesFactory uriLinkDependenciesFactory;
// Необходимо получить фабрику зависимостей из DI:
public CustomViewModel(IUriLinkDependenciesFactory uriLinkDependenciesFactory)
{
this.uriLinkDependenciesFactory = uriLinkDependenciesFactory;
}
// Some code...
// Метод обработки ссылки.
public async ValueTask OnUriOpeningAsync(Uri uri, ICardModel cardModel, CancellationToken cancellationToken = default)
{
// Получаем зависимости для обработки ссылок.
// Если UIContextExecutor не задан, действие будет выполняться в неизвестном контексте (т.е. будет использован UIHelper.UnknownContextExecutorAsync).
var uriLinkDependencies = this.uriLinkDependenciesFactory.Create(cardModel.UIContextExecutorAsync);
// Запускаем действие через UiContextExecutorAsync из зависимостей, он выполняет заданный метод в контексте IUIContext,
// который устанавливается как текущий контекст и передаётся как параметр в заданный метод.
await uriLinkDependencies.UiContextExecutorAsync.Invoke
(async (context, ct) =>
{
await uriLinkDependencies.UriLinkHandler.OpenAsync(uri, UriLinkHandlerEventType.Default, ct);
}, cancellationToken);
}
// Some code...
}
Web-клиент¶
Пример для web-клиента практически аналогичен примеру для desktop-клиента с той разницей, что в данном случае зависимости нужно получать из глобальной фабрики.
export class CustomViewModel {
// Зависимости для обработки ссылок.
private _uriLinkDependencies: IUriLinkDependencies;
public constructor() {
// Создание зависимостей для обработчиков uri из глобальной фабрики.
// Если UIContextExecutor не задан, действие будет выполняться в неизвестном контексте (UIContext.unknown).
this._uriLinkDependencies = UriLinkDependenciesFactory.instance.create(/*some UIContextExecutor*/);
}
// Some code...
// Метод обработки ссылки.
public handleLinkAsync = async (href: string): Promise<void> => {
this._uriLinkDependencies.uiContextExecutor(async () => {
await this._uriLinkDependencies.uriLinkHandler.openAsync(
href,
UriLinkHandlerEventType.HtmlPreview
);
});
};
// Some code...
}
Определение пользовательских обработчиков событий UriOpening для контролов “Текст с форматированием” и форумов (обсуждений)¶
Desktop-клиент¶
Пример desktop-расширения, которое добавляет обработчик события UriOpening в карточках типа “Автомобиль” для всех контролов “Текст с форматированием”.
public sealed class CarUriOpeningUIExtension : CardUIExtension
{
public override Task Initialized(ICardUIExtensionContext context)
{
if (context.Card.TypeID != DefaultCardTypes.CarTypeID) // CarTypeID - id типа карточки "Автомобиль"
{
return Task.CompletedTask;
}
this.AddHandlers(context);
return Task.CompletedTask;
}
private void AddHandlers(ICardUIExtensionContext context)
{
// Все контролы "Текст с форматированием".
var controls = context.Model.Forms
.SelectMany(x => x.Blocks)
.SelectMany(x => x.Controls)
.Where(x => x is CustomRichTextBoxViewModel).Cast<CustomRichTextBoxViewModel>()
.ToList();
foreach (var control in controls)
{
// Добавление обработчика события.
control.RichTextBox.UriOpening += this.LinkHandler;
}
return;
}
private void LinkHandler(object sender, UriLinkEventArgs e)
{
if (!e.Cancel) // Если требуется проверить не обработана ли данная ссылка уже в другом событии.
{
// Do something...
e.Cancel = true; // True, если требуется отменить вызов глобального обработчика после обработчика события UriOpening.
e.Handled = true; // Это свойство можно использовать, чтобы обозначить, что событие было обработано.
}
}
}
Web-клиент¶
Пример web-расширения, которое добавляет обработчик события UriOpening в карточках типа “Автомобиль” для контрола “Текст с форматированием” в блоке ‘MainInfo’.
export class CarUriOpeningUIExtension extends CardUIExtension {
public initialized(context: ICardUIExtensionContext): void {
if (!Guid.equals(context.card.typeId, CarTypeID)) { // CarTypeID - id типа карточки "Автомобиль"
return;
}
// контрол "Текст с форматированием" в блоке 'MainInfo'
const richTextBoxControl = context.model.forms
.find(x => x.tabCaption == '$CardTypes_Tabs_Card')
?.blocks.find(x => x.name == 'MainInfo')
?.controls.find(x => x instanceof RichTextBoxViewModel) as RichTextBoxViewModel;
if (richTextBoxControl) {
richTextBoxControl.uriOpening.add(this.testUriOpeningHandler);
}
return;
}
private testUriOpeningHandler = (e: UriLinkEventArgs) => {
if (!e.cancel) { // Если требуется проверить не обработана ли данная ссылка уже в другом событии.
// Do something...
e.cancel = true; // True, если требуется отменить вызов глобального обработчика после обработчика события UriOpening.
e.handled = true; // Это свойство можно использовать, чтобы обозначить, что событие было обработано.
}
};
}