diff --git a/RevitGen.Common/Attributes/CommandHandlerAttribute.cs b/RevitGen.Common/Attributes/CommandHandlerAttribute.cs new file mode 100644 index 0000000..6a6e155 --- /dev/null +++ b/RevitGen.Common/Attributes/CommandHandlerAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace RevitGen.Attributes +{ + /// + /// 标记一个方法作为 RevitGen 命令的执行逻辑入口点。 + /// ★★ 这个方法必须是无参数的,并且返回 void。★★ + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class CommandHandlerAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/RevitGen.Common/Attributes/RevitCommandAttribute.cs b/RevitGen.Common/Attributes/RevitCommandAttribute.cs new file mode 100644 index 0000000..b5767da --- /dev/null +++ b/RevitGen.Common/Attributes/RevitCommandAttribute.cs @@ -0,0 +1,56 @@ +// RevitGen.Common/Attributes/RevitCommandAttribute.cs + +using System; + +using Autodesk.Revit.Attributes; + +namespace RevitGen.Attributes +{ + /// + /// 将一个类标记为Revit外部命令,并自动为其生成UI按钮和必要的接口实现。 + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class RevitCommandAttribute : Attribute + { + /// + /// 按钮上显示的文本。 + /// + public string Text { get; } + + /// + /// 按钮所在的Ribbon Tab的名称。 + /// + public string TabName { get; set; } = "RevitGen"; + + /// + /// 按钮所在的Ribbon Panel的名称。 + /// + public string PanelName { get; set; } = "Commands"; + /// + /// 图标 + /// + public string Icon { get; set; } = ""; + /// + /// 鼠标悬停在按钮上时显示的工具提示。 + /// + public string ToolTip { get; set; } = ""; + + /// + /// 命令的事务模式。生成器将根据此模式自动处理事务。 + /// + public TransactionMode TransactionMode { get; set; } = TransactionMode.Manual; + + /// + /// 构造函数 + /// + /// 按钮上显示的文本。 + public RevitCommandAttribute(string text) + { + if (string.IsNullOrWhiteSpace(text)) + { + throw new ArgumentNullException(nameof(text), "Command button text cannot be empty."); + } + Text = text; + } + } +} \ No newline at end of file diff --git a/RevitGen.Common/RevitGen.Common.csproj b/RevitGen.Common/RevitGen.Common.csproj new file mode 100644 index 0000000..a7f2d98 --- /dev/null +++ b/RevitGen.Common/RevitGen.Common.csproj @@ -0,0 +1,14 @@ + + + + + netstandard2.0 + + $(NoWarn);NU1701 + + + + + + + \ No newline at end of file diff --git a/RevitGen.Generator/RevitCommandGenerator.cs b/RevitGen.Generator/RevitCommandGenerator.cs new file mode 100644 index 0000000..b0509a9 --- /dev/null +++ b/RevitGen.Generator/RevitCommandGenerator.cs @@ -0,0 +1,102 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace RevitGen.Generator +{ + [Generator(LanguageNames.CSharp)] + public class RevitCommandGenerator : ISourceGenerator + { + // ★★ 1. 修正常量,使用 RevitCommandAttribute ★★ + private const string RevitCommandAttributeFullName = "RevitGen.Attributes.RevitCommandAttribute"; + private const string CommandHandlerAttributeFullName = "RevitGen.Attributes.CommandHandlerAttribute"; + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + } + + public void Execute(GeneratorExecutionContext context) + { + var log = new StringBuilder(); + log.AppendLine("// RevitGen Log:"); + log.AppendLine($"// Compilation assembly: {context.Compilation.AssemblyName}"); + + if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) + { + // ... [日志和返回逻辑不变] ... + return; + } + + log.AppendLine($"// Candidate classes found by SyntaxReceiver: {receiver.CandidateClasses.Count}"); + if (receiver.CandidateClasses.Count == 0) + { + // ... [日志和返回逻辑不变] ... + return; + } + + // ★★ 2. 确保我们查找的是正确的特性符号 ★★ + var attributeSymbol = context.Compilation.GetTypeByMetadataName(RevitCommandAttributeFullName); + if (attributeSymbol == null) + { + log.AppendLine($"// ERROR: Could not find attribute symbol: {RevitCommandAttributeFullName}"); + AddSource(context, "RevitGen_Debug_Log.g.cs", log.ToString()); + return; + } + log.AppendLine($"// Successfully found attribute symbol: {attributeSymbol.Name}"); + + var commandClasses = new List(); + foreach (var candidateClass in receiver.CandidateClasses) + { + log.AppendLine($"// -> Processing candidate: {candidateClass.Identifier.ValueText}"); + var model = context.Compilation.GetSemanticModel(candidateClass.SyntaxTree); + var classSymbol = model.GetDeclaredSymbol(candidateClass) as INamedTypeSymbol; + + if (classSymbol == null) + { + log.AppendLine($"// -> SKIPPED: Could not get class symbol."); + continue; + } + + // ★★ 3. 检查是否应用了正确的 [RevitCommand] 特性 ★★ + bool hasAttribute = classSymbol.GetAttributes().Any(ad => + ad.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) ?? false); + + if (hasAttribute) + { + log.AppendLine($"// -> SUCCESS: Found [RevitCommand] attribute. Adding to list."); + commandClasses.Add(classSymbol); + } + else + { + log.AppendLine($"// -> SKIPPED: Did not find [RevitCommand] attribute."); + } + } + + log.AppendLine($"// Total command classes to generate: {commandClasses.Count}"); + + if (commandClasses.Any()) + { + // ... [生成 partial 类和 App 类的逻辑不变] ... + foreach (var classSymbol in commandClasses) + { + var partialClassSource = SourceGenerationHelper.GenerateCommandPartialClass(classSymbol); + AddSource(context, $"{classSymbol.Name}.g.cs", partialClassSource); + } + + var appSource = SourceGenerationHelper.GenerateApplicationClass(commandClasses); + AddSource(context, "RevitGenApplication.g.cs", appSource); + } + + AddSource(context, "RevitGen_Debug_Log.g.cs", log.ToString()); + } + + private void AddSource(GeneratorExecutionContext context, string hintName, string source) + { + context.AddSource(hintName, SourceText.From(source, Encoding.UTF8)); + } + } +} \ No newline at end of file diff --git a/RevitGen.Generator/RevitGen.Generator.csproj b/RevitGen.Generator/RevitGen.Generator.csproj new file mode 100644 index 0000000..bbf9f67 --- /dev/null +++ b/RevitGen.Generator/RevitGen.Generator.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + true + + + + + + + + + + + + + diff --git a/RevitGen.Generator/SourceGenerationHelper.cs b/RevitGen.Generator/SourceGenerationHelper.cs new file mode 100644 index 0000000..96e1fb3 --- /dev/null +++ b/RevitGen.Generator/SourceGenerationHelper.cs @@ -0,0 +1,279 @@ +using Microsoft.CodeAnalysis; + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace RevitGen.Generator +{ + /// + /// 一个静态辅助类,包含了所有用于生成C#源代码的逻辑。 + /// + internal static class SourceGenerationHelper + { + // 定义我们属性的完整名称,以使用最终确定的、正确的命名方案。 + private const string RevitCommandAttributeFullName = "RevitGen.Attributes.RevitCommandAttribute"; + private const string CommandHandlerAttributeFullName = "RevitGen.Attributes.CommandHandlerAttribute"; + + /// + /// 为一个具体的命令生成其 partial class 的【另一半】。 + /// 这一半会实现 IExternalCommand 并提供所有上下文属性。 + /// + public static string GenerateCommandPartialClass(INamedTypeSymbol classSymbol) + { + var ns = classSymbol.ContainingNamespace.ToDisplayString(); + var className = classSymbol.Name; + + var commandHandlerMethod = classSymbol.GetMembers() + .OfType() + .FirstOrDefault(m => m.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == CommandHandlerAttributeFullName)); + + if (commandHandlerMethod == null || commandHandlerMethod.Parameters.Any() || !commandHandlerMethod.ReturnsVoid) + { + return $"// 错误({className}): 必须有一个被[CommandHandler]标记的、无参数且返回void的方法。"; + } + + var attributeData = classSymbol.GetAttributes().First(ad => ad.AttributeClass?.ToDisplayString() == RevitCommandAttributeFullName); + var transactionMode = GetAttributeProperty(attributeData, "TransactionMode", 1); + + var source = new StringBuilder(); + source.AppendLine("// "); + source.AppendLine($"namespace {ns}"); + source.AppendLine("{"); + source.AppendLine(" using Autodesk.Revit.DB;"); + source.AppendLine(" using Autodesk.Revit.UI;"); + source.AppendLine(" using Autodesk.Revit.Attributes;"); + source.AppendLine(" using System.ComponentModel;"); + source.AppendLine(); + + source.AppendLine($" [Transaction((TransactionMode){transactionMode})]"); + source.AppendLine($" public partial class {className} : IExternalCommand"); + source.AppendLine(" {"); + source.AppendLine(" private ExternalCommandData _commandData;"); + source.AppendLine(" public UIApplication UIApplication => _commandData.Application;"); + source.AppendLine(" public UIDocument UIDocument => UIApplication.ActiveUIDocument;"); + source.AppendLine(" public Document Document => UIDocument.Document;"); + source.AppendLine(" public View ActiveView => UIDocument.ActiveView;"); + source.AppendLine(" public Result Result { get; set; } = Result.Succeeded;"); + source.AppendLine(" public string ErrorMessage { get; set; } = string.Empty;"); + source.AppendLine(" public ElementSet ElementSet { get; private set; }"); + source.AppendLine(); + + source.AppendLine(" public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)"); + source.AppendLine(" {"); + source.AppendLine(" this._commandData = commandData;"); + source.AppendLine(" this.ElementSet = elements;"); + source.AppendLine(); + source.AppendLine(" try"); + source.AppendLine(" {"); + if (transactionMode == 1) + { + source.AppendLine($" using (var trans = new Transaction(this.Document, \"{className}\"))"); + source.AppendLine(" {"); + source.AppendLine(" trans.Start();"); + source.AppendLine($" this.{commandHandlerMethod.Name}();"); + source.AppendLine(" if (this.Result == Result.Succeeded) trans.Commit(); else trans.RollBack();"); + source.AppendLine(" }"); + } + else + { + source.AppendLine($" this.{commandHandlerMethod.Name}();"); + } + source.AppendLine(" }"); + source.AppendLine(" catch (System.Exception ex)"); + source.AppendLine(" {"); + source.AppendLine(" this.ErrorMessage = ex.ToString();"); + source.AppendLine(" this.Result = Result.Failed;"); + source.AppendLine(" }"); + source.AppendLine(); + source.AppendLine(" message = this.ErrorMessage;"); + source.AppendLine(" return this.Result;"); + source.AppendLine(" }"); + source.AppendLine(" }"); + source.AppendLine("}"); + return source.ToString(); + } + + /// + /// 生成唯一的 IExternalApplication 类,用于创建Revit界面 (选项卡, 面板, 按钮)。 + /// + public static string GenerateApplicationClass(IEnumerable commandClasses) + { + var source = new StringBuilder(); + source.AppendLine("// "); + source.AppendLine("using Autodesk.Revit.UI;"); + source.AppendLine("using System;"); + source.AppendLine("using System.Reflection;"); + source.AppendLine("using System.Windows.Media;"); + source.AppendLine("using System.Windows.Media.Imaging;"); + source.AppendLine("using System.IO;"); + source.AppendLine("using System.Drawing;"); + source.AppendLine("using System.Resources;"); + + source.AppendLine(); + source.AppendLine("namespace RevitGen.Runtime"); + source.AppendLine("{"); + source.AppendLine(" public class RevitGenApplication : IExternalApplication"); + source.AppendLine(" {"); + source.AppendLine(" private static ResourceManager _resourceManager;"); + + source.AppendLine(" private BitmapSource BitmapToImageSource(Bitmap bitmap)"); + source.AppendLine(" {"); + source.AppendLine(" if (bitmap == null) return null;"); + source.AppendLine(" using (MemoryStream stream = new MemoryStream())"); + source.AppendLine(" {"); + source.AppendLine(" bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);"); + source.AppendLine(" stream.Position = 0;"); + source.AppendLine(" BitmapImage result = new BitmapImage();"); + source.AppendLine(" result.BeginInit();"); + source.AppendLine(" result.CacheOption = BitmapCacheOption.OnLoad;"); + source.AppendLine(" result.StreamSource = stream;"); + source.AppendLine(" result.EndInit();"); + source.AppendLine(" result.Freeze();"); + source.AppendLine(" return result;"); + source.AppendLine(" }"); + source.AppendLine(" }"); + source.AppendLine(); + + source.AppendLine(" private BitmapSource LoadImageFromEmbeddedResource(Assembly assembly, string resourcePath)"); + source.AppendLine(" {"); + source.AppendLine(" try"); + source.AppendLine(" {"); + source.AppendLine(" string assemblyName = assembly.GetName().Name;"); + source.AppendLine(" string resourceName = $\"{assemblyName}.{resourcePath.Replace('/', '.').Replace('\\\\', '.')}\";"); + source.AppendLine(" using (Stream stream = assembly.GetManifestResourceStream(resourceName))"); + source.AppendLine(" {"); + source.AppendLine(" if (stream == null) return null;"); + source.AppendLine(" var image = new BitmapImage();"); + source.AppendLine(" image.BeginInit();"); + source.AppendLine(" image.StreamSource = stream;"); + source.AppendLine(" image.CacheOption = BitmapCacheOption.OnLoad;"); + source.AppendLine(" image.EndInit();"); + source.AppendLine(" image.Freeze();"); + source.AppendLine(" return image;"); + source.AppendLine(" }"); + source.AppendLine(" }"); + source.AppendLine(" catch { return null; }"); + source.AppendLine(" }"); + source.AppendLine(); + + source.AppendLine(" public Result OnStartup(UIControlledApplication app)"); + source.AppendLine(" {"); + source.AppendLine(" var assembly = Assembly.GetExecutingAssembly();"); + source.AppendLine(" string assemblyPath = assembly.Location;"); + + source.AppendLine(" if (_resourceManager == null)"); + source.AppendLine(" {"); + source.AppendLine(" string resourceName = assembly.GetName().Name + \".Properties.Resources\";"); + source.AppendLine(" try { _resourceManager = new ResourceManager(resourceName, assembly); } catch { /* ResX 不存在时忽略 */ }"); + source.AppendLine(" }"); + + source.AppendLine(); + + var commandsWithData = commandClasses.Select(c => new { + Symbol = c, + Attribute = c.GetAttributes().First(ad => ad.AttributeClass?.ToDisplayString() == RevitCommandAttributeFullName) + }).ToList(); + + var groupedByTab = commandsWithData.GroupBy(data => GetAttributeProperty(data.Attribute, "TabName", null)); + + foreach (var tabGroup in groupedByTab) + { + var tabName = tabGroup.Key; + + if (!string.IsNullOrEmpty(tabName)) + { + source.AppendLine($" try {{ app.CreateRibbonTab(\"{tabName}\"); }} catch {{ /* 选项卡已存在 */ }}"); + } + + var groupedByPanel = tabGroup.GroupBy(data => GetAttributeProperty(data.Attribute, "PanelName", "Commands")); + foreach (var panelGroup in groupedByPanel) + { + var panelName = panelGroup.Key; + var panelVar = $"panel_{SanitizeIdentifier(tabName ?? "AddIns")}_{SanitizeIdentifier(panelName)}"; + + if (!string.IsNullOrEmpty(tabName)) + { + source.AppendLine($" RibbonPanel {panelVar} = app.CreateRibbonPanel(\"{tabName}\", \"{panelName}\");"); + } + else + { + source.AppendLine($" RibbonPanel {panelVar} = app.CreateRibbonPanel(\"{panelName}\");"); + } + + foreach (var commandData in panelGroup) + { + var commandSymbol = commandData.Symbol; + var attr = commandData.Attribute; + var buttonText = (string)attr.ConstructorArguments.First().Value; + var fullClassName = commandSymbol.ToDisplayString(); + var iconName = GetAttributeProperty(attr, "Icon", string.Empty); + var tooltip = GetAttributeProperty(attr, "ToolTip", string.Empty); + + source.AppendLine($" var pbd_{commandSymbol.Name} = new PushButtonData(\"cmd_{commandSymbol.Name}\", \"{buttonText}\", assemblyPath, \"{fullClassName}\");"); + + if (!string.IsNullOrEmpty(tooltip)) + { + source.AppendLine($" pbd_{commandSymbol.Name}.ToolTip = \"{tooltip}\";"); + } + + if (!string.IsNullOrEmpty(iconName)) + { + // ★★ 核心修正:为每个按钮的图标处理逻辑创建一个独立的局部作用域 ★★ + source.AppendLine(" {"); + source.AppendLine($" string iconExtension = System.IO.Path.GetExtension(\"{iconName}\");"); + source.AppendLine(" if (!string.IsNullOrEmpty(iconExtension))"); + source.AppendLine(" {"); + source.AppendLine($" pbd_{commandSymbol.Name}.LargeImage = LoadImageFromEmbeddedResource(assembly, \"{iconName}\");"); + source.AppendLine(" }"); + source.AppendLine(" else"); + source.AppendLine(" {"); + source.AppendLine($" if (_resourceManager != null)"); + source.AppendLine($" {{"); + source.AppendLine($" try"); + source.AppendLine($" {{"); + source.AppendLine($" var iconBitmap = (Bitmap)_resourceManager.GetObject(\"{iconName}\");"); + source.AppendLine($" pbd_{commandSymbol.Name}.LargeImage = BitmapToImageSource(iconBitmap);"); + source.AppendLine($" }}"); + source.AppendLine($" catch {{ /* 资源未找到或加载失败 */ }}"); + source.AppendLine($" }}"); + source.AppendLine(" }"); + source.AppendLine(" }"); // ★★ 结束局部作用域 ★★ + } + + source.AppendLine($" {panelVar}.AddItem(pbd_{commandSymbol.Name});"); + } + } + } + + source.AppendLine(" return Result.Succeeded;"); + source.AppendLine(" }"); + source.AppendLine(" public Result OnShutdown(UIControlledApplication app) => Result.Succeeded;"); + source.AppendLine(" }"); + source.AppendLine("}"); + return source.ToString(); + } + + /// + /// 一个辅助方法,用于从特性的数据中安全地读取一个具名属性。 + /// + private static T GetAttributeProperty(AttributeData attributeData, string propertyName, T defaultValue) + { + var namedArgument = attributeData.NamedArguments.FirstOrDefault(kvp => kvp.Key == propertyName); + if (namedArgument.Value.Value == null) + { + return defaultValue; + } + return (T)namedArgument.Value.Value; + } + + /// + /// 一个辅助方法,用于清理字符串,使其可以用作C#变量名。 + /// + private static string SanitizeIdentifier(string name) + { + return Regex.Replace(name, @"[^\w]", "_"); + } + } +} \ No newline at end of file diff --git a/RevitGen.Generator/SyntaxReceiver.cs b/RevitGen.Generator/SyntaxReceiver.cs new file mode 100644 index 0000000..d5704e6 --- /dev/null +++ b/RevitGen.Generator/SyntaxReceiver.cs @@ -0,0 +1,40 @@ +// RevitGen.Generator/SyntaxReceiver.cs + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using System.Collections.Generic; + +namespace RevitGen.Generator +{ + /// + /// 在语法树中查找所有带有属性的、定义为 partial 的类,作为代码生成的候选对象。 + /// + internal class SyntaxReceiver : ISyntaxReceiver + { + public List CandidateClasses { get; } = new List(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + // 检查节点是否是一个类声明,并且它带有属性 + //if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax && + // classDeclarationSyntax.AttributeLists.Count > 0) + //{ + // // 检查类是否被声明为 partial + // foreach (var modifier in classDeclarationSyntax.Modifiers) + // { + // if (modifier.ValueText == "partial") + // { + // CandidateClasses.Add(classDeclarationSyntax); + // break; + // } + // } + //} + if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax && + classDeclarationSyntax.AttributeLists.Count > 0) + { + CandidateClasses.Add(classDeclarationSyntax); + } + } + } +} \ No newline at end of file diff --git a/RevitGen.sln b/RevitGen.sln new file mode 100644 index 0000000..684872f --- /dev/null +++ b/RevitGen.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitGen", "RevitGen\RevitGen.csproj", "{36A67B40-B574-495A-A8EA-ED45B19E2A79}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitGenTest", "RevitGenTest\RevitGenTest.csproj", "{6303F3AA-6ECC-4619-8260-6A87E2BCBD50}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitGen.Generator", "RevitGen.Generator\RevitGen.Generator.csproj", "{01D32114-2908-4AEF-A420-DB3D257A6FDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitGen.Common", "RevitGen.Common\RevitGen.Common.csproj", "{DFFA8B05-51AE-4A96-834E-726090A329AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36A67B40-B574-495A-A8EA-ED45B19E2A79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36A67B40-B574-495A-A8EA-ED45B19E2A79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36A67B40-B574-495A-A8EA-ED45B19E2A79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36A67B40-B574-495A-A8EA-ED45B19E2A79}.Release|Any CPU.Build.0 = Release|Any CPU + {6303F3AA-6ECC-4619-8260-6A87E2BCBD50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6303F3AA-6ECC-4619-8260-6A87E2BCBD50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6303F3AA-6ECC-4619-8260-6A87E2BCBD50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6303F3AA-6ECC-4619-8260-6A87E2BCBD50}.Release|Any CPU.Build.0 = Release|Any CPU + {01D32114-2908-4AEF-A420-DB3D257A6FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01D32114-2908-4AEF-A420-DB3D257A6FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01D32114-2908-4AEF-A420-DB3D257A6FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01D32114-2908-4AEF-A420-DB3D257A6FDD}.Release|Any CPU.Build.0 = Release|Any CPU + {DFFA8B05-51AE-4A96-834E-726090A329AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFFA8B05-51AE-4A96-834E-726090A329AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFFA8B05-51AE-4A96-834E-726090A329AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFFA8B05-51AE-4A96-834E-726090A329AD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {655FFF69-A9C7-470E-9D97-47894C4DC93D} + EndGlobalSection +EndGlobal diff --git a/RevitGen/RevitGen.csproj b/RevitGen/RevitGen.csproj new file mode 100644 index 0000000..2f2f557 --- /dev/null +++ b/RevitGen/RevitGen.csproj @@ -0,0 +1,35 @@ + + + + + netstandard2.0 + RevitGen + 1.0.0 + ShrlAlgo + A powerful source generator to accelerate Revit add-in development. + Revit;SourceGenerator;CodeGen + + + false + true + + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RevitGenTest/Properties/Resources.Designer.cs b/RevitGenTest/Properties/Resources.Designer.cs new file mode 100644 index 0000000..432a9e4 --- /dev/null +++ b/RevitGenTest/Properties/Resources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace RevitGenTest.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevitGenTest.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap CodeList_16px { + get { + object obj = ResourceManager.GetObject("CodeList_16px", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap CodeList_32px { + get { + object obj = ResourceManager.GetObject("CodeList_32px", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/RevitGenTest/Properties/Resources.resx b/RevitGenTest/Properties/Resources.resx new file mode 100644 index 0000000..918b2ce --- /dev/null +++ b/RevitGenTest/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\CodeList_16px.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CodeList_32px.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/RevitGenTest/Resources/CodeList_16px.png b/RevitGenTest/Resources/CodeList_16px.png new file mode 100644 index 0000000..ced1916 Binary files /dev/null and b/RevitGenTest/Resources/CodeList_16px.png differ diff --git a/RevitGenTest/Resources/CodeList_32px.png b/RevitGenTest/Resources/CodeList_32px.png new file mode 100644 index 0000000..64a11be Binary files /dev/null and b/RevitGenTest/Resources/CodeList_32px.png differ diff --git a/RevitGenTest/RevitAddin.cs b/RevitGenTest/RevitAddin.cs new file mode 100644 index 0000000..8f8c5e2 --- /dev/null +++ b/RevitGenTest/RevitAddin.cs @@ -0,0 +1,59 @@ +using System; +using System.Drawing; + +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using Autodesk.Revit.UI; +using Autodesk.Revit.UI.Selection; + +using RevitGen.Attributes; + +using RevitGenTest.Properties; + +namespace RevitGenTest +{ + [RevitCommand("我的第一个命令", ToolTip = "这是一个自动生成的酷炫命令!", PanelName = "核心功能", Icon = nameof(Resources.CodeList_32px))] + public partial class RevitAddin + { + [CommandHandler] + private void Run() + { + var walls = new FilteredElementCollector(Document) + .OfCategory(BuiltInCategory.OST_Walls) + .WhereElementIsNotElementType() + .ToElements(); + + if (walls.Count == 0) + { + // 如果出现问题,只需设置属性即可 + this.ErrorMessage = "项目中没有找到任何墙。"; + this.Result = Result.Failed; + return; // 提前返回 + } + + TaskDialog.Show("成功", $"找到了 {walls.Count} 面墙。"); + } + } + [RevitCommand("我的第一个命令", ToolTip = "这是一个自动生成的酷炫命令!", PanelName = "核心功能", Icon = "Resources/CodeList_32px.png")] + public partial class RevitAddinX + { + [CommandHandler] + private void Run() + { + var walls = new FilteredElementCollector(Document) + .OfCategory(BuiltInCategory.OST_Walls) + .WhereElementIsNotElementType() + .ToElements(); + + if (walls.Count == 0) + { + // 如果出现问题,只需设置属性即可 + this.ErrorMessage = "项目中没有找到任何墙。"; + this.Result = Result.Failed; + return; // 提前返回 + } + + TaskDialog.Show("成功", $"找到了 {walls.Count} 面墙。"); + } + } +} \ No newline at end of file diff --git a/RevitGenTest/RevitGenTest.csproj b/RevitGenTest/RevitGenTest.csproj new file mode 100644 index 0000000..3ffd3ca --- /dev/null +++ b/RevitGenTest/RevitGenTest.csproj @@ -0,0 +1,33 @@ + + + net472 + Library + True + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..7c3cd6f --- /dev/null +++ b/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file