Пишем надстройку для Excel на .NET с помощью библиотеки Excel-DNA
Excel-DNA
Excel-DNA — библиотека для .NET, с помощью которой можно написать и собрать полностью самодостаточный файл надстройки для Excel с расширением .xll
Этот файл достаточно положить в пользовательскую папку, без прав администратора, и просто включить в настройках Excel.
Такая надстройка будет иметь доступ к COM-модели Excel, C API Excel — позволяет взаимодействовать с интерфейсом программы и рабочими книгами. Сможет реализовать пользовательские формулы и добавить свое меню на интерфейсную ленту Ribbon UI.
В статье я расскажу как написать свою формулу для Excel на C#. Собрать и установить свою первую надстройку.
Подготовка
Создадим новый проект библиотеки классов для версии .NET 6
dotnet new classlib --framework net6.0 -o ExcelAddIn
В файле .csproj
нужно изменить значение версии фреймворка на
net6.0-windows
Теперь установим базовый пакет ExcelDna.AddIn
dotnet add package ExcelDna.AddIn
Писать можно и в Visual Studio, и в VSCode.
Чтобы дебажить код в VSCode нужно создать файл launch.json
и в нем изменить две строки, "program"
и "args"
:
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
"args": [
"${workspaceFolder}\\bin\\Debug\\net6.0-windows\\ExcelAddIn-AddIn64.xll"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
Пути к файлу программы Excel и собранной надстройки нужно поставить свои.
Первая формула
Напишем простую формулу, которая складывает два числа:
global using ExcelDna.Integration;
namespace ExcelAddIn;
public static class ExcelFunctions
{
[ExcelFunction]
public static double DNASUM(double a, double b)
{
return a + b;
}
}
Все статические методы помеченные атрибутом [ExcelFunction]
теперь принимаются Excel как пользовательские формулы.
Нажмем F5, откроется Excel, который уже откроет файл нашей надстройки. Поскольку у нас нет цифровой подписи появится предупреждение.
Извещение системы безопасности
Нажимаем «Включить» и проверяем работу нашей формулы
Первая формула работает!
Интерфейс IExcelAddIn
Для работы нашей надстройки может понадобится, чтобы она при открытии и закрытии выполняла какие-либо полезные вещи. Например читала настройки из реестра, подключала базы данных.
Cоздадим класс MyAddIn
, реализующий интерфейс IExcelAddIn
public class MyAddIn : IExcelAddIn
{
public void AutoClose()
{
throw new NotImplementedException();
}
public void AutoOpen()
{
throw new NotImplementedException();
}
}
При открытии надстройки будет создан экземпляр класса MyAddIn
и выполнен метод AutoOpen()
Делаем формулу запроса курса валют с сайта ЦБ
Напишем теперь формулу, которая делает что-то полезное, например запрашивает курс валют с сайта ЦБ.
Интерфейс для клиента:
namespace ExcelAddIn.Services;
public interface ICurrencyClient
{
public Task GetExchangeRate(DateTime date);
}
Класс клиента API сайта ЦБ. Парсим XML с курсами на нужную дату:
using System.Diagnostics;
using System.Text;
using System.Xml.Linq;
namespace ExcelAddIn.Services;
public class CurrencyClient : ICurrencyClient
{
private readonly HttpClient _httpClient;
private const string _requestAddress = @"https://www.cbr.ru/scripts/XML_daily.asp?date_req=";
public CurrencyClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task GetExchangeRate(DateTime date)
{
string request = $"{_requestAddress}{date.Date:dd/MM/yyyy}";
HttpResponseMessage response = await _httpClient.GetAsync(request);
try
{
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
var xml = Encoding.GetEncoding(1251).GetString(bytes);
XElement valCourses = XElement.Parse(xml);
decimal? exchangeRate = decimal.Parse(valCourses.Elements("Valute")
.Where(e => e.Element("Name").Value == "Евро")
.FirstOrDefault()
.Element("Value").Value);
return exchangeRate;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
Добавляем нужные сервисы и создаем провайдер сервисов
using Microsoft.Extensions.DependencyInjection;
using ExcelAddIn.Services;
using System.Text;
public class MyAddIn : IExcelAddIn
{
public static ServiceProvider ServiceProvider { get; private set; }
public void AutoOpen()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
IServiceCollection Services = new ServiceCollection();
Services.AddHttpClient()
.AddSingleton();
ServiceProvider = Services.BuildServiceProvider();
}
public void AutoClose()
{
}
}
И, наконец, определяем метод для формулы:
using ExcelAddIn.Services;
using Microsoft.Extensions.DependencyInjection;
namespace ExcelAddIn;
public static class ExcelFunctions
{
[ExcelFunction]
public static object ExchangeRate(double dateField)
{
ICurrencyClient currencyClient = MyAddIn.ServiceProvider.GetService();
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
if (ExcelAsyncUtil.Run(nameof(ExchangeRate), dateField, delegate
{
return currencyClient.GetExchangeRate(date)
.GetAwaiter()
.GetResult() ?? -1m;
}) is not decimal requestResult)
{
return "Загрузка...";
}
else if (requestResult < 0)
{
return ExcelError.ExcelErrorNA;
}
else
{
return Math.Round(requestResult, 2);
}
}
}
Теперь формула =ExchangeRate( )
возвращает сегодняшний курс евро, а если сослаться на ячейку с датой в формате Excel, на нужную дату.
Сборка и установка
Чтобы собрать нашу надстройку в один файл со всеми зависимостями, нужно добавить в файл .csproj
строки
@(References)
Теперь делаем
dotnet build
и в папке \bin\Debug\net6.0-windows
забираем два собранных файла для 32 и 64-разрядных версий Excel
Эти файлы нужно поместить в папку %AppData%\Microsoft\AddIns
и в настройках Excel поставить галочку напротив нашего файла. Готово!
Заключение
В этой статье описывается как написать и собрать простую надстройку для Excel средствами .NET.
Код можно посмотреть по ссылке https://gitea.cebotari.ru/chebser/ExcelAddIn/