修改命名空间

This commit is contained in:
GG Z
2026-02-21 16:31:24 +08:00
parent 97c0b18dc7
commit 2ad3d0fde0
188 changed files with 783 additions and 2710 deletions

View File

@@ -0,0 +1,67 @@
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public class ColorAssist
{
/// <summary>
/// 获取与 ElementId 唯一对应的鲜艳颜色
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Color GetDistinctColorById(ElementId id)
{
int hash = id.GetHashCode();
// 修正点:强制转换为 (int),利用 unchecked 溢出机制
// 2654435761 在 int 中就是 -1640531527
// unchecked 关键字确保在某些开启了溢出检查的项目配置中不会报错
unchecked
{
hash *= (int)2654435761;
}
// 2. 将哈希值映射到 0.0 - 1.0 的色相 (Hue)
// 使用黄金分割比 0.6180339887... 累加可以让颜色在色环上分布最均匀
double goldenRatioConjugate = 0.618033988749895;
double h = (hash * goldenRatioConjugate) % 1.0;
// 保证 h 为正数
if (h < 0) h += 1.0;
// 3. 固定饱和度 (S) 和 亮度 (V) 为较高值,保证颜色鲜艳
double s = 0.7; // 饱和度 0.0-1.0
double v = 0.9; // 亮度 0.0-1.0
// 4. HSV 转 Revit Color (RGB)
return HsvToRgb(h, s, v);
}
/// <summary>
/// 辅助函数HSV 转 Revit Color
/// </summary>
/// <param name="h"></param>
/// <param name="s"></param>
/// <param name="v"></param>
/// <returns></returns>
private static Color HsvToRgb(double h, double s, double v)
{
int hi = (int)(h * 6);
double f = h * 6 - hi;
byte p = (byte)(v * (1 - s) * 255);
byte q = (byte)(v * (1 - f * s) * 255);
byte t = (byte)(v * (1 - (1 - f) * s) * 255);
byte vByte = (byte)(v * 255);
return (hi % 6) switch
{
0 => new Color(vByte, t, p),
1 => new Color(q, vByte, p),
2 => new Color(p, vByte, t),
3 => new Color(p, q, vByte),
4 => new Color(t, p, vByte),
_ => new Color(vByte, p, q),
};
}
}
}

View File

@@ -0,0 +1,26 @@
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// 载入族,覆盖,只会覆盖类型参数值
/// </summary>
public class DefaultFamilyLoadOptions: IFamilyLoadOptions
{
//族已载入
public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
{
overwriteParameterValues = true;
return true;
}
//存在共享族
public bool OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
{
source = FamilySource.Family;
overwriteParameterValues = true;
return true;
}
}
}

View File

@@ -0,0 +1,33 @@
using System.ComponentModel;
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 复制粘贴选项时处理重复类型名称的处理程序。
/// </summary>
/// <example>
///<c>
///var options = new CopyPasteOptions();
///options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler());
///options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(() => DuplicateTypeAction.Abort));
///options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(DuplicateTypeAction.UseDestinationTypes));
///ElementTransformUtils.CopyElements(source, elementIds, destination, null, options);</c>
/// </example>
public class DuplicateTypeNamesHandler : IDuplicateTypeNamesHandler
{
public DuplicateTypeNamesHandler() => duplicateTypeAction = DuplicateTypeAction.UseDestinationTypes;
public DuplicateTypeNamesHandler(DuplicateTypeAction action) => duplicateTypeAction = action;
public DuplicateTypeNamesHandler(Func<DuplicateTypeAction> actionHandler) => duplicateTypeAction = actionHandler();
private readonly DuplicateTypeAction duplicateTypeAction;
/// <summary>
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public DuplicateTypeAction OnDuplicateTypeNamesFound(DuplicateTypeNamesHandlerArgs args) => duplicateTypeAction;
}

View File

@@ -0,0 +1,37 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// <c>
/// dwg元素选择过滤ObjectType.PointOnElement默认选择块选择曲线使用 ElementReferenceType.REFERENCE_TYPE_LINEAR
/// var dwgElemSelection = new DwgElementSelection(ElementReferenceType.REFERENCE_TYPE_LINEAR);
/// var refer = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.PointOnElement, dwgElemSelection);
/// Element elem = Document.GetElement(refer);
/// GeometryElement geoElem = elem.get_Geometry(new Options());
/// GeometryObject geoObj = elem.GetGeometryObjectFromReference(refer);
/// 图层:(Document.GetElement(geoObj.GraphicsStyleId) as GraphicsStyle).GraphicsStyleCategory.Name
/// 元素类型geoObj?.GetType().Name
/// </c>
/// </summary>
public class DwgElementSelection : ISelectionFilter
{
private readonly ElementReferenceType elementReferenceType;
private Element e;
public GeometryObject SelectedElement { get; private set; }
public DwgElementSelection(ElementReferenceType elementReferenceType = ElementReferenceType.REFERENCE_TYPE_INSTANCE)
{ this.elementReferenceType = elementReferenceType; }
public bool AllowElement(Element elem)
{
e = elem;
return e.Document.GetElement(e.GetTypeId()) is CADLinkType;
}
public bool AllowReference(Reference reference, XYZ position)
{
return reference.ElementReferenceType == elementReferenceType &&
e.GetGeometryObjectFromReference(reference).GraphicsStyleId != ElementId.InvalidElementId;
}
}

View File

@@ -0,0 +1,47 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 获取当前模型或链接模型的实体元素
/// </summary>
public class ElementInLinkOrCurrentDocument : ISelectionFilter
{
public ElementInLinkOrCurrentDocument(Document doc)
{
this.doc = doc;
}
private readonly Document doc;
public bool LastCheckedWasFromLink => null != LinkedDocument;
public Document LinkedDocument { get; private set; }
public bool AllowElement(Element e)
{
return true;
}
public bool AllowReference(Reference r, XYZ p)
{
LinkedDocument = null;
var e = doc.GetElement(r);
if (e is RevitLinkInstance li)
{
LinkedDocument = li.GetLinkDocument();
e = LinkedDocument.GetElement(r.LinkedElementId);
}
return e != null
&& e.CanHaveTypeAssigned()
&& e.HasPhases()
&& e.get_BoundingBox(null) != null
&& e.Category is { Parent: null }
&& e is not Panel;
}
}

View File

@@ -0,0 +1,15 @@
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public enum ElementOrientation
{
Horizontal,
Vertical,
CloseToHorizontal,
CloseToVertical,
Undefined
}
}

View File

@@ -0,0 +1,13 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 族文档可用
/// </summary>
internal class EnableInFamilyDocument : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) =>
applicationData.ActiveUIDocument?.Document.IsFamilyDocument == true;
}

View File

@@ -0,0 +1,13 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 三维视图可用
/// </summary>
public class EnableInView3D : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) =>
applicationData.ActiveUIDocument?.Document.ActiveView is View3D && applicationData.ActiveUIDocument?.Document.IsFamilyDocument == false;
}

View File

@@ -0,0 +1,16 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 命令在平面视图可用
/// </summary>
public class EnableInViewPlan : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
return applicationData.ActiveUIDocument?.Document.ActiveView is ViewPlan
&& applicationData.ActiveUIDocument?.Document.IsFamilyDocument == false;
}
}

View File

@@ -0,0 +1,13 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 命令在剖面、立面可用
/// </summary>
internal class EnableInViewSection : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) =>
applicationData.ActiveUIDocument?.Document.IsFamilyDocument == false && applicationData.ActiveUIDocument.Document.ActiveView is ViewSection;
}

View File

@@ -0,0 +1,13 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 图纸视图可用
/// </summary>
internal class EnableInViewSheet : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) =>
applicationData.ActiveUIDocument?.Document.ActiveView is ViewSheet && applicationData.ActiveUIDocument?.Document.IsFamilyDocument == false;
}

View File

