- Старая школа: формат RTF никто не отменял, однако вручную составить что-либо сложнее цветного текста со шрифтами разного цвета и ссылками лично у меня не получалось. Способ действенный, но стандарт не самый легкий для понимания, на последнюю версию даже смотреть страшно
- Для Silverlight/WPF — формирование FlowDocument своими руками. Объектная модель однозначно удобнее формирования старого формата вручную, но вариант тоже только для смелых
- Сторонние решения вроде Crystal Reports, возможно и очень мощный инструмент для создания отчетов, но не всегда в этом есть необходимость, к тому же не всегда есть возможность тянуть пару-тройку дополнительных программных продуктов1
- Использование Microsoft Office Interop. Нет, это издевательство. Ручная работа с памятью в управляемой среде вызывает лишь бурю радости. Начинающий программист выпьет не одну упаковку кофе, прежде чем смирится с подобным принципом работы.
- Формирование HTML. На нашу радость, стандарт позволяет создать документы средней степени форматированности, с помощью CSS можно довести до ума. Минусы — оформить документ по всем требованиям крайне затруднительно. Но это если в этом есть необходимость. Плюсы: простота стандарта и всеобщий переход «в облака и браузеры» стимулирует развитие HTML.
О последнем я и буду вести речь.
Самый простой и очевидный вариант — формирование HTML строки вручную. Те, кто только знакомится с платформой, тут же начнут заниматься конкатенацией объектов String, кто читал документацию — обратят внимание на класс StringBuilder. В любом случае, String.Format не всегда самый удобный способ формирования отчетов. Зато коллеги по платформе, пользующиеся ASP.NET MVC никак не могут нарадоваться движку представлений Razor. И тут возникает вопрос: а почему бы и не использовать мощь ASP.NET Razor в своих целях?
Прежде всего, необходимо подключить сборку System.Web.Razor. Она из поставки ASP.NET MVC, так что если у целевого компьютера он не установлен, следует позаботиться о локальном копировании необходимых dll’ок.
Объявим базовый класс, от которого будет наследоваться наше представление
public abstract class TemplateBase { public StringBuilder Buffer { get; set; } public StringWriter Writer { get; set; } public TemplateBase() { Buffer = new StringBuilder(); Writer = new StringWriter(Buffer); } public abstract void Execute(); // Записывает в строку выражения типа: "@foo.Bar" public virtual void Write(object value) { // Don't need to do anything special // Razor for ASP.Net does HTML encoding here. WriteLiteral(value); } // Записывает литералы разметки: "<p>Foo</p>" public virtual void WriteLiteral(object value) { Buffer.Append(value); } // ... а здесь мы можем указать еще какие-либо публичные свойства, которые будут передаваться движку Razor вне зависимости от того, хочет он этого или нет }
Прежде всего, необходимо инициализировать движок.
private RazorTemplateEngine SetupRazorEngine() { // 1. Указываем, что хотим использовать C# описания представления RazorEngineHost host = new RazorEngineHost(new CSharpRazorCodeLanguage()); // 2. Указываем базовый класс, от которого наследуется представление. Тот самый, что описывали выше host.DefaultBaseClass = typeof(TemplateBase).FullName; // 3. Указываем пространство имен и название класса представления host.DefaultNamespace = "RazorOutput"; host.DefaultClassName = "Template"; // 4. Указываем используемые по умолчанию пространства имен (using) host.NamespaceImports.Add("System"); // 5. Создаем новый движок представления return new RazorTemplateEngine(host); }
Как известно, .NET поддерживает компиляцию кода «на лету». Эту особенность и использует Razor. Следовательно в нашем случае надо прочитать файл с шаблоном и сохранить его в отдельную сборку.
// Генерируем код шаблона GeneratorResults razorResult = null; using (TextReader rdr = new StringReader( MyTemplatesString )) razorResult = _engine.GenerateCode(rdr); CSharpCodeProvider codeProvider = new CSharpCodeProvider(); // Записываем компилированный код в файл сборки string outputAssemblyName = String.Format("Temp_{0}.dll", Guid.NewGuid().ToString("N")); CompilerResults results = codeProvider.CompileAssemblyFromDom( new CompilerParameters(new string[] { typeof(Form1).Assembly.CodeBase.Replace("file:///", "").Replace("/", "\\") }, outputAssemblyName), razorResult.GeneratedCode);
Если все прошло хорошо, то дальше использование простое:
Assembly asm = Assembly.LoadFrom(outputAssemblyName); if (asm == null) { MessageBox.Show("Ошибка во время загрузки сборки"); } else { Type typ = asm.GetType("RazorOutput.Template"); if (typ == null) { MessageBox.Show("Не найден RazorOutput.Template в сборке {0}", asm.FullName); } else { TemplateBase newTemplate = Activator.CreateInstance(typ) as TemplateBase; if (newTemplate == null) { MessageBox.Show("Невозможно создать экземпляр RazorOutput.Template или он не наследуется TemplateBase"); } else { // здесь мы присваиваем newTemplate все необходимые нам данные // ... newTemplate.Execute(); string resultHTML = newTemplate.Buffer.ToString(); newTemplate.Buffer.Clear(); return resultHTML; // разумеется никто в здравом уме не будет здесь возвращать String, но это исключительно для примера } } }
Вот, собственно и все. Остается только вопрос, как именно указывать данные представления — через TemplateBase, или же через Razor-синтаксис вида
@functions { public string CustomerName { get; set; } public string ResetLink { get; set; } }
В последнем случае вам придется отказаться от динамической генерации шаблонов, зато очень легко получить доступ к этим данным.
Итак, с помощью нехитрых движений мы избежали искушения написания «еще одного» шаблонизатора, воспользовавшись мощным и удобным средством от MS
1 предполагается, что раз вы пишете под .NET Framework, то он предполагается установленным у клиента. А вот Office, Crystal Reports, SQL Server, Visual Studio — не обязательно
ссылка на оригинал статьи http://habrahabr.ru/post/173923/
Добавить комментарий