Обмен информацией между Revit и Web-приложением
Сегодня расскажу, как решить одну интересную задачу — написать плагин для Revit, который будет работать как веб-страница. Я расскажу про базовый обмен данными между Revit и веб-приложением, в моём случае оно будет статичным, но в целом, никто не мешает вам сделать его динамическим или задеплоить на удалённым сервере.
Задача
- Написать веб-страницу, которая будет представлять собой интерфейс плагина для Revit
- Настроить отображение этой веб-странице в Revit с помощью WebView2.
- Настроить обмен сообщениями между веб-страницей и Revit
- Изменить что-нибудь в Ревит-файле через действия на веб-странице
Создание веб-страницы
Напишем простую веб-страницу, которая будет получать через js-script данные о выделенном в Ревите окне, позволять редактировать высоту нижнего бруса окна,
а потом через тот же скрипт отправлять данные обратно. Если вы читаете мой блог давно, то вы наверное помните, что я C#-программист.
Это действительно так, я лишь в общих чертах разбираюсь в html, css и JavaScript. К счастью, на дворе 2026 год, поэтому я попросил Claude Code написать мне такую веб-страницу.
Надеюсь, мои уважаемые читатели меня за это не осудят.
Он написал всё в одном файле, но я попросил разделить на 3 блока — вёрстку, стили и скрипт. Посмотреть все файлы и сделать копию на своём локальном компьютере можно в моём репозитории, который я создал специально для этого здесь.
Код на JavaScript для нас важен в контексте темы статьи, потому что там я показываю, как идёт подписка на сообщения в WebView2 и их отправка. Я приведу его полностью:
const placeholder = document.getElementById('placeholder');
const panel = document.getElementById('panel');
const winName = document.getElementById('win-name');
const winId = document.getElementById('win-id');
const winHeight = document.getElementById('win-height');
const btnApply = document.getElementById('btn-apply');
let currentDto = null;
function showWindow(dto) {
currentDto = dto;
winName.textContent = dto.name;
winId.textContent = dto.id;
winHeight.value = dto.height;
placeholder.style.display = 'none';
panel.style.display = 'flex';
}
btnApply.addEventListener('click', () => {
if (!currentDto) return;
const updated = {
...currentDto,
height: Number(winHeight.value)
};
window.chrome.webview.postMessage(JSON.stringify(updated));
});
// Listen for messages from WebView2 host
if (window.chrome && window.chrome.webview) {
window.chrome.webview.addEventListener('message', (event) => {
try {
const dto = typeof event.data === 'string'
? JSON.parse(event.data)
: event.data;
showWindow(dto);
} catch (e) {
console.error('Failed to parse message:', e);
}
});
}
// Dev helper: expose showWindow globally for manual testing in browser
window.__devShowWindow = showWindow;
Через JS-код мы получаем сообщения от Revit-файла, и отправляем их обратно. В данном случае у нас получается так — Revit отправляет данные о выделенном окне, и мы их отображаем. Затем пользователь меняет высоту нижнего блока и жмёт на кнопку — в этот момент мы отправляем сообщение.
Вот эта строка кода отвечает за получение сообщений, по сути это подписка на событие получения сообщения от WebView2:
window.chrome.webview.addEventListener('message', (event) => { /*action*/});
По сути мы тут говорим: когда нам пришло сообщение от WebView2, сделай что-то, что именно, вынесу ещё раз:
window.chrome.webview.addEventListener('message', (event) => {
try {
const dto = typeof event.data === 'string'
? JSON.parse(event.data)
: event.data;
showWindow(dto);
} catch (e) {
console.error('Failed to parse message:', e);
}
});
Тут написано следующее:
- Если содержание сообщения имеет строковый тип, преврати его в объект, распарсив его как json. И покажи окно, содержащие данные переданного объекта. А если не получится, сообщи об этом в консоль.
На этом с JS-частью мы заканчиваем. В моей статье её написал Claude, в реальном мире её напишет фронтенд-разработчик (или вы-Revit разработчик, если вы Full-stack или специалист широкого профиля). Давайте посмотрим, как выглядит это окно, и перейдём к C#-коду
HTML-страница с данными из Revit
Отображение созданной Web-страницы в Revit
Я уже однажды писал статью о работе с веб-страницами в Revit.
Там я создал DockPanel, которая выводила саму эту статью из Дзена в окно Ревита. Но сейчас я буду использовать другую технологию — WebView2.
По сути, это технология, которая позволяет использовать браузер Edge внутри десктопных приложений для Windows (да, наше приложение для Revit всё также является десктопным приложение для Windows, хоть и не имеет .exe-файла. Ну ладно, надстройкой для десктопного приложения Autodesk Revit — суть та же, мы пишем не веб-приложение, мы пишем приложение, которое общается с веб-приложением, написанным в первой части, и общаются они через WebView2).
Вы можете спросить: а почему браузер Edge, а не более популярный Chrome, например. Дело в том, что начиная с Windows 10, Edge является частью операционной системы, его нельзя так просто взять и удалить. А значит, мы можем просто использовать небольшой nuget-пакет для работы нашего приложения, и он запустится на большинстве компьютеров с Windows, и нам не надо поставлять браузер отдельно, чтобы гарантировать, что всё будет работать.
Неудаляемый Edge
Итак, для начала добавим Nuget-пакет для WebView2:
<PackageReference Include="Microsoft.Web.WebView2">
<Version>1.0.2739.15</Version>
</PackageReference>
Далее создадим xaml-файл, в котором будем отображать наш WebView2:
<Window x:Class="WPFApplication.DataExchangeExample.WebView2BrowserWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
Title="Window Editor"
Height="600" Width="900"
WindowStartupLocation="CenterScreen">
<Grid>
<wv2:WebView2 x:Name="WebView" />
</Grid>
</Window>
Такая xaml-вёрстка мне нравится, никаких проблем и вопросов.
Далее нам нужно сделать то же самое, что мы сделали в js-файле — настроить приём и отправку сообщений через WebView2.
Мы это обязательно сделаем, но для начала нам надо просто настроить WebView2, чтобы всё нормально отображалось без ошибок. Надёжный способ это сделать
в файле xaml.cs выглядит так:
public WebView2BrowserWindow()
{
InitializeComponent();
InitializeWebViewAsync();
}
private async void InitializeWebViewAsync()
{
try
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WpfApplication");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: path);
await WebView.EnsureCoreWebView2Async(env);
}
catch (Exception exception)
{
//async-related errors
Debug.WriteLine(exception);
}
}
Особое внимание обратите на то, что происходит в методе InitializeWebViewAsync. Если мы не зададим существующую папку для пользовательских
данных (Environment.SpecialFolder.LocalApplicationData), приложение вообще не запустится.
Теперь давайте добавим обработку приёма сообщений от WebView2. Это делается через подписку на событие WebMessageReceived.
Также я здесь добавлю и код для отправки сообщения: оно будет отправляться сразу после загрузки окна:
public partial class WebView2BrowserWindow
{
private readonly WindowDto _dto;
private readonly WindowService _windowService;
public WebView2BrowserWindow(WindowDto dto, WindowService windowService)
{
_dto = dto;
_windowService = windowService;
InitializeComponent();
InitializeWebViewAsync();
}
private async void InitializeWebViewAsync()
{
try
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WpfApplication");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var env = await CoreWebView2Environment.CreateAsync(userDataFolder: path);
await WebView.EnsureCoreWebView2Async(env);
WebView.CoreWebView2.NavigationCompleted += OnNavigationCompleted;
WebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived;
WebView.Source = new Uri(@"path-to-your-html-file.html");
}
catch (Exception exception)
{
//async-related errors
Debug.WriteLine(exception);
}
}
private void OnNavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs args)
{
WebView.CoreWebView2.PostWebMessageAsString(JsonSerializer.Serialize(_dto));
}
private void OnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var json = args.WebMessageAsJson;
var updated = JsonSerializer.Deserialize<WindowDto>(json);
if (updated == null) return;
Dispatcher.Invoke(() => _windowService.UpdateWindowHeight(updated));
}
}
Обмен сообщениями тут тоже довольно простой: отправка в одну строку:
WebView.CoreWebView2.PostWebMessageAsString(JsonSerializer.Serialize(_dto));
Где вместо _dto — тот объект, который вы хотите передать в JS-приложение.
Получение тоже несложное: просто пишем обработчик события:
private void OnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var json = args.WebMessageAsJson;
var model = JsonSerializer.Deserialize<YourDto>(json);
// YourAction(model);
}
Мы, разумеется, видим и факт сообщение, и его содержание. И можем его обрабатывать внутри контекста Revit.
Результат
Я не стал добавлять в статью код, который меняет высоту после получения сообщения, он достаточно тривиален. Вы можете посмотреть его здесь.
Давайте посмотрим на результат работы. Запускаем плагин:
Запуск плагина
И результат работы после закрытия страницы:
Результат работы
Заключение
Со стороны может показаться, что это довольно простая игрушка. Но на самом деле, это очень крутая вещь, которую вы можете развить во множество интересных вещей:
- За счёт сообщений можно обрабатывать данные на удалённом сервере, вынося долгие расчёты на другой компьютер и не тратя время инженеров на ожидание плагина
- За счёт сообщений можно вынести все действия из однопоточного Ревита в многопоточное приложение. Это необязательно будут сообщения через
WebView2, но принцип будет примерно такой же. - Через WebView2 можно уйти от ограничений UI-разработки в WPF и использовать самые современные и красивые технологии из веб-разработки, не страдая при написании каждой анимации
В общем, на основании этой технологии можно сделать серьёзный шаг вперёд в разработке плагинов и уйти от множества ограничений. Так что пользуйтесь и делитесь в комментариях своими идеями применения (я обязательно скоро добавлю их на сайт).
Подписывайтесь на мой телеграм-канал о Revit API. До новых встреч!