@@ -0,0 +1,29 @@
using System.Reflection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class EncryptParameters
{
public static bool InvokeModule(string mName, object[] parameters, bool apiModule = true)
{
try
{
MethodInfo m;
if (apiModule)
m = NativeModule.GetApiModuleMethod(mName);
else
m = NativeModule.GetUiModuleMethod(mName);
m.Invoke(null, parameters);
return true;
}
catch (Exception)
{
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
namespace ShrlAlgoToolkit.RevitCore.Assists;
public enum ExtremeWallVariant
{
Left,
Right,
Top,
Bottom
}

View File

@@ -0,0 +1,53 @@
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// 错误预处理
/// </summary>
public class FailuresPreProcessor : IFailuresPreprocessor
{
////警告、错误处理,放在发生警告的前面
public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
{
//获取所有的失败信息
var listFma = failuresAccessor.GetFailureMessages();
if (listFma.Count == 0)
{
return FailureProcessingResult.Continue;
}
foreach (var fma in listFma)
{
if (fma?.GetFailureDefinitionId() == BuiltInFailures.JoinElementsFailures.CannotJoinElementsError)
{
failuresAccessor.ResolveFailure(fma);
}
if (fma.GetFailureDefinitionId() == BuiltInFailures.OverlapFailures.WallsOverlap)
{
failuresAccessor.DeleteWarning(fma);
}
//如果是错误,则尝试解决
if (fma.GetSeverity() == FailureSeverity.Error)
{
//模拟手动单击"删除连接"按钮
if (fma.HasResolutions())
{
failuresAccessor.ResolveFailure(fma);
}
return FailureProcessingResult.ProceedWithRollBack;
}
//如果是警告,则禁止弹框
if (fma.GetSeverity() == FailureSeverity.Warning)
{
failuresAccessor.DeleteWarning(fma);
}
}
return FailureProcessingResult.ProceedWithCommit;
}
}
}

View File

@@ -0,0 +1,19 @@
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public class FailuresPreprocessorHandler : IFailuresPreprocessor
{
public FailuresPreprocessorHandler(Func<FailuresAccessor, FailureProcessingResult> func)
{
this.func = func;
}
FailureProcessingResult IFailuresPreprocessor.PreprocessFailures(FailuresAccessor failuresAccessor)
{
return func(failuresAccessor);
}
private readonly Func<FailuresAccessor, FailureProcessingResult> func;
}
}

View File

@@ -0,0 +1,21 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 任意选择过滤器
/// </summary>
public class FuncFilter(Func<Element, bool> allowElement, Func<Reference, XYZ, bool> allowReference = null)
: ISelectionFilter
{
public bool AllowElement(Element elem)
{
return allowElement(elem);
}
public bool AllowReference(Reference reference, XYZ position)
{
return allowReference == null || allowReference(reference, position);
}
}

View File

@@ -0,0 +1,96 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class FunctionByGuidLine
{
public static Line GetGuideLine(Document doc, List<ElementId> eleIdsAdded)
{
var referLineId = eleIdsAdded.FirstOrDefault();
var guideLine = doc.GetElement(referLineId) as FamilyInstance;
var line = (guideLine?.Location as LocationCurve)?.Curve as Line;
doc.Delete(referLineId);
return line;
}
#region 线
private static readonly bool PlaceSingleInstanceAbort = true;
//只有一条线
private readonly List<ElementId> eleIdsRefer = new();
private IntPtr revitWindow;
public bool PlaceElementsByLine(UIApplication uiapp)
{
var uidoc = uiapp.ActiveUIDocument;
var app = uiapp.Application;
var doc = uidoc.Document;
#if REVIT2018
revitWindow = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
#else
revitWindow = uiapp.MainWindowHandle;
#endif
var f = new FilteredElementCollector(doc).OfClass(typeof(Family)).Cast<Family>().FirstOrDefault(x => x.Name == "LocationCurve");
var symbol = doc.GetElement(f.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
eleIdsRefer.Clear();
using var tg = new TransactionGroup(doc, "创建");
tg.Start();
try
{
//var x = UiDocument.GetPlacementTypes(symbol, doc.ActiveView);
//存在不能切换布置在平面上还是工作平面上的问题(如果族不需要切换,则没问题)
app.DocumentChanged += App_DocumentChanged;
uidoc.PromptForFamilyInstancePlacement(symbol);
app.DocumentChanged -= App_DocumentChanged;
//UiDocument.PostRequestForElementTypePlacement(symbol);
}
catch (OperationCanceledException)
{
var n = eleIdsRefer.Count;
//XYZ place = UiDocument.Selection.PickPoint("请点击布置的一侧");
if (n > 0)
{
using var ts = new Transaction(doc, "删除");
ts.Start();
var eid = eleIdsRefer.FirstOrDefault();
var l = uidoc.ActiveView.GenLevel;
if (doc.GetElement(eid) is FamilyInstance fi)
{
Wall.Create(doc, (fi.Location as LocationCurve)?.Curve, l.Id, false);
}
doc.Delete(eid);
//Debug.Print(ex.ViewMessage);
ts.Commit();
}
}
tg.Assimilate();
return true;
}
private void App_DocumentChanged(object sender, DocumentChangedEventArgs e)
{
var idsAdded = e.GetAddedElementIds();
var n = idsAdded.Count;
eleIdsRefer.AddRange(idsAdded);
if (PlaceSingleInstanceAbort && 0 < n)
{
//KeyIntPtrHelper.OneKey(_revit_window, (char)System.Windows.Forms.Keys.Escape);
KeyIntPtrHelper.OneKey(revitWindow, '\u001b');
//System.Windows.Forms.SendKeys.SendWait("^{F4}");
//KeyIntPtrHelper.SendKeys(Autodesk.Windows.ComponentManager.ApplicationWindow, System.Windows.Forms.Keys.Escape);
}
}
#endregion
}

View File

@@ -0,0 +1,21 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 类型过滤
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericFilter<T> : ISelectionFilter
{
public bool AllowElement(Element elem)
{
return elem is T;
}
public bool AllowReference(Reference reference, XYZ position)
{
return true;
}
}

View File

@@ -0,0 +1,272 @@
using System.Diagnostics;
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public class GeometryAssist
{
/// <summary>
/// 获取多个元素的合并包围盒
/// </summary>
/// <param name="elements">元素集合</param>
/// <returns>合并后的 BoundingBoxXYZ如果集合为空或无有效包围盒则返回 null</returns>
public static BoundingBoxXYZ GetCombinedBoundingBox(IEnumerable<Element> elements)
{
if (elements == null) return null;
double minX = double.MaxValue;
double minY = double.MaxValue;
double minZ = double.MaxValue;
double maxX = double.MinValue;
double maxY = double.MinValue;
double maxZ = double.MinValue;
bool hasValidBox = false;
foreach (var elem in elements)
{
// 传入 null 表示获取模型坐标系下的包围盒
BoundingBoxXYZ box = elem.get_BoundingBox(null);
// 注意:有些元素(如某些基准面或未放置的族实例)可能返回 null
if (box == null) continue;
// 比较并更新最小值
if (box.Min.X < minX) minX = box.Min.X;
if (box.Min.Y < minY) minY = box.Min.Y;
if (box.Min.Z < minZ) minZ = box.Min.Z;
// 比较并更新最大值
if (box.Max.X > maxX) maxX = box.Max.X;
if (box.Max.Y > maxY) maxY = box.Max.Y;
if (box.Max.Z > maxZ) maxZ = box.Max.Z;
hasValidBox = true;
}
if (!hasValidBox) return null;
// 构造新的包围盒
BoundingBoxXYZ combinedBox = new BoundingBoxXYZ();
combinedBox.Min = new XYZ(minX, minY, minZ);
combinedBox.Max = new XYZ(maxX, maxY, maxZ);
return combinedBox;
}
/// <summary>
/// 对曲线列表进行排序,使其正确排序和定向,形成线串。
/// </summary>
public static void SortCurvesContiguous(IList<Curve> curves, bool debug_output = false)
{
const double inch = 1.0 / 12.0;
const double sixteenth = inch / 16.0;
int n = curves.Count;
for (int i = 0; i < n; ++i)
{
Curve curve = curves[i];
XYZ endPoint = curve.GetEndPoint(1);
XYZ p;
// 查找起点 = 终点的曲线
bool found = (i + 1 >= n);
for (int j = i + 1; j < n; ++j)
{
p = curves[j].GetEndPoint(0);
// 如果匹配 end->start、
// 这是下一条曲线
if (sixteenth > p.DistanceTo(endPoint))
{
if (debug_output)
{
Debug.Print("{0} 起始点, 换成 {1}", j, i + 1);
}
if (i + 1 != j)
{
Curve tmp = curves[i + 1];
curves[i + 1] = curves[j];
curves[j] = tmp;
}
found = true;
break;
}
p = curves[j].GetEndPoint(1);
// 如果有匹配结果 end->end、
// 反转下一条曲线
if (sixteenth > p.DistanceTo(endPoint))
{
if (i + 1 == j)
{
if (debug_output)
{
Debug.Print("{0} 终点, 反向 {1}", j, i + 1);
}
curves[i + 1] = curves[j].CreateReversed();
}
else
{
if (debug_output)
{
Debug.Print("{0} 终点, 倒换 {1}", j, i + 1);
}
Curve tmp = curves[i + 1];
curves[i + 1] = curves[j].CreateReversed();
curves[j] = tmp;
}
found = true;
break;
}
}
if (!found)
{
throw new Exception("SortCurvesContiguous:" + " 非连续输入曲线");
}
}
}
public static void CreateDirectShape(
Document doc,
List<XYZ> vertices,
List<Face> faces,
ElementId graphicsStyleId,
ElementId categoryId,
string appGuid,
string shapeName
)
{
var nFaces = 0;
var builder = new TessellatedShapeBuilder { LogString = shapeName };
var corners = new List<XYZ>();
builder.OpenConnectedFaceSet(false);
foreach (var f in faces)
{
builder.LogInteger = nFaces;
//if (corners.Capacity < f.Indices.Count)//设置corner的Capacity值减少内存开销
//{
// corners = new ErrorModels<XYZ>(f.Indices.Count);
//}
//corners.Clear();
//foreach (Index i in f.Indices)
//{
// if (i.vertex >= vertices.Count)
// {
// return;
// }
// corners.Add(vertices[i.vertex]);
//}
try
{
builder.AddFace(new TessellatedFace(corners, ElementId.InvalidElementId));
}
catch (Autodesk.Revit.Exceptions.ArgumentException) { }
}
builder.CloseConnectedFaceSet();
builder.Target = TessellatedShapeBuilderTarget.AnyGeometry;
builder.Fallback = TessellatedShapeBuilderFallback.Mesh;
builder.GraphicsStyleId = graphicsStyleId;
builder.Build();
var ds = DirectShape.CreateElement(doc, categoryId);
ds.ApplicationId = appGuid;
ds.ApplicationDataId = shapeName;
ds.SetShape(builder.GetBuildResult().GetGeometricalObjects());
ds.Name = shapeName;
}
//public BRepBuilder CreateGeo()
//{
//BRepBuilder bRepBuilder=new BRepBuilder(BRepType.Solid);
//BRepBuilderEdgeGeometry.Create();
//BRepBuilderSurfaceGeometry.Create();
//BRepBuilderSurfaceGeometry.CreateNURBSSurface();
//}
public static void CreateDirectShapeFromCylinder(Document doc)
{
// Naming convention for faces and edges: we assume that x is to the left and pointing down, y is horizontal and pointing to the right, z is up
var brepBuilder = new BRepBuilder(BRepType.Solid);
// The surfaces of the four faces.
var basis = new Frame(new XYZ(50, -100, 0), new XYZ(0, 1, 0), new XYZ(-1, 0, 0), new XYZ(0, 0, 1));
var cylSurf = CylindricalSurface.Create(basis, 50);
var top = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 100)); // normal points outside the cylinder
var bottom = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 0)); // normal points inside the cylinder
// Add the four faces
var frontCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
var backCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
var topFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(top, null), false);
var bottomFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(bottom, null), true);
// Geometry for the four semi-circular edges and two vertical linear edges
var frontEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 0), new XYZ(100, -100, 0), new XYZ(50, -50, 0)));
var backEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(100, -100, 0), new XYZ(0, -100, 0), new XYZ(50, -150, 0)));
var frontEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -50, 100)));
var backEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -150, 100)));
var linearEdgeFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, -100, 0), new XYZ(100, -100, 100));
var linearEdgeBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, -100, 0), new XYZ(0, -100, 100));
// Add the six edges
var frontEdgeBottomId = brepBuilder.AddEdge(frontEdgeBottom);
var frontEdgeTopId = brepBuilder.AddEdge(frontEdgeTop);
var linearEdgeFrontId = brepBuilder.AddEdge(linearEdgeFront);
var linearEdgeBackId = brepBuilder.AddEdge(linearEdgeBack);
var backEdgeBottomId = brepBuilder.AddEdge(backEdgeBottom);
var backEdgeTopId = brepBuilder.AddEdge(backEdgeTop);
// Loops of the four faces
var loopIdTop = brepBuilder.AddLoop(topFaceId);
var loopIdBottom = brepBuilder.AddLoop(bottomFaceId);
var loopIdFront = brepBuilder.AddLoop(frontCylFaceId);
var loopIdBack = brepBuilder.AddLoop(backCylFaceId);
// Add coedges for the loop of the front face
brepBuilder.AddCoEdge(loopIdFront, linearEdgeBackId, false);
brepBuilder.AddCoEdge(loopIdFront, frontEdgeTopId, false);
brepBuilder.AddCoEdge(loopIdFront, linearEdgeFrontId, true);
brepBuilder.AddCoEdge(loopIdFront, frontEdgeBottomId, true);
brepBuilder.FinishLoop(loopIdFront);
brepBuilder.FinishFace(frontCylFaceId);
// Add coedges for the loop of the back face
brepBuilder.AddCoEdge(loopIdBack, linearEdgeBackId, true);
brepBuilder.AddCoEdge(loopIdBack, backEdgeBottomId, true);
brepBuilder.AddCoEdge(loopIdBack, linearEdgeFrontId, false);
brepBuilder.AddCoEdge(loopIdBack, backEdgeTopId, true);
brepBuilder.FinishLoop(loopIdBack);
brepBuilder.FinishFace(backCylFaceId);
// Add coedges for the loop of the top face
brepBuilder.AddCoEdge(loopIdTop, backEdgeTopId, false);
brepBuilder.AddCoEdge(loopIdTop, frontEdgeTopId, true);
brepBuilder.FinishLoop(loopIdTop);
brepBuilder.FinishFace(topFaceId);
// Add coedges for the loop of the bottom face
brepBuilder.AddCoEdge(loopIdBottom, frontEdgeBottomId, false);
brepBuilder.AddCoEdge(loopIdBottom, backEdgeBottomId, false);
brepBuilder.FinishLoop(loopIdBottom);
brepBuilder.FinishFace(bottomFaceId);
brepBuilder.Finish();
using var tr = new Transaction(doc, "Create a DirectShape");
tr.Start();
var ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
ds.SetShape(brepBuilder);
tr.Commit();
}
}
}

View File

@@ -0,0 +1,11 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 已经打开文档
/// </summary>
public class HasActiveDocument : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) => applicationData.ActiveUIDocument != null;
}

View File

