应用程序名称:ScheduleToHTML

Revit平台:所有

Revit版本:2014.0

首次发布版本:2014.0

编程语言:C#

技能级别:中级

类别:视图

类型:外部应用程序

主题:Schedules

摘要:将时间表导出为格式化的HTML

相关类:

Autodesk.Revit.DB.ViewSchedule

Autodesk.Revit.DB.TableData

Autodesk.Revit.DB.TableSectionData

Autodesk.Revit.DB.TableCellStyle

Autodesk.Revit.DB.TableMergedCell

项目文件:

Application.cs - 实现了 IExternalApplication 接口。

ScheduleHTMLExportCommand.cs - 导出活动进度表为 HTML 的外部命令。

ScheduleHTMLExporter.cs - 一个可以将进度表导出为 HTML 的类。

描述:

本示例将当前活动的进度表导出到一个 HTML 文件中。该文件将被创建在“TEMP”目录中,并使用默认的 Web 浏览器进行展示。

• 输出支持头部和主体部分。

• 输出包括格式化 - 背景颜色、加粗、斜体、下划线、水平对齐。

• 输出支持从进度表中获取的合并单元格。

说明:

打开一个 Revit 项目,打开进度表视图并执行添加到添加功能选项卡中的命令。

源代码:

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

Application.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable. 
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using Autodesk;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
namespace Revit.SDK.Samples.ScheduleToHTML.CS
{
    /// <summary>
    /// Implementation of the IExternalApplication.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    public class Application : IExternalApplication
    {
        #region IExternalApplication Members
        /// <summary>
        /// Implements the OnShutdown event
        /// </summary>
        /// <param name="application"></param>
        /// <returns></returns>
        public Result OnShutdown(UIControlledApplication application)
        {
            return Result.Succeeded;
        }
        /// <summary>
        /// Implements the OnStartup event
        /// </summary>
        /// <param name="application"></param>
        /// <returns></returns>
        public Result OnStartup(UIControlledApplication application)
        {
            CreateScheduleToHTMLPanel(application);
            return Result.Succeeded;
        }
        #endregion
        
        /// <summary>
        /// Sets up the add-in panel for this sample.
        /// </summary>
        private void CreateScheduleToHTMLPanel(UIControlledApplication application)
        {
            RibbonPanel rp = application.CreateRibbonPanel("Schedule To HTML");
            PushButtonData pbd = new PushButtonData("ScheduleToHTML", "Export to HTML",
                    addAssemblyPath,
                    typeof(Revit.SDK.Samples.ScheduleToHTML.CS.ScheduleHTMLExportCommand).FullName);
            pbd.LongDescription = "Export the active schedule to HTML.";
            PushButton duplicateAllPB = rp.AddItem(pbd) as PushButton;
            SetIconsForPushButton(duplicateAllPB, Revit.SDK.Samples.ScheduleToHTML.CS.Properties.Resources.ScheduleExport);
        }
        /// <summary>
        /// Utility for adding icons to the button.
        /// </summary>
        /// <param name="button">The push button.</param>
        /// <param name="icon">The icon.</param>
        private static void SetIconsForPushButton(PushButton button, System.Drawing.Icon icon)
        {
            button.LargeImage = GetStdIcon(icon);
            button.Image = GetSmallIcon(icon);
        }
        /// <summary>
        /// Gets the standard sized icon as a BitmapSource.
        /// </summary>
        /// <param name="icon">The icon.</param>
        /// <returns>The BitmapSource.</returns>
        private static BitmapSource GetStdIcon(System.Drawing.Icon icon)
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
                icon.Handle,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }
        /// <summary>
        /// Gets the small sized icon as a BitmapSource.
        /// </summary>
        /// <param name="icon">The icon.</param>
        /// <returns>The BitmapSource.</returns>
        private static BitmapSource GetSmallIcon(System.Drawing.Icon icon)
        {
            System.Drawing.Icon smallIcon = new System.Drawing.Icon(icon, new System.Drawing.Size(16, 16));
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
                smallIcon.Handle,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }
        /// <summary>
        /// The path to this add-in assembly.
        /// </summary>
        static String addAssemblyPath = typeof(Revit.SDK.Samples.ScheduleToHTML.CS.Application).Assembly.Location;
    }
}

