Введение
Это продолжение статьи о создании надстройки для Excel с помощью библиотеки Excel-DNA о сборке простой надстройки с пользовательской формулой. Здесь рассмотрим как сделать свою панель с кнопками, взаимодействовать с рабочей книгой, выводить уведомления, а также получить доступ к элементам интерфейса Excel.
Создание панели
Панели Ribbon описываются в XML. Простейшая выглядит так:
<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
<ribbon>
<tabs>
<tab id='MyAddinTab' label='My Addin Tab'>
<group id='MyAddinGroup' label='My Addin Group'>
<button id='Button1' label='Button 1' size='large' imageMso='HappyFace' />
<button id='Button2' label='Button 2' size='large' imageMso='SadFace' />
<button id='Button3' label='Button 3' size='large' imageMso='Piggy' />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Тег tab
описывает вкладки на панели инструментов, group
- группы элементов, которые визуально отделяются друг от друга вертикальной чертой, button
- элементы кнопок.
Для того чтобы отобразить такую панель в Excel нужно наследовать классу ExcelRibbon
и переопределить метод GetCustomUI
в котором возвращаем XML панели.
using ExcelDna.Integration.CustomUI;
namespace ExcelAddIn.Controllers;
public class RibbonController : ExcelRibbon
{
public override string GetCustomUI(string ribbonID)
{
return @"<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
<ribbon>
<tabs>
<tab id='MyAddinTab' label='My Addin Tab'>
<group id='MyAddinGroup' label='My Addin Group'>
<button id='Button1' label='Button 1' size='large' imageMso='HappyFace' />
<button id='Button2' label='Button 2' size='large' imageMso='SadFace' />
<button id='Button3' label='Button 3' size='large' imageMso='Piggy' />
</group>
</tab>
</tabs>
</ribbon>
</customUI>";
}
}
Фабрика инструментов
Определим абстрактный класс инструмента, который будет вызываться кнопкой:
namespace ExcelAddIn.Tools;
public abstract class Tool : IDisposable
{
public abstract void Execute();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected abstract void Dispose(bool disposing);
}
И фабрику этих инструментов:
using ExcelDna.Integration.CustomUI;
namespace ExcelAddIn.Tools;
public static class ToolFactory
{
public static Tool GetTool(IRibbonControl control)
{
return control.Id switch
{
"Button1" => new Button1Tool(),
"Button2" => new Button2Tool(),
"Button3" => new Button3Tool(),
_ => throw new NotImplementedException(control.Id)
};
}
}
Чтобы кнопка заработала, нужно добавить в её описание атрибут onAction
с именем метода который будем выполнять.
using ExcelDna.Integration.CustomUI;
using ExcelAddIn.Tools;
namespace ExcelAddIn.Controllers;
public class RibbonController : ExcelRibbon
{
public override string GetCustomUI(string ribbonID)
{
return @"<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
<ribbon>
<tabs>
<tab id='MyAddinTab' label='My Addin Tab'>
<group id='MyAddinGroup' label='My Addin Group'>
<button id='Button1' label='Button 1' size='large' imageMso='HappyFace' onAction='OnToolPressed'/>
<button id='Button2' label='Button 2' size='large' imageMso='SadFace' onAction='OnToolPressed'/>
<button id='Button3' label='Button 3' size='large' imageMso='Piggy' onAction='OnToolPressed'/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>";
}
public void OnToolPressed(IRibbonControl control)
{
using var tool = ToolFactory.GetTool(control);
tool.Execute();
}
}
Первый инструмент
Пусть первая кнопка просто покажет нам сообщение в MessageBox
.
Чтобы надстройка смогла работать с Windows Forms нужно добавить в .csproj
строку:
<PropertyGroup>
...
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
Теперь сделаем класс-наследник абстрактного класса Tool
:
namespace ExcelAddIn.Tools;
public class Button1Tool : Tool
{
public override void Execute()
{
MessageBox.Show($"Message from {nameof(Button1Tool)}");
}
protected override void Dispose(bool disposing)
{
}
}
Получаем доступ к рабочей книге
Взаимодействие с Excel - его рабочими книгами, листами, интерфейсом - осуществляется через интерфейс Application
.
Чтобы получить к нему доступ нужно установить пакет ExcelDna.Interop
:
dotnet add package ExcelDna.Interop
Объект Application
является свойством класса ExcelDnaUtil
.
Пусть вторая кнопка инкрементирует текущую ячейку:
using Application = Microsoft.Office.Interop.Excel.Application;
namespace ExcelAddIn.Tools;
public class Button2Tool : Tool
{
private readonly Application app;
public Button2Tool()
{
app = (Application)ExcelDnaUtil.Application;
}
public override void Execute()
{
if (app.ActiveCell == null)
{
return;
}
double? cellValue = app.ActiveCell.Cells.Value2;
if (cellValue != null)
{
app.ActiveCell.Cells.Value2 = ++cellValue;
}
}
protected override void Dispose(bool disposing)
{
}
}
Строка состояния
В нижней части окна Excel есть строка состояния, куда можно выводить сервисную информацию.
Это бывает полезно для отображения процесса какой-либо ресурсоемкой задачи.
После окончания работы необходимо сбросить строку, передав ей значение false
. Это вернет контроль над ней приложению Excel, иначе в ней зависнет последнее переданное нами сообщение.
using Application = Microsoft.Office.Interop.Excel.Application;
namespace ExcelAddIn.Tools;
public class Button3Tool : Tool
{
private readonly Application app;
public Button3Tool()
{
app = (Application)ExcelDnaUtil.Application;
}
public override void Execute()
{
for (int i = 0; i < 10;)
{
Thread.Sleep(400);
app.StatusBar = $"Выполнено {++i * 10}%...";
}
}
protected override void Dispose(bool disposing)
{
app.StatusBar = false;
}
}
Заключение
В этой статье описываются основы создания собственной панели инструментов надстройки и её взаимодействие с Excel.
Код к этой и предыдущей статье лежит в репозитории https://gitea.cebotari.ru/chebser/ExcelAddIn
Surrogate
Спасибо, хорошая статья!
Немного не хватает: картинки со структурой проекта
И упоминания, о каком файле идёт речь (./Tools/ToolFactory.cs).
Начинающим вроде меня это было бы полезно.
После второго прочтения и просмотра репозитория, даже я понял.