@@ -0,0 +1,710 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using Autodesk.Windows;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class KeyIntPtrHelper
{
public delegate bool CallBack(IntPtr hwnd, int lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
private static void CompleteMultiSelection()
{
var window = ComponentManager.ApplicationWindow;
var list = new List<IntPtr>();
var flag = EnumChildWindows(
window,
(hwnd, l) =>
{
var windowText = new StringBuilder(200);
_ = GetWindowText(hwnd, windowText, windowText.Capacity);
var className = new StringBuilder(200);
_ = GetClassName(hwnd, className, className.Capacity);
if (
(
windowText.ToString().Equals("完成", StringComparison.Ordinal)
|| windowText.ToString().Equals("Finish", StringComparison.Ordinal)
) && className.ToString().Contains("Button")
)
{
list.Add(hwnd);
return false;
}
return true;
},
new IntPtr(0)
);
var complete = list.FirstOrDefault();
_ = PostMessage(complete, 245, 0, 0);
}
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
/// <summary>
/// 确定主窗口
/// </summary>
/// <param name="handles"></param>
/// <returns></returns>
private static IntPtr DetermineMainWindow(IReadOnlyList<IntPtr> handles)
{
IntPtr result;
if (handles == null || handles.Count <= 0)
{
result = IntPtr.Zero;
}
else
{
var intPtr = IntPtr.Zero;
if (handles.Count == 1)
{
intPtr = handles[0];
}
else
{
foreach (var intPtr2 in handles)
{
var windowTextLength = GetWindowTextLength(intPtr2);
if (windowTextLength == 0) continue;
var stringBuilder = new StringBuilder(windowTextLength);
_ = GetWindowText(intPtr2, stringBuilder, windowTextLength + 1);
if (stringBuilder.ToString().ToLower().Contains("autodesk revit"))
{
intPtr = intPtr2;
break;
}
}
}
result = intPtr;
}
return result;
}
[DllImport("user32.dll", CharSet = CharSet.None, ExactSpelling = false)]
private static extern bool EnumChildWindows(IntPtr hwndParent, CallBack lpEnumFunc, IntPtr lParam);
[DllImport("user32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("user32.dll", EntryPoint = "FindWindow", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern int GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetKeyNameText(int lParam, [Out] StringBuilder lpString, int nSize);
[DllImport("user32.dll")]
private static extern short GetKeyState(short nVirtKey);
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("user32.DLL")]
private static extern IntPtr GetShellWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, int revert);
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr_1(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
private static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);
[DllImport("user32.DLL")]
private static extern bool IsWindowVisible(IntPtr hWnd);
/// <summary>
/// 键盘事件
/// </summary>
/// <param name="bVk">虚拟键值查询虚拟键值表</param>
/// <param name="bScan">扫描不设置为0</param>
/// <param name="dwFlags">按键状态选项按下为0如果为松开 则设置成2KEYEVENT_KEYUP</param>
/// <param name="dwExtraInfo">,一般为0</param>
[DllImport("user32.dll", EntryPoint = "keybd_event")]
private static extern void keybdEvent(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
/// <summary>
/// 虚拟按钮
/// </summary>
/// <param name="uCode"></param>
/// <param name="uMapType"></param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
/// <summary>
///发送消息
/// </summary>
/// <param name="hWnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <example>
/// <para>Press.PostMessage(_revit_window, (uint)Press.KEYBOARD_MSG.WM_KEYDOWN, (uint)System.Windows.Forms.Keys.Escape, 0);</para>
/// <para>Press.PostMessage(_revit_window, (uint)Press.KEYBOARD_MSG.WM_KEYDOWN, (uint)System.Windows.Forms.Keys.Escape, 0);</para></example>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, uint msg, uint wParam, uint lParam);
[DllImport("user32.dll")]
private static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
/// <summary>
/// 显示窗口
/// </summary>
/// <param name="hWnd"></param>
/// <param name="nCmdShow">0隐藏窗口1恢复窗口大小2最小化窗口3最大化窗口</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern int ToUnicode(
int wVirtKey,
int wScanCode,
byte[] lpKeyState,
[MarshalAs(UnmanagedType.LPWStr)][Out] StringBuilder pwszBuff,
int cchBuff,
int wFlags
);
/// <summary>
/// 查找内容
/// </summary>
/// <param name="hwndParent"></param>
/// <param name="hwndChildAfter"></param>
/// <param name="lpszClass"></param>
/// <param name="lpszWindow"></param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow
);
public static IntPtr GetMainWindow(int pid)
{
var shellWindow = GetShellWindow();
var windowsForPid = new List<IntPtr>();
_ = EnumWindows(
(hWnd, _) =>
{
if (hWnd == shellWindow) { }
else
{
if (!IsWindowVisible(hWnd))
{
return true;
}
GetWindowThreadProcessId(hWnd, out var num);
if (num != (ulong)pid)
{
return true;
}
var parent = GetParent(hWnd);
if (parent == IntPtr.Zero)
{
windowsForPid.Add(hWnd);
}
}
return true;
},
0
);
return DetermineMainWindow(windowsForPid);
}
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
return IntPtr.Size == 8 ? GetWindowLongPtr_1(hWnd, nIndex) : new IntPtr(GetWindowLong(hWnd, nIndex));
}
public static void HideWindow(string winName)
{
var parenthWndldsl = FindWindow(null, winName);
if (!parenthWndldsl.Equals(IntPtr.Zero))
{
_ = ShowWindow(parenthWndldsl, 0);
}
}
public static bool IsVisible(string winName)
{
var parenthWndldsl = FindWindow(null, winName);
return !parenthWndldsl.Equals(IntPtr.Zero);
}
/// <summary>
/// 提交一系列按钮
/// </summary>
/// <param name="revitHandle"></param>
/// <param name="command">快捷键命令</param>
public static void Keys(IntPtr revitHandle, string command)
{
//IntPtr revitHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
foreach (var letter in command)
{
OneKey(revitHandle, letter);
}
}
/// <summary>
/// 提交单个按键
/// </summary>
/// <param name="handle">窗口句柄</param>
/// <param name="letter"></param>
public static void OneKey(IntPtr handle, char letter)
{
var scanCode = MapVirtualKey(letter, (uint)MvkMapType.VkeyToScancode);
var keyDownCode = (uint)WhKeyboardLparam.Keydown | (scanCode << 16);
var keyUpCode = (uint)WhKeyboardLparam.Keyup | (scanCode << 16);
PostMessage(handle, (uint)KeyboardMsg.WmKeydown, letter, keyDownCode);
PostMessage(handle, (uint)KeyboardMsg.WmKeyup, letter, keyUpCode);
}
/// <summary>
/// 执行Esc两次退出Revit选择
/// </summary>
public static void RaiseEscTwice()
{
SetForegroundWindow(ComponentManager.ApplicationWindow);
keybdEvent(27, 0, 0, 0);
keybdEvent(27, 0, 2, 0);
//keybdEvent(0x1B, 0, 0, 0);
//keybdEvent(0x1B, 0, 0, 0);
}
/// <summary>
/// 执行Esc两次退出Revit命令常用
/// </summary>
public static void PostEscTwice()
{
SetForegroundWindow(ComponentManager.ApplicationWindow);
PostMessage(ComponentManager.ApplicationWindow, (uint)KEYBOARD_MSG.WM_KEYDOWN, (uint)27, 0);
PostMessage(ComponentManager.ApplicationWindow, (uint)KEYBOARD_MSG.WM_KEYDOWN, (uint)27, 0);
//keybdEvent(0x1B, 0, 0, 0);
//keybdEvent(0x1B, 0, 0, 0);
}
public static bool RestoreWindow(IntPtr hWnd)
{
return ShowWindow(hWnd, 1);
}
public static void SendKeys(IntPtr proc, byte bVk)
{
SetActiveWindow(proc);
SetForegroundWindow(proc);
//keybd_event(27, 0, 0, 0);
//keybd_event(27, 0, 2, 0);
keybdEvent(bVk, 0, 0, 0);
keybdEvent(bVk, 0, 2, 0);
keybdEvent(bVk, 0, 0, 0);
keybdEvent(bVk, 0, 2, 0);
}
public static void SetAsOwner(IntPtr mainHandler, Window childWindow)
{
new WindowInteropHelper(childWindow).Owner = mainHandler;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetWindowText(IntPtr hWnd, string lpString);
public static void ShowAndActive(string winname)
{
var parenthWndldsl = FindWindow(null, winname);
if (!parenthWndldsl.Equals(IntPtr.Zero))
{
ShowWindow(parenthWndldsl, 1);
SetActiveWindow(parenthWndldsl);
}
}
private enum WH_KEYBOARD_LPARAM : uint
{
KEYDOWN = 0x00000001,
KEYUP = 0xC0000001
}
public enum KEYBOARD_MSG : uint
{
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101
}
private enum MVK_MAP_TYPE : uint
{
VKEY_TO_SCANCODE = 0,
SCANCODE_TO_VKEY = 1,
VKEY_TO_CHAR = 2,
SCANCODE_TO_LR_VKEY = 3
}
/// <summary>
/// 键盘消息
/// </summary>
public enum KeyboardMsg : uint
{
WmKeydown = 0x100,
WmKeyup = 0x101
}
private enum MvkMapType : uint
{
VkeyToScancode = 0,
ScancodeToVkey = 1,
VkeyToChar = 2,
ScancodeToLrVkey = 3
}
private enum WhKeyboardLparam : uint
{
Keydown = 0x00000001,
Keyup = 0xC0000001
}
#region Win32 API Hook
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private const int VbWH_MOUSE_LL = 14;
private const int VbWM_LBUTTONDOWN = 0x0201;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(HookProc proc)
{
using var curProcess = Process.GetCurrentProcess();
using var curModule = curProcess.MainModule;
return SetWindowsHookEx(VbWH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)VbWM_LBUTTONDOWN)
{
MessageBox.Show("Left button down");
CompleteMultiSelection();
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
public static void MouseLeftButtonAction(Action action)
{
_hookID = SetHook(MouseHookCallback);
action();
UnhookWindowsHookEx(_hookID);
}
#endregion
#region Keys
public const byte VbKey0 = 48;
public const byte VbKey1 = 49;
public const byte VbKey2 = 50;
public const byte VbKey3 = 51;
public const byte VbKey4 = 52;
public const byte VbKey5 = 53;
public const byte VbKey6 = 54;
public const byte VbKey7 = 55;
public const byte VbKey8 = 56;
public const byte VbKey9 = 57;
public const byte VbKeyA = 65;
public const byte VbKeyAdd = 107;
public const byte VbKeyAlt = 18;
public const byte VbKeyB = 66;
public const byte VbKeyBack = 8;
public const byte VbKeyC = 67;
public const byte VbKeyCancel = 3;
public const byte VbKeyCapital = 20;
public const byte VbKeyClear = 12;
public const byte VbKeyControl = 17;
public const byte VbKeyD = 68;
public const byte VbKeyDecimal = 110;
public const byte VbKeyDelete = 46;
public const byte VbKeyDivide = 111;
public const byte VbKeyDown = 40;
public const byte VbKeyE = 69;
public const byte VbKeyEnd = 35;
public const byte VbKeyEscape = 27;
public const byte VbKeyExecute = 43;
public const byte VbKeyF = 70;
public const byte VbKeyF1 = 112;
public const byte VbKeyF10 = 121;
public const byte VbKeyF11 = 122;
public const byte VbKeyF12 = 123;
public const byte VbKeyF2 = 113;
public const byte VbKeyF3 = 114;
public const byte VbKeyF4 = 115;
public const byte VbKeyF5 = 116;
public const byte VbKeyF6 = 117;
public const byte VbKeyF7 = 118;
public const byte VbKeyF8 = 119;
public const byte VbKeyF9 = 120;
public const byte VbKeyG = 71;
public const byte VbKeyH = 72;
public const byte VbKeyHelp = 47;
public const byte VbKeyHome = 36;
public const byte VbKeyI = 73;
public const byte VbKeyJ = 74;
public const byte VbKeyK = 75;
public const byte VbKeyL = 76;
public const byte VbKeyLButton = 1;
public const byte VbKeyLeft = 37;
public const byte VbKeyM = 77;
public const byte VbKeyMButton = 4;
public const byte VbKeyMenu = 18;
public const byte VbKeyMultiply = 106;
public const byte VbKeyN = 78;
public const byte VbKeyNumlock = 144;
public const byte VbKeyNumpad0 = 96;
public const byte VbKeyNumpad1 = 97;
public const byte VbKeyNumpad2 = 98;
public const byte VbKeyNumpad3 = 99;
public const byte VbKeyNumpad4 = 100;
public const byte VbKeyNumpad5 = 101;
public const byte VbKeyNumpad6 = 102;
public const byte VbKeyNumpad7 = 103;
public const byte VbKeyNumpad8 = 104;
public const byte VbKeyNumpad9 = 105;
public const byte VbKeyO = 79;
public const byte VbKeyP = 80;
public const byte VbKeyPageUp = 33;
public const byte VbKeyPause = 19;
public const byte VbKeyPrint = 42;
public const byte VbKeyQ = 81;
public const byte VbKeyR = 82;
public const byte VbKeyRButton = 2;
public const byte VbKeyReturn = 13;
public const byte VbKeyRight = 39;
public const byte VbKeyS = 83;
public const byte VbKeySelect = 41;
public const byte VbKeySeparator = 108;
public const byte VbKeyShift = 16;
public const byte VbKeySnapshot = 44;
public const byte VbKeySpace = 32;
public const byte VbKeySubtract = 109;
public const byte VbKeyT = 84;
public const byte VbKeyTab = 9;
public const byte VbKeyU = 85;
public const byte VbKeyUp = 38;
public const byte VbKeyV = 86;
public const byte VbKeyW = 87;
public const byte VbKeyX = 88;
public const byte VbKeyY = 89;
public const byte VbKeyZ = 90;
public const int WmChar = 258;
public const int WmClick = 245;
public const int WmClose = 16;
public const int WmDeadchar = 259;
public const int WmKeydown = 256;
public const int WmKeyup = 257;
public const int WmLbuttondblclk = 515;
public const int WmLbuttondown = 513;
public const int WmLbuttonup = 514;
public const int WmMbuttondblclk = 521;
public const int WmMbuttondown = 519;
public const int WmMbuttonup = 520;
public const int WmMousehwheel = 526;
public const int WmMousemove = 512;
public const int WmMousewheel = 522;
public const int WmRbuttondblclk = 518;
public const int WmRbuttonup = 517;
public const int WmSyschar = 262;
public const int WmSysdeadchar = 263;
public const int WmSyskeydown = 260;
public const int WmSyskeyup = 261;
public const int WmXbuttondblclk = 525;
public const int WmXbuttondown = 523;
public const int WmXbuttonup = 524;
#endregion
}

View File

@@ -0,0 +1,318 @@
using System.Reflection;
using Autodesk.Revit.UI;
using UIFramework;
using UIFrameworkServices;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// 通过命令id设置快捷键
/// </summary>
public class KeyboardShortcutService
{
/// <summary>
/// 设置菜单快捷键
/// </summary>
/// <param name="id"></param>
/// <param name="shortcutsRep"></param>
/// <returns></returns>
public bool SetRibbonItemShortcut(string id, string shortcutsRep)
{
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
if (this.Commands.TryGetValue(id, out UIFrameworkServices.ShortcutItem shortcutItem))
{
shortcutItem.ShortcutsRep = shortcutsRep;
shortcutItem.Shortcuts.Clear();
//多个快捷键中间以#分割
foreach (string text in shortcutsRep.Split('#'))
{
shortcutItem.Shortcuts.Add(text);
}
dictionary.Add(id, shortcutItem);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.SaveShortcuts(list);
this.ArmCommands();
在此之后:
SaveShortcuts(list);
this.ArmCommands();
*/
KeyboardShortcutService.SaveShortcuts(list);
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.ArmCommands();
return true;
在此之后:
ArmCommands();
return true;
*/
KeyboardShortcutService.ArmCommands();
return true;
}
return false;
}
/// <summary>
/// 通过外部命令类设置快捷键
/// </summary>
/// <param name="id"></param>
/// <param name="shortcutsRep"></param>
/// <returns></returns>
public bool SetRibbonItemShortcut<T>() where T : IExternalCommand
{
var attribute = typeof(T).GetCustomAttribute<ShortcutRepAttribute>();
if (attribute == null)
{
return false;
}
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
foreach (var item in Commands)
{
if (item.Key.EndsWith(nameof(T)))
{
ShortcutItem shortcutItem = item.Value;
shortcutItem.ShortcutsRep = attribute.Shortcuts;
shortcutItem.Shortcuts.Clear();
//多个快捷键中间以#分割
foreach (string text in attribute.Shortcuts.Split('#'))
{
shortcutItem.Shortcuts.Add(text);
}
dictionary.Add(item.Key, shortcutItem);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.SaveShortcuts(list);
this.ArmCommands();
在此之后:
SaveShortcuts(list);
this.ArmCommands();
*/
KeyboardShortcutService.SaveShortcuts(list);
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.ArmCommands();
return true;
在此之后:
ArmCommands();
return true;
*/
KeyboardShortcutService.ArmCommands();
return true;
}
}
return false;
}
#region 使
/// 基于Revit封装的RibbonButton设置其快捷键.
/// </summary>
/// <param name="btn">RibbonButton.</param>
/// <param name="key">快捷键字符串.</param>
/// <returns></returns>
public bool SetShortCut(Autodesk.Revit.UI.RibbonButton btn, string key)
{
if (btn == null)
return false;
var item = btn.GetType()
.InvokeMember(
"getRibbonItem",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
Type.DefaultBinder,
btn,
null
);
return item != null && (item is Autodesk.Windows.RibbonItem ribbonItem) && SetAdShortCut(ribbonItem, key);
}
/// <summary>
/// 基于通用库封装的RibbonCommandItem设置其快捷键.
/// </summary>
/// <param name="commandItem">RibbonCommandItem.</param>
/// <param name="key">快捷键字符串.</param>
/// <returns></returns>
private static bool SetAdShortCut(Autodesk.Windows.RibbonItem commandItem, string key)
{
if (commandItem == null || string.IsNullOrEmpty(key))
return false;
Autodesk.Windows.ComponentManager.Ribbon.FindItem(
commandItem.Id,
false,
out var parentPanel,
out var parentTab,
true
);
if (parentTab == null || parentPanel == null)
return false;
var path = string.Format("{0}>{1}", parentTab.Id, parentPanel.Source.Id);
var cmdId = ControlHelper.GetCommandId(commandItem);
if (string.IsNullOrEmpty(cmdId))
{
cmdId = Guid.NewGuid().ToString();
ControlHelper.SetCommandId(commandItem, cmdId);
}
var shortcutItem = new ShortcutItem(commandItem.Text, cmdId, key, path)
{
ShortcutType = StType.RevitAPI
};
UIFrameworkServices.KeyboardShortcutService.applyShortcutChanges(
new Dictionary<string, ShortcutItem>() { { cmdId, shortcutItem } }
);
return true;
}
#endregion
/// <summary>
/// 移除菜单快捷键
/// </summary>
/// <param name="id">命令id</param>
/// <returns></returns>
public bool RemoveRibbonItemShortcut(string id)
{
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
UIFrameworkServices.ShortcutItem shortcutItem;
if (dictionary.TryGetValue(id, out shortcutItem))
{
dictionary.Remove(id);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.SaveShortcuts(list);
this.ArmCommands();
在此之后:
SaveShortcuts(list);
this.ArmCommands();
*/
KeyboardShortcutService.SaveShortcuts(list);
/* 项目“ShrlAlgo.Toolkit.Revit (net481)”的未合并的更改
在此之前:
this.ArmCommands();
return true;
在此之后:
ArmCommands();
return true;
*/
KeyboardShortcutService.ArmCommands();
return true;
}
return false;
}
/// <summary>
/// 是否具有菜单快捷键
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool HasRibbonItemShortcut(string id)
{
return this.LoadShortcuts().TryGetValue(id, out var _);
}
/// <summary>
/// 获取快捷键的映射(最多五个按键)
/// </summary>
/// <param name="id">CommandId</param>
/// <returns>快捷键</returns>
public string GetRibbonItemShortcutsRep(string id)
{
if (this.LoadShortcuts().TryGetValue(id, out var shortcutItem))
{
return shortcutItem.ShortcutsRep;
}
return "";
}
/// <summary>
/// 获取全部命令快捷键CommandId-ObservableCollect<string>()
/// </summary>
/// <returns></returns>
public Dictionary<string, IEnumerable<string>> GetCommandShortcuts()
{
return (from e in this.Commands
select e.Value into e
group e by e.CommandId).ToDictionary((IGrouping<string, UIFrameworkServices.ShortcutItem> i) => i.Key, (IGrouping<string, UIFrameworkServices.ShortcutItem> j) => j.SelectMany((UIFrameworkServices.ShortcutItem i) => i.Shortcuts));
}
/// <summary>
/// 获取自定义快捷键被关联到的命令CommandId
/// </summary>
/// <param name="shortcut">自定义的快捷键</param>
/// <returns>CommandId</returns>
public string GetCommandShortcut(string shortcut)
{
return this.Commands.FirstOrDefault((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value.Shortcuts.Contains(shortcut)).Key;
}
/// <summary>
/// 自定义的快捷键是否已经被关联到某个命令上
/// </summary>
/// <param name="shortcut">自定义的快捷键</param>
/// <returns></returns>
public bool HasCommandShortcut(string shortcut)
{
return this.GetCommandShortcuts().Any((KeyValuePair<string, IEnumerable<string>> e) => e.Value.Contains(shortcut));
}
/// <summary>
/// 加载命令
/// </summary>
public static void LoadCommands()
{
ShortcutsHelper.LoadCommands();
}
/// <summary>
/// 应用快捷键更改
/// </summary>
public void ApplyShortcutChanges()
{
UIFrameworkServices.KeyboardShortcutService.applyShortcutChanges(this.LoadShortcuts());
}
/// <summary>
/// 应用更改
/// </summary>
private static void ArmCommands()
{
ShortcutsHelper.ArmCommands();
}
/// <summary>
/// 获取已经加载的ShortcutItems
/// </summary>
/// <returns></returns>
private Dictionary<string, UIFrameworkServices.ShortcutItem> LoadShortcuts()
{
return ShortcutsHelper.LoadShortcuts();
}
/// <summary>
/// 保存ShortcutItems
/// </summary>
/// <param name="shortcutList"></param>
private static void SaveShortcuts(ICollection<UIFrameworkServices.ShortcutItem> shortcutList)
{
ShortcutsHelper.SaveShortcuts(shortcutList);
}
/// <summary>
/// 所有快捷命令条目字典
/// </summary>
private Dictionary<string, UIFrameworkServices.ShortcutItem> Commands
{
get
{
return ShortcutsHelper.Commands;
}
}
/// <summary>
/// 获取所有ShortcutItems
/// </summary>
/// <returns>ShortcutItems</returns>
public IEnumerable<UIFrameworkServices.ShortcutItem> GetShortcutItems()
{
return from e in this.LoadShortcuts()
select new UIFrameworkServices.ShortcutItem(e.Value);
}
}
}

View File

@@ -0,0 +1,55 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Reflection;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class NativeModule
{
/// <summary>
/// 获取RevitAPI程序集Module内的方法名称.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static MethodInfo GetApiModuleMethod(string name)
{
var module = GetApiModule();
var rs = module.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == name).ToList();
return !rs.Any() ? null : rs.First();
}
/// <summary>
/// 获取RevitAPIUI程序集Module内的方法名称.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static MethodInfo GetUiModuleMethod(string name)
{
var module = GetUiModule();
var rs = module.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == name).ToList();
return !rs.Any() ? null : rs.First();
}
/// <summary>
/// 获取RevitAPI Modules.
/// </summary>
/// <returns></returns>
internal static Module GetApiModule()
{
return typeof(Document).Assembly.Modules.First();
}
/// <summary>
/// 获取RevitAPIUI Modules.
/// </summary>
/// <returns></returns>
internal static Module GetUiModule()
{
return typeof(UIDocument).Assembly.Modules.First();
}
}