ScheduleHTMLExportCommand.cs

using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace Revit.SDK.Samples.ScheduleToHTML.CS
{
    /// <summary>
    /// The external command exporting the active schedule to HTML.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    class ScheduleHTMLExportCommand : IExternalCommand
    {
        #region IExternalCommand Members
        /// <summary>
        /// The implementation of the command.
        /// </summary>
        /// <param name="commandData"></param>
        /// <param name="message"></param>
        /// <param name="elements"></param>
        /// <returns></returns>
        public Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {
            Document doc = commandData.Application.ActiveUIDocument.Document;
            View activeView = commandData.View;
            if (activeView is ViewSchedule)
            {
                ScheduleHTMLExporter exporter = new ScheduleHTMLExporter(activeView as ViewSchedule);
                Autodesk.Revit.ApplicationServices.Application revitApplication = commandData.Application.Application;
                bool bInteractive = revitApplication.IsJournalPlaying() ? false : true;
                return exporter.ExportToHTML(bInteractive, ref message) ? Result.Succeeded : Result.Cancelled;
            }
            
            message = "Unable to proceed: Active view must be a schedule.";
            return Result.Cancelled;
        }
        #endregion
    }
}

ScheduleHTMLExporter.cs

using System;
using System.Collections.Generic;
using System.Web.UI;
using System.IO;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ScheduleToHTML.CS
{
/// <summary>
/// A class that can export a schedule to HTML.
/// </summary>
class ScheduleHTMLExporter
{
/// <summary>
/// Constructs a new instance of the schedule exporter operating on the input schedule.
/// </summary>
/// <param name="input">The schedule to be exported.</param>
public ScheduleHTMLExporter(ViewSchedule input)
{
theSchedule = input;
}

/// <summary>
/// Exports the schedule to formatted HTML.
/// </summary>
/// <param name="bInteractive">true if the export is being run interactively, false for journal playback.</param>
/// <param name="errMessage">String to contain message to display if the export fails.</param>
/// <returns>true if HTML exported without error, false otherswise.</returns>
public bool ExportToHTML(bool bInteractive, ref string errMessage)
{
// Setup file location in temp directory
string folder = Environment.GetEnvironmentVariable("TEMP");
string htmlFile = System.IO.Path.Combine(folder, ReplaceIllegalCharacters(theSchedule.Name) + ".html");

// Initialize StringWriter instance, but handle any io exceptions and close as appropriate.
StreamWriter stringWriter = null;
try
{
stringWriter = new StreamWriter(htmlFile);

// Put HtmlTextWriter in using block because it needs to call Dispose.
using (writer = new HtmlTextWriter(stringWriter))
{
writer.AddAttribute(HtmlTextWriterAttribute.Align, "center");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

//Write schedule header
WriteHeader();

//Write schedule body
WriteBody();

writer.RenderEndTag();
}
}
catch(System.IO.IOException e)
{
// set error message and return failure, finally will close stringWriter if necessary.
errMessage = "Exception occured generating HTML: " + e.Message + " Command canceled.";
return false;
}
finally
{
if (stringWriter != null)
stringWriter.Close();
}

// Show the created file, but only if in interactive mode.
if (bInteractive)
System.Diagnostics.Process.Start(htmlFile);
return true;
}

/// <summary>
/// Writes the header section of the table to the HTML file.
/// </summary>
private void WriteHeader()
{
// Clear written cells
writtenCells.Clear();

// Start table to represent the header
writer.AddAttribute(HtmlTextWriterAttribute.Border, "1");
writer.RenderBeginTag(HtmlTextWriterTag.Table);

// Get header section and write each cell
headerSection = theSchedule.GetTableData().GetSectionData(SectionType.Header);
int numberOfRows = headerSection.NumberOfRows;
int numberOfColumns = headerSection.NumberOfColumns;

for (int iRow = headerSection.FirstRowNumber; iRow < numberOfRows; iRow++)
{
WriteHeaderSectionRow(iRow, numberOfColumns);
}

// Close header table
writer.RenderEndTag();
}

/// <summary>
/// Writes the body section of the table to the HTML file.
/// </summary>
private void WriteBody()
{
// Clear written cells
writtenCells.Clear();

// Write the start of the body table
writer.AddAttribute(HtmlTextWriterAttribute.Border, "1");
writer.RenderBeginTag(HtmlTextWriterTag.Table);

// Get body section and write contents
bodySection = theSchedule.GetTableData().GetSectionData(SectionType.Body);
int numberOfRows = bodySection.NumberOfRows;
int numberOfColumns = bodySection.NumberOfColumns;

for (int iRow = bodySection.FirstRowNumber; iRow < numberOfRows; iRow++)
{
WriteBodySectionRow(iRow, numberOfColumns);
}

// Close the table
writer.RenderEndTag();
}

/// <summary>
/// Gets the Color value formatted for HTML (#XXXXXX) output.
/// </summary>
/// <param name="color">he color.</param>
/// <returns>The color string.</returns>
private static String GetColorHtmlString(Color color)
{
return String.Format("#{0}{1}{2}", color.Red.ToString("X"), color.Green.ToString("X"), color.Blue.ToString("X"));
}

/// <summary>
/// A predefined color value used for comparison.
/// </summary>
private static Color Black
{
get
{
return new Color(0, 0, 0);
}
}

/// <summary>
/// A predefined color value used for comparison.
/// </summary>
private static Color White
{
get
{
return new Color(255, 255, 255);
}
}

/// <summary>
/// Compares two colors.
/// </summary>
/// <param name="color1">The first color.</param>
/// <param name="color2">The second color.</param>
/// <returns>True if the colors are equal, false otherwise.</returns>
private bool ColorsEqual(Color color1, Color color2)
{
return color1.Red == color2.Red && color1.Green == color2.Green && color1.Blue == color2.Blue;
}

/// <summary>
/// Gets the HTML string representing this horizontal alignment.
/// </summary>
/// <param name="style">The horizontal alignment.</param>
/// <returns>The related string.</returns>
private static String GetAlignString(HorizontalAlignmentStyle style)
{
switch (style)
{
case HorizontalAlignmentStyle.Left:
return "left";
case HorizontalAlignmentStyle.Center:
return "center";
case HorizontalAlignmentStyle.Right:
return "right";
}
return "";
}

/// <summary>
/// Writes a row of the header.
/// </summary>
/// <param name="iRow">The row number.</param>
/// <param name="numberOfColumns">The number of columns to write.</param>
private void WriteHeaderSectionRow(int iRow, int numberOfColumns)
{
WriteSectionRow(SectionType.Header, headerSection, iRow, numberOfColumns);
}

/// <summary>
/// Writes a row of the body.
/// </summary>
/// <param name="iRow">The row number.</param>
/// <param name="numberOfColumns">The number of columns to write.</param>
private void WriteBodySectionRow(int iRow, int numberOfColumns)
{
WriteSectionRow(SectionType.Body, bodySection, iRow, numberOfColumns);
}

/// <summary>
/// Writes a row of a table section.
/// </summary>
/// <param name="iRow">The row number.</param>
/// <param name="numberOfColumns">The number of columns to write.</param>
/// <param name="secType">The section type.</param>
/// <param name="data">The table section data.</param>
private void WriteSectionRow(SectionType secType, TableSectionData data, int iRow, int numberOfColumns)
{
// Start the table row tag.
writer.RenderBeginTag(HtmlTextWriterTag.Tr);

// Loop over the table section row.
for (int iCol = data.FirstColumnNumber; iCol < numberOfColumns; iCol++)
{
// Skip already written cells
if (writtenCells.Contains(new Tuple<int, int>(iRow, iCol)))
continue;

// Get style
TableCellStyle style = data.GetTableCellStyle(iRow, iCol);
int numberOfStyleTags = 1;

// Merged cells
TableMergedCell mergedCell = data.GetMergedCell(iRow, iCol);

// If merged cell spans multiple columns
if (mergedCell.Left != mergedCell.Right)
{
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, (mergedCell.Right - mergedCell.Left + 1).ToString());
}

// If merged cell spans multiple rows
if (mergedCell.Top != mergedCell.Bottom)
{
writer.AddAttribute(HtmlTextWriterAttribute.Rowspan, (mergedCell.Bottom - mergedCell.Top + 1).ToString());
}

// Remember all written cells related to the merge
for (int iMergedRow = mergedCell.Top; iMergedRow <= mergedCell.Bottom; iMergedRow++)
{
for (int iMergedCol = mergedCell.Left; iMergedCol <= mergedCell.Right; iMergedCol++)
{
writtenCells.Add(new Tuple<int, int>(iMergedRow, iMergedCol));
}
}

// Write formatting attributes for the upcoming cell
// Background color
if (!ColorsEqual(style.BackgroundColor, White))
{
writer.AddAttribute(HtmlTextWriterAttribute.Bgcolor, GetColorHtmlString(style.BackgroundColor));
}

// Horizontal alignment
writer.AddAttribute(HtmlTextWriterAttribute.Align, GetAlignString(style.FontHorizontalAlignment));

// Write cell tag
writer.RenderBeginTag(HtmlTextWriterTag.Td);

// Write subtags for the cell
// Underline
if (style.IsFontUnderline)
{
writer.RenderBeginTag(HtmlTextWriterTag.U);
numberOfStyleTags++;
}

//Italic
if (style.IsFontItalic)
{
writer.RenderBeginTag(HtmlTextWriterTag.I);
numberOfStyleTags++;
}

//Bold
if (style.IsFontBold)
{
writer.RenderBeginTag(HtmlTextWriterTag.B);
numberOfStyleTags++;
}

// Write cell text
String cellText = theSchedule.GetCellText(secType, iRow, iCol);
if (cellText.Length > 0)
{
writer.Write(cellText);
}
else
{
writer.Write("&nbsp;");
}

// Close open style tags & cell tag
for (int i = 0; i < numberOfStyleTags; i++)
{
writer.RenderEndTag();
}
}
// Close row tag
writer.RenderEndTag();
}

/// <summary>
/// An utility method to replace illegal characters of the Schedule name when creating the HTML file name.
/// </summary>
/// <param name="stringWithIllegalChar">The Schedule name.</param>
/// <returns>The updated string without illegal characters.</returns>
private static string ReplaceIllegalCharacters(string stringWithIllegalChar)
{
char[] illegalChars = System.IO.Path.GetInvalidFileNameChars();

string updated = stringWithIllegalChar;
foreach (char ch in illegalChars)
{
updated = updated.Replace(ch, '_');
}

return updated;
}

/// <summary>
/// The writer for the HTML file.
/// </summary>
private HtmlTextWriter writer;

/// <summary>
/// The schedule being exported.
/// </summary>
private ViewSchedule theSchedule;

/// <summary>
/// The body section of the table.
/// </summary>
private TableSectionData bodySection;

/// <summary>
/// The header section of the table.
/// </summary>
private TableSectionData headerSection;

/// <summary>
/// A collection of cells which have already been output. This is needed to deal with
/// cell merging - each cell should be written only once even as all the cells are iterated in
/// order.
/// </summary>
List<Tuple<int, int>> writtenCells = new List<Tuple<int, int>>();
}
}