View File

@@ -0,0 +1,18 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 项目文档可用
/// </summary>
public class OnProjectDocument : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
var doc = applicationData?.ActiveUIDocument?.Document;
if (doc != null) { return !doc.IsFamilyDocument; }
return false;
}
}

View File

@@ -0,0 +1,183 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using UIFramework;
namespace ShrlAlgoToolkit.RevitCore.Assists;
#if REVIT2020||REVIT2025
public static class OptionsBarAssist
{
private static readonly Grid RootGrid;
private static ContentPresenter _panelPresenter;
private static readonly FrameworkElement InternalToolPanel;
static OptionsBarAssist()
{
//找到整个Revit界面的Grid
RootGrid = VisualAssist.FindVisualParent<Grid>(Autodesk.Windows.ComponentManager.Ribbon, "rootGrid");
if (RootGrid is null)
{
throw new InvalidOperationException("无法在 Revit UI 中找到root Grid");
}
//选项栏所在的Panel在第二行Index为1类型是DialogBarControl
InternalToolPanel = VisualAssist.FindVisualChild<DialogBarControl>(RootGrid, string.Empty);
if (InternalToolPanel is null)
{
throw new InvalidOperationException("无法在 Revit UI 中找到内部工具面板");
}
}
public static TViewModel SetupOptionsBar<TPanel, TViewModel>()
where TViewModel : ObservableObject, new() where TPanel : Panel, new()
{
CloseOptionsBar();
var options = new TViewModel();
var view = new TPanel();
view.DataContext = options;
ShowOptionsBar(view);
return options;
}
public static void ShowInternalToolPanel()
{
if (InternalToolPanel is null)
{
return;
}
InternalToolPanel.Visibility = Visibility.Visible;
InternalToolPanel.Height = 26;
}
public static void ShowOptionsBar(FrameworkElement content)
{
if (_panelPresenter is not null)
{
//var actuallWidth = 0.0;
//var controlHost = VisualAssist.FindVisualChild<ControlHost>(InternalToolPanel, string.Empty);
//var size = VisualTreeHelper.GetChildrenCount(controlHost);
//for (int i = 0; i < size; i++)
//{
// var child = VisualTreeHelper.GetChild(controlHost, 0) as FrameworkElement;
// actuallWidth += child.ActualWidth;
//}
_panelPresenter.Content = content;
_panelPresenter.Visibility = Visibility.Visible;
//设为0隐藏内部的Panel
InternalToolPanel.Height = 0;
return;
}
//第一次执行时,会创建
_panelPresenter = CreateOptionsBar();
_panelPresenter.Content = content;
InternalToolPanel.Height = 0;
}
/// <summary>
/// 关闭、释放
/// </summary>
public static void CloseOptionsBar()
{
if (_panelPresenter is null)
{
return;
}
_panelPresenter.Content = null;
_panelPresenter.Visibility = Visibility.Collapsed;
InternalToolPanel.Height = 26;
}
/// <summary>
/// 隐藏
/// </summary>
public static void HideOptionsBar()
{
if (_panelPresenter is null)
{
return;
}
//_panelPresenter.Content = null;
_panelPresenter.Visibility = Visibility.Collapsed;
InternalToolPanel.Height = 26;
}
/// <summary>
/// 显示
/// </summary>
public static void ShowOptionsBar()
{
if (_panelPresenter is null)
{
return;
}
//_panelPresenter.Content = null;
_panelPresenter.Visibility = Visibility.Visible;
InternalToolPanel.Height = 0;
}
public static async void CloseOptionsBarAsync(TimeSpan delay)
{
await Task.Delay(delay);
try
{
CloseOptionsBar();
}
catch (Exception)
{
}
}
private static ContentPresenter CreateOptionsBar()
{
const int panelRow = 1;
//在内部OptionsBar下方插入一行
RootGrid.RowDefinitions.Insert(panelRow, new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
//RootGrid.ColumnDefinitions.Insert(1,new ColumnDefinition { Width=new GridLength(1, GridUnitType.Auto) });
//把下方的所有元素都往下移动一行
foreach (UIElement child in RootGrid.Children)
{
var row = Grid.GetRow(child);
if (row > 0)
{
Grid.SetRow(child, row + 1);
}
//Grid.SetColumnSpan(child, 2);
}
var panelPresenter = new ContentPresenter();
Grid.SetRow(panelPresenter, panelRow);
//Grid.SetColumn(panelPresenter, panelRow);
RootGrid.Children.Add(panelPresenter);
return panelPresenter;
//var panelPresenter = new ContentPresenter();
//Grid.SetRow(panelPresenter, 1);
//RootGrid.Children.Add(panelPresenter);
//return panelPresenter;
}
/// <summary>
/// 简单注册并调用
/// </summary>
/// <typeparam name="TV"></typeparam>
/// <typeparam name="TM"></typeparam>
/// <param name="action"></param>
public static void RegisterOptionsBar<TV, TM>(Action<TV, TM> action)
where TV : Panel, new()
where TM : ObservableObject, new()
{
try
{
TM viewModel = new();
var view = new TV() { DataContext = viewModel };
action(view, viewModel);
ShowOptionsBar(view);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,520 @@
#if false
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using OpenMcdf;
using System.Windows;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class PublishAssist
{
#region FileAttributes
/// <summary>
/// 设置文件为只读
/// </summary>
/// <param name="filePath"></param>
/// <param name="readOnly"></param>
public static void SetReadOnly(string filePath, bool readOnly = true)
{
try
{
if (File.Exists(filePath))
{
var info = new FileInfo(filePath);
info.IsReadOnly = readOnly;
}
else
{
Debug.WriteLine("文件不存在。");
}
}
catch (UnauthorizedAccessException)
{
Debug.WriteLine("无权限修改文件属性。");
}
catch (Exception ex)
{
Debug.WriteLine($"错误: {ex.Message}");
}
}
/// <summary>
/// 设置文件为只读
/// </summary>
/// <param name="filePath"></param>
public static void SetPrivate(string filePath)
{
try
{
if (File.Exists(filePath))
{
var info = new FileInfo(filePath);
File.SetAttributes(filePath, FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReadOnly);
}
else
{
Debug.WriteLine("文件不存在。");
}
}
catch (UnauthorizedAccessException)
{
Debug.WriteLine("无权限修改文件属性。");
}
catch (Exception ex)
{
Debug.WriteLine($"错误: {ex.Message}");
}
}
#endregion
//需要与addin文件里的GUID保持一致
private static readonly Guid SchemaGuid = new Guid("A519E82B-911C-4CA5-9BC5-ED509B2055D2");
/// <summary>
/// 存储图片字节数据列表 (使用 Base64 编码)
/// </summary>
public static void StoreSignatureMap(Document doc, IDictionary<string, byte[]> signatureMap)
{
ProjectInfo projectInfo = doc.ProjectInformation;
if (projectInfo == null) return;
Schema schema = GetOrCreateSchema();
Entity entity = new Entity(schema);
IDictionary<int, string> imageMap = new Dictionary<int, string>();
try
{
IDictionary<string, string> stringMap = signatureMap
.ToDictionary(kvp => kvp.Key, kvp => Convert.ToBase64String(kvp.Value));
entity.Set<IDictionary<string, string>>("SignatureMap", stringMap);
projectInfo.SetEntity(entity);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 读取图片字节数据列表 (Revit 2020 兼容版, 使用 Base64 解码)
/// </summary>
public static IDictionary<string, byte[]> RetrieveSignatureMap(Document doc)
{
ProjectInfo projectInfo = doc.ProjectInformation;
var emptyDict = new Dictionary<string, byte[]>();
if (projectInfo == null) return emptyDict;
Schema schema = Schema.Lookup(SchemaGuid);
if (schema == null) return emptyDict;
Entity entity = projectInfo.GetEntity(schema);
if (entity == null || !entity.IsValid()) return emptyDict;
IDictionary<string, string> retrievedMap = entity.Get<IDictionary<string, string>>("SignatureMap");
if (retrievedMap == null) return emptyDict;
return retrievedMap.ToDictionary(kvp => kvp.Key, kvp => Convert.FromBase64String(kvp.Value));
}
/// <summary>
/// 获取或创建数据模式 (Revit 2020 兼容版)
/// </summary>
private static Schema GetOrCreateSchema()
{
Schema schema = Schema.Lookup(SchemaGuid);
if (schema == null)
{
try
{
SchemaBuilder schemaBuilder = new SchemaBuilder(SchemaGuid);
schemaBuilder.SetReadAccessLevel(AccessLevel.Application);
schemaBuilder.SetWriteAccessLevel(AccessLevel.Application);
schemaBuilder.SetVendorId("SZMC");
schemaBuilder.SetApplicationGUID(SchemaGuid);
schemaBuilder.SetSchemaName("SignatureStorage");
schemaBuilder.AddMapField("SignatureMap", typeof(string), typeof(string));
schema = schemaBuilder.Finish();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "创建schema错误");
}
}
return schema;
}
#region ExtensionsStorage
//private const string SchemaGuid = "{FBC67C54-781F-4D0A-B31B-8F54707889E0}";
///// <summary>
///// 计算hash256
///// </summary>
///// <param name="filePath"></param>
///// <returns></returns>
//public static string ComputeFileHash(string filePath)
//{
// using (SHA256 sha256 = SHA256.Create())
// using (FileStream stream = File.OpenRead(filePath))
// {
// byte[] hashBytes = sha256.ComputeHash(stream);
// return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower();
// }
//}
#endregion
#region PreviewSettings
/// <summary>
/// 完全替换缩略图
/// </summary>
/// <param name="rvtFilePath"></param>
/// <param name="imgPath"></param>
internal static void ReplaceThumb(string rvtFilePath, Bitmap bmp)
{
using var cf = new CompoundFile(rvtFilePath, CFSUpdateMode.Update, CFSConfiguration.Default);
// 获取根存储
CFStorage root = cf.RootStorage;
// 更新 Windows 预览
if (root.TryGetStream("RevitPreview4.0", out var previewStream))
{
using var outMs = new MemoryStream();
bmp.Save(outMs, ImageFormat.Png);
previewStream.SetData(outMs.ToArray());
}
// 更新 Revit 预览
if (root.TryGetStream("BasicFileInfo", out var basicFileInfo))
{
// 读取现有数据
byte[] data = basicFileInfo.GetData();
// 基本文件信息头部大小(版本等信息)
const int HEADER_SIZE = 0x2C8;
if (data.Length > HEADER_SIZE)
{
// 转换图片为字节数组
using (var ms = new MemoryStream())
{
// 转换为24位色
using (var temp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb))
{
using (var g = Graphics.FromImage(temp))
{
g.DrawImage(bmp, 0, 0);
}
temp.Save(ms, ImageFormat.Bmp);
}
byte[] imageBytes = ms.ToArray();
// 跳过BMP文件头(14字节)和信息头(40字节)
int startIndex = 54;
// 计算新数据大小
int newDataSize = HEADER_SIZE + (imageBytes.Length - startIndex);
byte[] newData = new byte[newDataSize];
// 复制原有头部数据
Array.Copy(data, 0, newData, 0, HEADER_SIZE);
// 复制新的图像数据
Array.Copy(imageBytes, startIndex, newData, HEADER_SIZE, imageBytes.Length - startIndex);
// 写回流
basicFileInfo.SetData(newData);
}
}
}
cf.Commit();
}
/// <summary>
/// 合并图片
/// </summary>
/// <param name="thumbnail">缩略图</param>
/// <param name="overlay">图标</param>
/// <returns></returns>
internal static Bitmap CombineImages(Bitmap thumbnail, Bitmap overlay)
{
// 创建画布
Bitmap result = new Bitmap(thumbnail.Width, thumbnail.Height);
using (Graphics graphics = Graphics.FromImage(result))
{
// 绘制缩略图
graphics.DrawImage(thumbnail, 0, 0);
// 计算叠加位置(右下角)
int padding = 5;
int x = 0 + padding;
//int x = thumbnail.Width - overlay.Width - padding;
int y = thumbnail.Height - overlay.Height - padding;
// 保持图标原始大小或按比例缩放
int iconSize = 48;
var resizedOverlay = new Bitmap(overlay, iconSize, iconSize);
// 带透明度绘制
graphics.DrawImage(
resizedOverlay,
new System.Drawing.Rectangle(x, y, iconSize, iconSize),
new System.Drawing.Rectangle(0, 0, overlay.Width, overlay.Height),
GraphicsUnit.Pixel
);
}
return result;
}
/// <summary>
/// 添加水印
/// </summary>
/// <param name="rvtFilePath"></param>
/// <param name="bmp"></param>
/// <param name="watermark"></param>
internal static void AddWatermark(string rvtFilePath, Bitmap bmp, string watermark)
{
// 打开 RVT 文件的复合文件结构
using var cf = new CompoundFile(rvtFilePath, CFSUpdateMode.Update, CFSConfiguration.Default);
// 获取根存储
CFStorage root = cf.RootStorage;
// 检查是否存在 "RevitPreview4.0" 流
if (!root.TryGetStream("RevitPreview4.0", out var previewStream))
{
Debug.WriteLine("未找到 RevitPreview4.0 流。");
return;
}
// 在图像上绘制半透明文字水印
if (!string.IsNullOrEmpty(watermark))
{
using var g = Graphics.FromImage(bmp);
var font = new Font("微软雅黑", 20, System.Drawing.FontStyle.Bold);
var brush = new SolidBrush(System.Drawing.Color.FromArgb(128, System.Drawing.Color.Red));
g.DrawString(watermark, font, brush, new PointF(20, bmp.Height - 80));
}
// 将修改后的图像保存为 PNG再写回流
using var outMs = new MemoryStream();
bmp.Save(outMs, ImageFormat.Png);
previewStream.SetData(outMs.ToArray());
// 提交更改
cf.Commit();
}
#endregion
#region Hash
// 公钥(硬编码)
private const string PublicKeyPem = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyN+Z04RmVOvzI2Z8Li1t
xB9yv80Y4Pd+M0wiuW9BHccmVlm7LVlWDRZgaAykQxIt0UskZbaIQ5eR4mqywVoM
BygfWqurVtgC6dO3SSbhZiOuSA3g1xJLgje6TOQIPWDGw1Ap0sbWrCuk+bxPFjSi
XANksY3Qfl1e6YETYEEpptTXorM8enM4368/KkvEhFswBqE5/Qf2x7XUpPAfnh3Y
cl45bUu87yDeyAnX4HEuA+aZPHEgXFPmhndtqMiUsLEajQNd8zZQHKmjMbubAq9s
l10w7smZUck+bozHO0RP31ytI6n/7I0T9PXOb/rwjeXBxXD1K0kvU3DPuq2+a5sa
iwIDAQAB
-----END PUBLIC KEY-----
""";
// 私钥(硬编码)
private const string PrivateKeyPem = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyN+Z04RmVOvzI2Z8Li1txB9yv80Y4Pd+M0wiuW9BHccmVlm7
LVlWDRZgaAykQxIt0UskZbaIQ5eR4mqywVoMBygfWqurVtgC6dO3SSbhZiOuSA3g
1xJLgje6TOQIPWDGw1Ap0sbWrCuk+bxPFjSiXANksY3Qfl1e6YETYEEpptTXorM8
enM4368/KkvEhFswBqE5/Qf2x7XUpPAfnh3Ycl45bUu87yDeyAnX4HEuA+aZPHEg
XFPmhndtqMiUsLEajQNd8zZQHKmjMbubAq9sl10w7smZUck+bozHO0RP31ytI6n/
7I0T9PXOb/rwjeXBxXD1K0kvU3DPuq2+a5saiwIDAQABAoIBAACdcETZkedzXBM5
6WofTAof4bWOgovvHJCipBgOiubPKPdG5jNq/bHX2/kSnbl2eWWMQUEuGIiYJccG
/6zF6GDQ3FqHZcNbp8M4gbymNZV0TfTB0KOTyDAaojQ381LgyIK1DcWBHdw2wCRZ
vo6v1BXez4evaUy5ckXQfAA9POiAH6Im7QQlPamaUJfBCfRqFNvTcL6YQngHg8RW
4xFvN8Lxhgnqftw0dY1YzNkDBz+toQ+211LTAJWYC4vZ2sHoGvR74YxZUWPS4wms
QdIq50kRwL45Z9IZcxqp1x27W0pEwBsOO2DhnM6r7mB7Mg9BMEjUFf3oM+vrx67W
w6McEXkCgYEA8q+Bb6ldNqWNa5uLmAziEqXCnN/X9o1Cr81YIeWZpx4Ovx2aR3wH
4OtWl9VmYrU7w758nhPH27q8ADxjSbIB3ShPPdfLcKBoW/5eg+K8Bexr5Fb6Lyqo
SMhFLptxOdNzYLej99XppzRk4CV3s7IZsfQd6okSdfvlqka5ExFjXDkCgYEA0+TZ
IBsc0nCZFTlcDxxMCkmj+c4+c01BGgSeoVSKIgyp5d5DSZoNjxXsrP3Nw3uq2+fh
v4DD/V76BpzQ6v7P+Xg9YbW1QO6QdsG4Cf1bnAFYaTwQwdm2To5SXixYpVnON4Xv
4fOWVL0qrn3PK6aUTJCcEOdhx1YtBl+P4gVq9OMCgYEAhtLcDOXBhE96/rI+Xi2i
FwwBz1dISo/14WcqNEKzFzXKqYBPqrEMS3dS9y02IVZgKoIUB1oj6T2XnmXuHw4G
nQ/83fWZ7ysebyUk6w42uO+8jPGJMlT3gt/IF3mB8Mc5TR0YueIcWajw9dm645jp
T4S4bgjSubtEv1FlFmPNmHkCgYAtmq5ka5FrdVJtL/gxRFzVRf4lsI7eT/039VCS
1lgYdfNHBuwidiZ+6jATF1jmvsIKLED472S/LmbPb4bDXO4z+f3z/qCxBedPt+e9
Nqs1y1BU7dmJbyr+g0EVBaRaihaI+qmjTsLHICOEhI1HDsYSKHl+Zd40gcaU/ZJB
Cft4EwKBgQDeK/3k6OJTPuzskuqMsQy6yHd7hesxvdsfUH78tZrkRB2B9fciOa2v
ulfBss9KKVCQeKaC+91ptLpMOlSJhc9tZUsWvE19vVlNmN0ezQyfSFn+E09Ouqin
0cMfedRgHigwi+T818aFnC8qPzTMqCMQyxRTYxEC43OB9VcPyzo1yA==
-----END RSA PRIVATE KEY-----
""";
private static AsymmetricKeyParameter GetPublicKey()
{
using var reader = new StringReader(PublicKeyPem);
var pemReader = new PemReader(reader);
return (AsymmetricKeyParameter)pemReader.ReadObject();
}
private static AsymmetricKeyParameter GetPrivateKey()
{
using var reader = new StringReader(PrivateKeyPem);
var pemReader = new PemReader(reader);
var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
return keyPair.Private;
}
private static byte[] ComputeFileHash(string filePath)
{
using var sha256 = SHA256.Create();
using var stream = File.OpenRead(filePath);
return sha256.ComputeHash(stream);
}
private static byte[] EncryptData(byte[] data, AsymmetricKeyParameter publicKey)
{
var rsaEngine = new Org.BouncyCastle.Crypto.Engines.RsaEngine();
rsaEngine.Init(true, publicKey);
return rsaEngine.ProcessBlock(data, 0, data.Length);
}
private static byte[] DecryptData(byte[] encryptedData, AsymmetricKeyParameter privateKey)
{
var rsaEngine = new Org.BouncyCastle.Crypto.Engines.RsaEngine();
rsaEngine.Init(false, privateKey);
return rsaEngine.ProcessBlock(encryptedData, 0, encryptedData.Length);
}
private static readonly string EncryptFileName = ".hashes.enc";
/// <summary>
/// 获取相对路径
/// </summary>
/// <param name="relativeTo">相对于某个目录</param>
/// <param name="path">示例路径</param>
/// <returns></returns>
private static string GetRelativePath(string relativeTo, string path)
{
// 确保路径以目录分隔符结尾(对于目录路径)
string from = Path.GetFullPath(relativeTo).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
string to = Path.GetFullPath(path);
Uri fromUri = new Uri(from);
Uri toUri = new Uri(to);
// 计算相对 URI
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
// 将相对 URI 转换回字符串,并替换可能的 '/' 为当前平台的分隔符
string relativePath = Uri.UnescapeDataString(relativeUri.ToString()).Replace('/', Path.DirectorySeparatorChar);
return relativePath;
}
public static string GenerateEncFile(string directoryPath)
{
var publicKey = GetPublicKey();
var files = Directory.GetFiles(directoryPath, "*.rvt", SearchOption.AllDirectories);
var encFilePath = Path.Combine(directoryPath, EncryptFileName);
using var fs = new FileStream(encFilePath, FileMode.Create, FileAccess.Write);
using var bw = new BinaryWriter(fs);
foreach (var file in files)
{
//var fileName = Path.GetFileName(file);
var fileName = GetRelativePath(directoryPath, file);
var fileHash = ComputeFileHash(file);
var nameBytes = Encoding.UTF8.GetBytes(fileName);
var combined = nameBytes.Concat(fileHash).ToArray();
var encrypted = EncryptData(combined, publicKey);
bw.Write(nameBytes.Length);
bw.Write(nameBytes);
bw.Write(encrypted.Length);
bw.Write(encrypted);
}
return encFilePath;
}
public static StringBuilder VerifyEncFile(string directoryPath, out bool hasModified)
{
hasModified = false;
var privateKey = GetPrivateKey();
var encFilePath = Path.Combine(directoryPath, EncryptFileName);
StringBuilder sb = new StringBuilder();
if (!File.Exists(encFilePath))
{
sb.AppendLine("验证文件未找到。");
return sb;
}
Dictionary<string, string> allFiles;
try
{
//转成相对路径
allFiles = Directory.GetFiles(directoryPath, "*.rvt", SearchOption.AllDirectories)
.ToDictionary(
file => GetRelativePath(directoryPath, file),
file => file,
StringComparer.OrdinalIgnoreCase);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "验证出错!", MessageBoxButton.OK, MessageBoxImage.Error);
return sb;
}
using var fs = new FileStream(encFilePath, FileMode.Open, FileAccess.Read);
using var br = new BinaryReader(fs);
while (fs.Position < fs.Length)
{
var nameLen = br.ReadInt32();
//if (nameLen <= 0 || nameLen > 256) // 假设文件名长度不超过256字节
//{
// Console.WriteLine("无效的文件名长度。");
// continue;
//}
var nameBytes = br.ReadBytes(nameLen);
var fileName = Encoding.UTF8.GetString(nameBytes);
var encLen = br.ReadInt32();
var encrypted = br.ReadBytes(encLen);
if (!allFiles.TryGetValue(fileName, out var actualFilePath))
{
sb.AppendLine($"{fileName} 未找到");
continue;
}
var decrypted = DecryptData(encrypted, privateKey);
var expectedName = Encoding.UTF8.GetString(decrypted, 0, nameBytes.Length);
var expectedHash = decrypted.Skip(nameBytes.Length).ToArray();
var currentHash = ComputeFileHash(actualFilePath);
bool match = expectedName == fileName && currentHash.SequenceEqual(expectedHash);
if (!match)
{
hasModified = true;
sb.AppendLine($"{fileName}");
}
}
return sb;
}
#endregion
}
#endif

View File

@@ -0,0 +1,50 @@
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// UiApplication.PostCommand 结束事件监视器
/// </summary>
///<example>
/// <c>RevitCommandEndedMonitor revitCommandEndedMonitor = new RevitCommandEndedMonitor(UiApplication);
/// revitCommandEndedMonitor.CommandEnded += OnCommandEnded;
/// var command = RevitCommandId.LookupPostableCommandId(PostableCommand.TypeProperties);
/// UiApplication.PostCommand(command);</c>
/// </example>
public class RevitCommandEndedMonitor
{
private readonly UIApplication revitUiApplication;
private bool initializingCommandMonitor;
public event EventHandler CommandEnded;
public RevitCommandEndedMonitor(UIApplication uIApplication)
{
revitUiApplication = uIApplication;
initializingCommandMonitor = true;
revitUiApplication.Idling += OnRevitUiApplicationIdling;
}
private void OnRevitUiApplicationIdling(object sender, IdlingEventArgs idlingEventArgs)
{
if (initializingCommandMonitor)
{
initializingCommandMonitor = false;
return;
}
revitUiApplication.Idling -= OnRevitUiApplicationIdling;
OnCommandEnded();
}
protected virtual void OnCommandEnded()
{
CommandEnded?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -0,0 +1,649 @@
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Packaging;
using System.Reflection;
using System.Text;
using System.Windows.Media.Imaging;
using System.Xml;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// 直接读取文件的信息不依赖RevitAPI
/// </summary>
public class RevitFileAssist
{
public RevitFileAssist(string filePath)
{
if (filePath.EndsWith(".rvt") || filePath.EndsWith(".rte") || filePath.EndsWith(".rfa") ||
filePath.EndsWith(".rft"))
{
ParserRevitFile(filePath);
}
else
{
throw new Exception("文件格式不正确!");
}
}
/// <summary>
/// 图片资源转字节
/// </summary>
/// <param name="bitmapSource"></param>
/// <returns></returns>
private static byte[] BitSourceToArray(BitmapSource bitmapSource)
{
BitmapEncoder encoder = new JpegBitmapEncoder();
using var ms = new MemoryStream();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(ms);
return ms.ToArray();
}
private static int GetPngStartingOffset(byte[] previewData)
{
var markerFound = false;
var startingOffset = 0;
var previousValue = 0;
using var ms = new MemoryStream(previewData);
for (var i = 0; i < previewData.Length; i++)
{
var currentValue = ms.ReadByte();
// possible start of PNG file data
if (currentValue == 137) // 0x89
{
markerFound = true;
startingOffset = i;
previousValue = currentValue;
continue;
}
switch (currentValue)
{
case 80: // 0x50
if (markerFound && previousValue == 137)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
case 78: // 0x4E
if (markerFound && previousValue == 80)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
case 71: // 0x47
if (markerFound && previousValue == 78)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
case 13: // 0x0D
if (markerFound && previousValue == 71)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
case 10: // 0x0A
if (markerFound && previousValue == 26)
{
return startingOffset;
}
if (markerFound && previousValue == 13)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
case 26: // 0x1A
if (markerFound && previousValue == 10)
{
previousValue = currentValue;
continue;
}
markerFound = false;
break;
}
}
return 0;
}
/// <summary>
/// 获取缩略图
/// </summary>
/// <param name="previewData"></param>
/// <returns></returns>
private static Image GetPreviewAsImage(byte[] previewData)
{
if (previewData is not { Length: > 0 })
{
using var newBitmap = new Bitmap(128, 128);
return newBitmap.Clone() as Bitmap;
}
// read past the Revit metadata to the start of the PNG image
var startingOffset = GetPngStartingOffset(previewData);
if (startingOffset == 0)
{
using var newBitmap = new Bitmap(128, 128);
return newBitmap.Clone() as Bitmap;
}
try
{
var pngDataBuffer = new byte[previewData.GetUpperBound(0) - startingOffset + 1];
// read the PNG image data into a byte array
using (var ms = new MemoryStream(previewData))
{
ms.Position = startingOffset;
ms.Read(pngDataBuffer, 0, pngDataBuffer.Length);
}
byte[] decoderData = null;
// if the image data is valid
if (pngDataBuffer != null)
{
// use a memory stream to decode the PNG image data
// and copy the decoded data into a byte array
using var ms = new MemoryStream(pngDataBuffer);
var decoder = new PngBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
decoderData = BitSourceToArray(decoder.Frames[0]);
}
if (decoderData is { Length: > 0 })
{
// use another memory stream to create a Bitmap
// and then an Image from that Bitmap
using var ms = new MemoryStream(decoderData);
using var newBitmap = new Bitmap(ms);
using Image newImage = newBitmap;
return newImage.Clone() as Image;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
using (var newBitmap = new Bitmap(128, 128))
{
return newBitmap.Clone() as Bitmap;
}
}
private static StringBuilder ParseContents(string unicodeData)
{
var s = new StringBuilder();
var basicFileInfoParts = unicodeData.Split('\0');
foreach (var basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (var detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
private static string ParseDetailInfo(string detailInfo)
{
try
{
detailInfo = detailInfo.Trim();
var index = detailInfo.IndexOf(":", StringComparison.Ordinal);
var detailValue = detailInfo.Substring(index + 1);
var detailKey = detailInfo.Substring(0, index);
detailKey = detailKey.Trim().ToUpper().Replace(" ", string.Empty);
detailKey = PurgeUnprintableCharacters(detailKey);
detailValue = PurgeUnprintableCharacters(detailValue);
return $"{detailKey}:{detailValue}";
//Console.WriteLine($"{detailKey}:{detailValue}");
}
catch (Exception)
{
return detailInfo;
}
//switch (detailKey)
//{
// case "WORKSHARING":
// if (string.IsNullOrEmpty(detailValue))
// {
// WorkSharing = WorkSharingMode.HCLight;
// return;
// }
// string workSharing = detailValue.Replace(" ", string.Empty).Trim().ToUpper();
// switch (workSharing)
// {
// case "NOTENABLED":
// WorkSharing = WorkSharingMode.NotEnabled;
// break;
// case "LOCAL":
// WorkSharing = WorkSharingMode.Local;
// break;
// case "CENTRAL":
// WorkSharing = WorkSharingMode.Central;
// break;
// default:
// WorkSharing = WorkSharingMode.HCLight;
// break;
// }
// break;
// case "USERNAME":
// UserName = detailValue.Trim();
// break;
// case "CENTRALFILEPATH":
// CentralFilePath = detailValue.Trim();
// break;
// case "REVITBUILD":
// RevitBuild = detailValue.Trim();
// break;
// case "LASTSAVEPATH":
// LastSavedpath = detailValue.Trim();
// break;
// case "OPENWORKSETDEFAULT":
// OpenWorksetDefault = Convert.ToInt32(detailValue.Trim());
// break;
// default:
// Console.WriteLine($"{detailKey}:{detailValue}");
// //Debug.Assert(false, string.Format("{0} was not found in the case tests.", detailKey));
// break;
//}
}
private static StringBuilder ParseFileInfo(string unicodeData)
{
var s = new StringBuilder();
var basicFileInfoParts = unicodeData.Split('\0');
foreach (var basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (var detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
/// <summary>
/// 解析参数
/// </summary>
/// <param name="streamInfo"></param>
private List<FamilyTypeDefinition> ParseParameterDefinition(StreamInfo streamInfo)
{
var document = new XmlDocument();
string xmlStr;
byte[] streamData;
using var streamReader = streamInfo.GetStream(FileMode.Open, FileAccess.Read);
streamData = new byte[streamReader.Length];
streamReader.Read(streamData, 0, streamData.Length);
xmlStr = Encoding.UTF8.GetString(streamData);
document.LoadXml(xmlStr);
//string xmldocpath = Path.GetFileNameWithoutExtension(FileName) + ".xml";
//document.Save(xmldocpath);
//读取参数信息
var root = document.DocumentElement;
//节点前缀的命名空间
var nameSpace = root.GetNamespaceOfPrefix("A");
//string nameSpace = root.NamespaceURI;
var nsmgr = new XmlNamespaceManager(document.NameTable);
;
nsmgr.AddNamespace("entry", nameSpace);
//族类型
var xnlist = document.GetElementsByTagName("A:part");
var fileinfo = document.GetElementsByTagName("A:design-file");
foreach (XmlNode xn in fileinfo)
{
if (xn.HasChildNodes)
{
foreach (XmlNode child in xn.ChildNodes)
{
if (child.Name == "A:title")
{
SafeName = child.InnerText;
}
if (child.Name == "A:product")
{
Product = child.InnerText;
}
if (child.Name == "A:product-version")
{
RevitVersion = child.InnerText;
}
if (child.Name == "A:product-updated")
{
UpdateTime = child.InnerText;
}
}
}
}
//XmlNode rootnode = document.SelectSingleNode("/entry/A: family/A:part", nsmgr);
//XmlNodeList xnlist = rootnode.ChildNodes;
var symbols = new List<FamilyTypeDefinition>();
foreach (XmlNode xn in xnlist)
{
//XmlAttributeCollection attriCol = xn.Attributes;
//foreach (XmlAttribute xmlAttri in attriCol)
//{
// string name = xmlAttri.Name;
// string value = xmlAttri.Value;
//}
var symbol = new FamilyTypeDefinition();
if (xn.HasChildNodes)
{
foreach (XmlNode child in xn.ChildNodes)
{
var p = new ParameterDefinition();
if (child.Name == "title")
{
symbol.Name = child.InnerText;
continue;
}
//族类型节点
p.Name = child.Name;
//族名称
p.Value = child.InnerText;
symbol.ParameterDefinitions.Add(p);
}
}
symbols.Add(symbol);
}
return symbols;
}
private static StringBuilder ParsePartAtom(string unicodeData)
{
var s = new StringBuilder();
var basicFileInfoParts = unicodeData.Split('\0');
foreach (var basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (var detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
/// <summary>
/// 解析预览图像(使用循环确保完整读取,避免 CA2022 警告)
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
private static Image ParsePreviewImage(StreamInfo stream)
{
byte[] streamData;
using (var streamReader = stream.GetStream(FileMode.Open, FileAccess.Read))
{
// 获取长度并校验
long lengthLong = streamReader.Length;
if (lengthLong < 0)
{
throw new IOException("Stream length is negative.");
}
if (lengthLong > int.MaxValue)
{
throw new IOException("Stream is too large to be read into a single byte array.");
}
var totalBytes = (int)lengthLong;
streamData = new byte[totalBytes];
int offset = 0;
while (offset < totalBytes)
{
int bytesRead = streamReader.Read(streamData, offset, totalBytes - offset);
if (bytesRead == 0)
{
// 流在预期字节读取完成前就结束,抛出异常以避免不完整数据处理
throw new EndOfStreamException("Unable to read the full stream data.");
}
offset += bytesRead;
}
}
return GetPreviewAsImage(streamData);
}
/// <summary>
/// 解析传输数据
/// </summary>
/// <param name="unicodeData"></param>
/// <returns></returns>
private static StringBuilder ParseTransMissionData(string unicodeData)
{
var s = new StringBuilder();
var basicFileInfoParts = unicodeData.Split('\0');
foreach (var basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n") >= 0)
{
var detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (var detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
//public static StringBuilder GetFamilyInfo(List<FamilyTypeDefinition> symbols)
//{
// var sb = new StringBuilder();
// foreach (var symbol in symbols)
// {
// sb.Append($"族类型:{symbol.Name}{symbol.Value}\r\n");
// foreach (var param in symbol.ParameterDefinitions)
// {
// sb.Append($" {param.Name}:{param.Value}\r\n");
// }
// }
// return sb;
//}
/// <summary>
/// 解析Revit文件信息
/// </summary>
/// <param name="filename"></param>
private void ParserRevitFile(string filename)
{
//获取Storageroot Object
var bindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.InvokeMethod;
var storageRootType = typeof(StorageInfo).Assembly.GetType("System.IO.Packaging.StorageRoot", true, false);
//Type storageRootType = typeof(StorageInfo).Assembly.GetType("System.IO.Packaging", true, false);
object[] file =
{
filename,
FileMode.Open,
FileAccess.Read,
FileShare.Read
};
var Storage = (StorageInfo)storageRootType.InvokeMember("Open", bindingFlags, null, null, file);
//if (Storage != null)
//{
// storageRootType.InvokeMember("CloseTrigger", bindingFlags, null, Storage, file);
// //Console.Write($"{filePath}文件无法作为结构存储,并打开打开");
//}
//var x = Storage.ThumbnailImage.GetPreviewAsImage();
//读取结构化存储文件
var streams = Storage.GetStreams();
foreach (var streamInfo in streams)
{
string unicodeData;
using (var streamReader = streamInfo.GetStream(FileMode.Open, FileAccess.Read))
{
byte[] streamData = new byte[streamReader.Length];
streamReader.Read(streamData, 0, streamData.Length);
unicodeData = Encoding.Unicode.GetString(streamData);
//unicodeData = Encoding.UTF8.GetString(streamData);
}
if (streamInfo.Name.ToUpper().Equals("BASICFILEINFO"))
{
FileInfo = ParseFileInfo(unicodeData);
}
if (streamInfo.Name.ToUpper().Equals("PARTATOM"))
{
//Console.WriteLine("部分原子:\r\n" + ParsePartAtom(unicodeData));
if (filename.EndsWith(".rfa"))
{
SymbolTypes = ParseParameterDefinition(streamInfo);
//Console.WriteLine("族参数:\r\n" + GetFamilyInfo(symbols));
}
}
if (streamInfo.Name.ToUpper().Equals("TRANSMISSIONDATA"))
{
TransMissionData = ParseTransMissionData(unicodeData);
}
if (streamInfo.Name.ToUpper().Equals("REVITPREVIEW4.0"))
{
PreviewImage = ParsePreviewImage(streamInfo);
}
if (streamInfo.Name.ToUpper().Equals("CONTENTS"))
{
Content = ParseContents(unicodeData);
}
}
//if (Storage != null)
//{
// try
// {
// storageRootType.InvokeMember("CloseTrigger", bindingFlags, null, Storage, file);
// }
// catch (Exception ex)
// {
// MessageBox.ShowAhead(ex.ViewMessage);
// }
//}
}
/// <summary>
/// 清理无效字符
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
public static string PurgeUnprintableCharacters(string oldValue)
{
var sb = new StringBuilder();
var oldValueArray = oldValue.ToCharArray();
foreach (var letter in oldValueArray)
{
int decimalValue = letter;
if (decimalValue is >= 32 and <= 126)
{
sb.Append(letter);
}
}
oldValue = sb.ToString();
sb.Length = 0;
sb.Capacity = 0;
sb = null;
return oldValue;
}
public StringBuilder Content { get; private set; }
public StringBuilder FileInfo { get; private set; }
public Image PreviewImage { get; private set; }
public string Product { get; private set; }
public string RevitVersion { get; private set; }
public string SafeName { get; private set; }
public List<FamilyTypeDefinition> SymbolTypes { get; private set; }
public StringBuilder TransMissionData { get; private set; }
public string UpdateTime { get; private set; }
public class FamilyTypeDefinition
{
public string Name { get; set; }
public List<ParameterDefinition> ParameterDefinitions { get; set; } = new();
public string Value { get; set; }
}
public class ParameterDefinition
{
public string Name { get; set; }
public string TypeOfParameterDefinition { get; set; }
public string Value { get; set; }
}
}
}

View File

@@ -0,0 +1,179 @@
// [Directories](文件位置)
//
// ·ProjectPath——打开项目时的默认路径
//
// ·ExternalParameters——共享参数文件保存路径
//
//
//
// [Graphics](图形设置)
//
// ·TempDimFontSizeInPoints——临时尺寸标注文字外观
//
// ·AllowNavigationDuringRedraw——重绘期间允许导航
//
// ·UseGpuOcclusionCulling——仅绘制可见图元
//
// ·Antialiasing——使用反失真平滑线条
//
// ·AntialiasingOptions——使用反失真平滑线条选项
//
// ·SemiTransparent——半透明
//
// ·ThinLinesEnabled——是否启用细线
//
// ·UseGraphicsHardware——是否使用硬件加速
//
//
//
// [Selection](选择设置)
//
// ·AllowFaceSelection——是否按面选择图元
//
// ·AllowLinkSelection——是否选择链接
//
// ·AllowPinnedSelection——是否选择锁定图元
//
// ·AllowPressAndDrag——是否选择时拖拽图元
//
// ·AllowUnderlaySelection——是否选择基线图元
//
//
//
// [Windows](窗口)
//
// ·Maximized = 00 = 以最后一次任务期间使用的窗口大小启动 Revit默认1 = 以最大化窗口启动 Revit。
using System.IO;
using System.Text;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public static class RevitIniAssist
{
public static string RevitVersion;
public static string IniPath { get; } =
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + $"\\Autodesk\\Revit\\Autodesk Revit {RevitVersion}\\Revit.ini";
/// <summary>
/// 添加项目样板
/// </summary>
/// <param name="revitVersion"></param>
/// <param name="revitTemplateIniFilePath">.ini文件的完整路径</param>
/// <param name="templateFilePath"></param>
public static void AddProjectTemplate(string revitVersion, string revitTemplateIniFilePath, string templateFilePath)
{
//Autodesk.Revit.ApplicationServices.Application.CurrentUsersDataFolderPath
string oriDateFilePath = Environment.GetEnvironmentVariable("appdata") + $"\\Autodesk\\Revit\\Autodesk Revit {revitVersion}\\Revit.ini";
if (File.Exists(oriDateFilePath))
{
using (StreamReader sr = new StreamReader(oriDateFilePath, Encoding.Unicode))
{
StreamWriter sw = new StreamWriter(revitTemplateIniFilePath, false, Encoding.Unicode);
string inputLine;
while ((inputLine = sr.ReadLine()) != null)
{
if (inputLine.StartsWith("DefaultTemplate="))
{
_ = inputLine.Insert(16, templateFilePath);
//if (inputLine.Contains("Example_SCHEMA.rte"))
//{
//}
//else
//{
// inputLine = inputLine + @", Example_SCHEMA=C:\temp\Example_SCHEMA.rte";
//}
}
sw.WriteLine(inputLine);
}
sw.Close();
}
File.Replace(revitTemplateIniFilePath, oriDateFilePath, null);
}
}
public static string ReadRevitIniData(string fileName, string targetTemplateFilePath)
{
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(fileName, Encoding.Unicode))
{
string lineBuf;
bool isTargetSection = false;
while ((lineBuf = sr.ReadLine()) != null)
{
if (lineBuf == "[DirectoriesCHS]")
{
isTargetSection = true;
}
if (isTargetSection)
{
if (lineBuf.Contains("DefaultTemplate"))
{
if (!lineBuf.Contains(targetTemplateFilePath))
{
int starIndex = "DefaultTemplate=".Length;
string endStr = lineBuf.Substring(starIndex);
lineBuf = "DefaultTemplate=" + targetTemplateFilePath;
lineBuf += ",";
lineBuf += endStr;
}
isTargetSection = false;
}
sb.AppendLine(lineBuf);
}
}
}
return sb.ToString();
}
public static void WriteFileData(string fileName, string value)
{
using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.Unicode))
{
sw.Write(value);
sw.Flush();
sw.Close();
}
}
public static void WriteRevitIniData(string existFilePath, string templateFilePath)
{
string directory = Path.GetDirectoryName(existFilePath);
if (directory == null)
{
return;
}
DirectoryInfo dirInfo = new DirectoryInfo(directory);
try
{
if (dirInfo.Exists)
{
if (File.Exists(existFilePath))
{
string outPut = ReadRevitIniData(existFilePath, templateFilePath);
if (!string.IsNullOrEmpty(outPut))
{
WriteFileData(existFilePath, outPut);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}

View File

@@ -0,0 +1,16 @@
namespace ShrlAlgoToolkit.RevitCore.Assists
{
/// <summary>
/// 多个快捷键使用#分割
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
class ShortcutRepAttribute : Attribute
{
public ShortcutRepAttribute(string shortcuts)
{
Shortcuts = shortcuts;
}
public string Shortcuts { get; }
}
}

View File

@@ -0,0 +1,315 @@
using Autodesk.Revit.DB;
using ShrlAlgoToolkit.RevitCore.Extensions;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public static class SpatialAssist
{
//private static List<Curve> _curvesSorted;
/// <summary>
/// 拆分线串变成固定长度的曲线段。
/// </summary>
/// <param name="curveChain"></param>
/// <param name="segmentLength"></param>
/// <returns></returns>
public static List<ICurveContainer> SplitCurveChainByFixedLength(CurveLoop curveChain, double segmentLength)
{
List<ICurveContainer> container = new List<ICurveContainer>();
if (curveChain.GetExactLength() - segmentLength < 10e-6)//总长度小于分割长度
{
return [.. curveChain.Select(c => new SingleCurve(c))];
}
//double currentParam = 0.0;
//Curve currentCurve;
//Curve remainingCurve;
//int currentCurveIndex = 0;
CurveList mergeList = null;
//double tempLength = 0.0;
//double currentStartParam = 0.0;
//还需要的曲线长度才能满足分割长度
double neededLength = 0.0;
//List<CurveLoop> loops = new List<CurveLoop>();
for (int i = 0; i < curveChain.Count(); i++)
{
Curve currentCurve = curveChain.ElementAt(i);
//如果需要的长度大于曲线长度,说明整条曲线都应该被包含在内
if (neededLength > currentCurve.Length)
{
mergeList.Curves.Add(currentCurve);
neededLength -= currentCurve.Length;
continue;
}
var splitList = currentCurve.SplitByFixedLength(
neededLength,
segmentLength,
out var previousNeedCurve,
out var remainingCurve);
//需要将前置分割曲线添加上一段的列表中
if (previousNeedCurve != null)
{
mergeList.Curves.Add(previousNeedCurve);
//扣除已经添加的曲线长度,得到剩余需要的长度
neededLength = segmentLength - mergeList.Curves.Sum(c => c.Length);
}
//可以分割
if (splitList.Count > 0)
{
container.AddRange(splitList.Select(c => new SingleCurve(c)));
}
//已经满足分割长度,新建下一个列表
if (neededLength < 10e-6)
{
mergeList = new CurveList([]);
container.Add(mergeList);
}
if (remainingCurve == null)//完全均分时,重置起点
{
neededLength = 0.0;
}
else//剩余曲线添加到下一个列表中
{
mergeList.Curves.Add(remainingCurve);
neededLength = segmentLength - mergeList.Curves.Sum(c => c.Length);
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
/// <summary>
/// 拆分成点和右侧朝向。
/// </summary>
/// <param name="curveChain"></param>
/// <param name="segmentLength"></param>
/// <returns></returns>
public static Dictionary<XYZ, XYZ> EvalutePointCurveChainByFixedLength(CurveLoop curveChain, double segmentLength)
{
Dictionary<XYZ, XYZ> container = new();
if (curveChain.GetExactLength() - segmentLength < 10e-6)//总长度小于分割长度时,取起点
{
var curve = curveChain.FirstOrDefault();
var startPoint = curve.GetEndPoint(0);
var orientation = curve.ComputeDerivatives(0, true).BasisX.Normalize();
return new Dictionary<XYZ, XYZ> { { startPoint, orientation } };
}
//CurveList mergeList = null;
double currentLength = 0.0;
//还需要的曲线长度才能满足分割长度
double neededLength = 0.0;
for (int i = 0; i < curveChain.Count(); i++)
{
Curve currentCurve = curveChain.ElementAt(i);
//如果需要的长度大于曲线长度,说明整条曲线都应该被包含在内
if (neededLength > currentCurve.Length)
{
//mergeList.Curves.Add(currentCurve);
currentLength += currentCurve.Length;
neededLength -= currentCurve.Length;
continue;
}
var splitList = currentCurve.EvalutePointByFixedLength(
neededLength,
segmentLength,
out var previous,
out var remaining);
//需要将前置分割曲线添加上一段的列表中
if (previous > 0.0)
{
currentLength += previous;
//扣除已经添加的曲线长度,得到剩余需要的长度
neededLength = segmentLength - currentLength;
}
//可以分割
if (splitList.Count > 0)
{
foreach (var item in splitList)
{
if (!container.Any(p => p.Key.IsAlmostEqualTo(item.Key)))//闭合曲线起终点有可能一样
{
container.Add(item.Key, item.Value);
}
}
}
//已经满足分割长度
if (neededLength < 10e-6)
{
currentLength = 0;
//var point = currentCurve.Evaluate(1, false);
//var orientation = currentCurve.Evaluate(1, false);
//var p = new XYZ(-411.791042284402, 1330.9543320873, 0);
//if (p.IsAlmostEqualTo(point))
//{
//}
//container.Add(point,orientation);
}
if (remaining == 0.0)//完全均分时,重置起点
{
neededLength = 0.0;
currentLength = 0.0;
if (i < curveChain.Count() - 1)//如果还有下一个的时候,移除最后一个点
{
var last = container.Last();
container.Remove(last.Key);
}
}
else//剩余曲线添加到下一个列表中
{
currentLength += remaining;
neededLength = segmentLength - currentLength;
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
public static Transform GetProfileTransform(Curve referCurve)
{
//非刚性变换,会改变原几何,此变换在曲线计算导数时为非刚性变换各个basis的模不等于1
var point = referCurve.GetEndPoint(0);
var transform = referCurve.ComputeDerivatives(0, true);
var tangent = transform.BasisX.Normalize();
var rightBasis = tangent.CrossProduct(XYZ.BasisZ).Normalize();
var topBasis = rightBasis.CrossProduct(tangent).Normalize();
//构造新的变换
var profileTransform = Transform.Identity;
profileTransform.Origin = point;
profileTransform.BasisX = rightBasis;
profileTransform.BasisY = topBasis;
profileTransform.BasisZ = tangent;
return profileTransform;
}
/// <summary>
/// 多段线
/// </summary>
/// <param name="curves"></param>
/// <returns></returns>
public static PolyLine ByJoinedCurves(List<Curve> curves)
{
List<XYZ> points = [];
foreach (var c in curves)
{
points.Add(c.GetEndPoint(0));
}
points.Add(curves.Last().GetEndPoint(1));
if (points.Count == 0)
{
return null;
}
return PolyLine.Create(points);
}
/// <summary>
/// 将一组离散的曲线整理成多条连续的线串。
/// </summary>
/// <param name="curves">输入的曲线集合,可能包含直线、圆弧、样条曲线等。</param>
/// <returns>一个包含多条线串的列表,每条线串本身也是一个曲线列表,且内部曲线方向连续。</returns>
public static List<List<Curve>> GroupContinuousCurves(IEnumerable<Curve> curves)
{
// 最终返回的所有线串集合
var allChains = new List<List<Curve>>();
// 可被消耗的曲线列表
var remainingCurves = new List<Curve>(curves);
// Revit API 中用于几何比较的默认精度
double tolerance = 1e-9;
// 当还有未处理的曲线时,持续循环
while (remainingCurves.Any())
{
// 开始一条新的线串
var currentChain = new List<Curve>();
// 从剩余曲线中取第一条作为新线串的起点
var firstCurve = remainingCurves[0];
currentChain.Add(firstCurve);
remainingCurves.RemoveAt(0);
// 持续延长当前线串,直到无法再延长
while (true)
{
bool chainExtended = false;
// 获取当前线串的头尾端点
XYZ chainStartPoint = currentChain.First().GetEndPoint(0);
XYZ chainEndPoint = currentChain.Last().GetEndPoint(1);
// 从后向前遍历以安全地在循环中移除元素
for (int i = remainingCurves.Count - 1; i >= 0; i--)
{
var candidate = remainingCurves[i];
XYZ candidateStartPoint = candidate.GetEndPoint(0);
XYZ candidateEndPoint = candidate.GetEndPoint(1);
// 尝试连接到线串的尾部
if (chainEndPoint.IsAlmostEqualTo(candidateStartPoint, tolerance))
{
currentChain.Add(candidate);
remainingCurves.RemoveAt(i);
chainExtended = true;
break; // 找到一个后就跳出内层for循环重新获取线串端点
}
else if (chainEndPoint.IsAlmostEqualTo(candidateEndPoint, tolerance))
{
currentChain.Add(candidate.CreateReversed()); // 反向后添加
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
// 尝试连接到线串的头部
else if (chainStartPoint.IsAlmostEqualTo(candidateEndPoint, tolerance))
{
currentChain.Insert(0, candidate); // 插入到头部
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
else if (chainStartPoint.IsAlmostEqualTo(candidateStartPoint, tolerance))
{
currentChain.Insert(0, candidate.CreateReversed()); // 反向后插入到头部
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
}
// 如果本轮没有延长线串,说明这条线串已经构建完毕
if (!chainExtended)
{
break; // 跳出 while(true) 循环
}
}
// 将构建完成的线串添加到最终结果中
allChains.Add(currentChain);
}
return allChains;
}
}
public interface ICurveContainer
{
}
public class SingleCurve : ICurveContainer
{
public SingleCurve(Curve curve)
{
Curve = curve;
}
public Curve Curve { get; }
}
public class CurveList : ICurveContainer
{
public List<Curve> Curves { get; }
public CurveList(List<Curve> curves) => Curves = curves;
}

View File

@@ -0,0 +1,38 @@
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class StatusBarAssist
{
private IntPtr statusBar = IntPtr.Zero;
protected StatusBarAssist()
{
// 找到状态栏,这样我们就可以添加信息了。
var revitHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
if (revitHandle != IntPtr.Zero)
{
statusBar = KeyIntPtrHelper.FindWindowEx(revitHandle, IntPtr.Zero, "msctls_statusbar32", string.Empty);
}
}
/// <summary>
/// 如果状态栏有有效句柄,则设置状态栏的值。
/// </summary>
/// <param name="msg">The message.</param>
public void Set(string msg)
{
if (statusBar != IntPtr.Zero)
{
KeyIntPtrHelper.SetWindowText(statusBar, msg);
}
}
/// <summary>
/// 创建一个新的 StatusBarAssist。
/// </summary>
/// <returns>The StatusBarAssist.</returns>
public static StatusBarAssist Create()
{
return new StatusBarAssist();
}
}

View File

@@ -0,0 +1,149 @@
using System.Drawing;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using Autodesk.Revit.UI;
using adWin = Autodesk.Windows;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public class UIAssist
{
/// <summary>
/// 创建ToggleButton
/// </summary>
/// <param name="showText"></param>
/// <param name="largeImage"></param>
/// <param name="toolTip"></param>
/// <returns></returns>
public static adWin.RibbonToggleButton NewAwToggleButton(string showText, Bitmap largeImage, string toolTip = null)
{
return new adWin.RibbonToggleButton()
{
LargeImage = ToBitmapSource(largeImage),
Size = adWin.RibbonItemSize.Large,
ToolTip = toolTip ?? showText,
ShowText = true,
Id = $"ID_{showText}",
Name = $"Tog_{showText}",
Text = showText,
IsCheckable = true,
Orientation = System.Windows.Controls.Orientation.Vertical,
};
}
/// <summary>
/// 位图转像素集
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
private static BitmapSource ToBitmapSource(Bitmap bitmap)
{
return bitmap == null
? null
: Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
/// <summary>
/// 创建按钮
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="showText"></param>
/// <param name="largeImage"></param>
/// <param name="image"></param>
/// <param name="command">RelayCommand</param>
/// <param name="parameter">命令的参数</param>
/// <param name="toolTip"></param>
/// <returns></returns>
public static adWin.RibbonButton NewAwButton(string showText, Bitmap largeImage, Bitmap image, ICommand command, object parameter, string toolTip = default)
{
return new()
{
Image = ToBitmapSource(image),
LargeImage = ToBitmapSource(largeImage),
Size = adWin.RibbonItemSize.Standard,
ShowText = true,
Id = $"ID_{showText}",
Name = $"Btn_{showText}",
ToolTip = toolTip ?? showText,
Text = showText,
IsCheckable = true,
CommandHandler = command,
CommandParameter = parameter,
//Orientation = System.Windows.Controls.Orientation.Vertical
};
}
private static readonly string AddInPath = Assembly.GetExecutingAssembly().Location;
#region
/// <summary>
/// 新建命令按钮
/// </summary>
/// <typeparam name="Command"></typeparam>
/// <param name="text"></param>
/// <param name="image"></param>
/// <param name="largeImage"></param>
/// <param name="toolTip"></param>
/// <param name="longDescription"></param>
/// <returns></returns>
public static PushButtonData NewPushButtonData<Command>(
string text,
Bitmap largeImage = null,
Bitmap image = null,
string toolTip = null,
string longDescription = null
)
where Command : class, IExternalCommand
{
return new(typeof(Command).FullName, text, AddInPath, typeof(Command).FullName)
{
LargeImage = ToBitmapSource(largeImage),
Image = ToBitmapSource(image),
ToolTip = toolTip ?? text,
LongDescription = longDescription
};
}
/// <summary>
/// 新建可控的命令按钮
/// </summary>
/// <typeparam name="Command"></typeparam>
/// <typeparam name="Availablility"></typeparam>
/// <param name="text"></param>
/// <param name="bitmap"></param>
/// <param name="largeBitmap"></param>
/// <param name="toolTip"></param>
/// <param name="longDescription"></param>
/// <returns></returns>
public static PushButtonData NewButtonData<Command, Availablility>(
string text,
Bitmap largeBitmap = null,
Bitmap bitmap = null,
string toolTip = null,
string longDescription = null
)
where Command : class, IExternalCommand
where Availablility : class, IExternalCommandAvailability
{
return new(typeof(Command).FullName, text, AddInPath, typeof(Command).FullName)
{
LargeImage = ToBitmapSource(largeBitmap),
Image = ToBitmapSource(bitmap),
ToolTip = toolTip ?? text,
LongDescription = longDescription,
AvailabilityClassName = typeof(Availablility).FullName
};
}
#endregion
}
}

View File

@@ -0,0 +1,109 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public class Updater : IUpdater
{
/// <summary>
/// 应用程序范围的动态模型更新
/// </summary>
/// <param name="UiApplication"></param>
/// <param name="guid"></param>
/// <param name="execute"></param>
/// <param name="isOptional">true:只在本次生效,下次需要重新注册(不会发出警告)</param>
/// <param name="updateInfo"></param>
public Updater(UIApplication UiApplication, Guid guid, Action<UpdaterData> execute, bool isOptional = true, string updateInfo = "DynamicModelUpdate")
{
updaterId = new UpdaterId(UiApplication.ActiveAddInId, guid);
this.action = execute;
this.updateInfo = updateInfo;
if (!UpdaterRegistry.IsUpdaterRegistered(updaterId))
{
UpdaterRegistry.RegisterUpdater(this, isOptional);
/*
* 对FamilyInstance元素的增加和删除监听
*
* 如果需要关注某些有自己程序创建出来的Element可以把每个Element附上扩展数据
* 然后使用ExtensibleStorageFilter过滤器注册DMU即可
*
* DUM对用户的Ctrl + Z 无效, 可以在DocumentChanged事件中完善该机制
*/
}
}
private readonly Action<UpdaterData> action;
private readonly string updateInfo;
private readonly UpdaterId updaterId;
/// <summary>
/// 当注册的元素发生ChangeType触发时回调的函数
/// </summary>
/// <param name="data"></param>
public void Execute(UpdaterData data)
{
//var rvtDoc = data.GetDocument();
//var idsModified = data.GetModifiedElementIds();
//var idsDeleted = data.GetDeletedElementIds();
//var idsAdded = data.GetAddedElementIds();
//可以根据类型、元素Id、过滤器等等分情况执行更新的操作
action(data);
}
public void Enable()
{
UpdaterRegistry.EnableUpdater(updaterId);
}
public void Disable()
{
if (UpdaterRegistry.IsUpdaterEnabled(updaterId))
{
UpdaterRegistry.DisableUpdater(updaterId);
}
}
#region
public string GetAdditionalInformation()
{
return updateInfo;
}
public ChangePriority GetChangePriority()
{
return ChangePriority.FreeStandingComponents;
}
public UpdaterId GetUpdaterId()
{
return updaterId;
}
public string GetUpdaterName()
{
return updateInfo;
}
#endregion
public void RemoveAllTriggers()
{
UpdaterRegistry.RemoveAllTriggers(updaterId);
}
/// <summary>
/// 添加动态更新的对象和修改的类型的触发器
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="change">修改的类型</param>
public void AddClassTrigger<T>(ChangeType change)
{
var filter = new ElementClassFilter(typeof(T));
UpdaterRegistry.AddTrigger(updaterId, filter, change);
}
public void AddFilterTrigger<T>(ElementFilter filter, ChangeType change)
{
UpdaterRegistry.AddTrigger(updaterId, filter, change);
}
public void UnRegister()
{
UpdaterRegistry.UnregisterUpdater(updaterId);
}
}

View File

@@ -0,0 +1,70 @@
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists;
/// <summary>
/// 创建参数的参数过滤器
/// </summary>
public class ViewFilterAssist
{
/// <summary>
/// 类别视图过滤器
/// </summary>
/// <param name="builtInCategory"></param>
/// <returns></returns>
public static ElementParameterFilter CreateCategoryFilter(BuiltInCategory builtInCategory)
{
ElementParameterFilter commonElementParameterFilter = null;
List<FilterRule> commonFilterRules = new();
List<ElementId> commonCategoryForRules = new() { new(builtInCategory) };
if (FilterCategoryRule.AllCategoriesFilterable(commonCategoryForRules))
{
FilterCategoryRule commonCategoryRule = new(commonCategoryForRules);
commonFilterRules.Add(commonCategoryRule);
commonElementParameterFilter = new ElementParameterFilter(commonFilterRules);
}
return commonElementParameterFilter;
}
/// <summary>
/// 参数的参数过滤器
/// </summary>
/// <param name="builtInCategory"></param>
/// <param name="rules"></param>
/// <returns></returns>
public static ElementParameterFilter CreateParameterFilter(BuiltInCategory builtInCategory, params FilterRule[] rules)
{
ElementParameterFilter commonFilter = null;
List<FilterRule> commonFilterRules = new();
List<ElementId> commonCategoryForRules = new() { new(builtInCategory) };
//判断类别是否可用于过滤器
if (FilterCategoryRule.AllCategoriesFilterable(commonCategoryForRules))
{
//类别规则
FilterCategoryRule commonCategoryRule = new(commonCategoryForRules);
commonFilterRules.Add(commonCategoryRule);
foreach (var rule in rules)
{
commonFilterRules.Add(rule);
}
commonFilter = new ElementParameterFilter(commonFilterRules);
}
return commonFilter;
}
/// <summary>
/// 视图过滤器过滤规则
/// </summary>
/// <param name="parameter"></param>
/// <param name="value">元素Id或整型值</param>
/// <returns></returns>
public static FilterRule CreateParameterFilterRule(BuiltInParameter parameter, int value)
{
var paramId = new ElementId(parameter);
var filterRule = ParameterFilterRuleFactory.CreateEqualsRule(paramId, value);
return filterRule;
}
}

View File

@@ -0,0 +1,51 @@
using System.Windows;
using System.Windows.Media;
namespace ShrlAlgoToolkit.RevitCore.Assists;
public static class VisualAssist
{
public static T FindVisualParent<T>(FrameworkElement element, string name)
where T : FrameworkElement
{
var parentElement = (FrameworkElement)VisualTreeHelper.GetParent(element);
while (parentElement != null)
{
if (parentElement is T parent)
{
if (parentElement.Name == name)
{
return parent;
}
}
parentElement = (FrameworkElement)VisualTreeHelper.GetParent(parentElement);
}
return null;
}
public static T FindVisualChild<T>(FrameworkElement element, string name)
where T : Visual
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var childElement = (FrameworkElement)VisualTreeHelper.GetChild(element, i);
if (childElement is T child)
{
if (childElement.Name == name)
{
return child;
}
}
var descendent = FindVisualChild<T>(childElement, name);
if (descendent != null)
{
return descendent;
}
}
return null;
}
}