修改命名空间
This commit is contained in:
67
ShrlAlgoToolkit.RevitCore/Assists/ColorAssist.cs
Normal file
67
ShrlAlgoToolkit.RevitCore/Assists/ColorAssist.cs
Normal 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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
37
ShrlAlgoToolkit.RevitCore/Assists/DwgElementSelection.cs
Normal file
37
ShrlAlgoToolkit.RevitCore/Assists/DwgElementSelection.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
15
ShrlAlgoToolkit.RevitCore/Assists/ElementOrientation.cs
Normal file
15
ShrlAlgoToolkit.RevitCore/Assists/ElementOrientation.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace ShrlAlgoToolkit.RevitCore.Assists
|
||||
{
|
||||
public enum ElementOrientation
|
||||
{
|
||||
Horizontal,
|
||||
|
||||
Vertical,
|
||||
|
||||
CloseToHorizontal,
|
||||
|
||||
CloseToVertical,
|
||||
|
||||
Undefined
|
||||
}
|
||||
}
|
||||
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInFamilyDocument.cs
Normal file
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInFamilyDocument.cs
Normal 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;
|
||||
}
|
||||
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInView3D.cs
Normal file
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInView3D.cs
Normal 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;
|
||||
}
|
||||
16
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewPlan.cs
Normal file
16
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewPlan.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewSection.cs
Normal file
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewSection.cs
Normal 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;
|
||||
}
|
||||
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewSheet.cs
Normal file
13
ShrlAlgoToolkit.RevitCore/Assists/EnableInViewSheet.cs
Normal 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;
|
||||
}
|
||||
29
ShrlAlgoToolkit.RevitCore/Assists/EncryptParameters.cs
Normal file
29
ShrlAlgoToolkit.RevitCore/Assists/EncryptParameters.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
ShrlAlgoToolkit.RevitCore/Assists/ExtremeWallVariant.cs
Normal file
12
ShrlAlgoToolkit.RevitCore/Assists/ExtremeWallVariant.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ShrlAlgoToolkit.RevitCore.Assists;
|
||||
|
||||
public enum ExtremeWallVariant
|
||||
{
|
||||
Left,
|
||||
|
||||
Right,
|
||||
|
||||
Top,
|
||||
|
||||
Bottom
|
||||
}
|
||||
53
ShrlAlgoToolkit.RevitCore/Assists/FailuresPreProcessor.cs
Normal file
53
ShrlAlgoToolkit.RevitCore/Assists/FailuresPreProcessor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
21
ShrlAlgoToolkit.RevitCore/Assists/FuncFilter.cs
Normal file
21
ShrlAlgoToolkit.RevitCore/Assists/FuncFilter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
96
ShrlAlgoToolkit.RevitCore/Assists/FunctionByGuidLine.cs
Normal file
96
ShrlAlgoToolkit.RevitCore/Assists/FunctionByGuidLine.cs
Normal 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
|
||||
}
|
||||
21
ShrlAlgoToolkit.RevitCore/Assists/GenericFilter.cs
Normal file
21
ShrlAlgoToolkit.RevitCore/Assists/GenericFilter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
272
ShrlAlgoToolkit.RevitCore/Assists/GeometryAssist.cs
Normal file
272
ShrlAlgoToolkit.RevitCore/Assists/GeometryAssist.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
ShrlAlgoToolkit.RevitCore/Assists/HasActiveDocument.cs
Normal file
11
ShrlAlgoToolkit.RevitCore/Assists/HasActiveDocument.cs
Normal 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;
|
||||
}
|
||||
710
ShrlAlgoToolkit.RevitCore/Assists/KeyIntPtrHelper.cs
Normal file
710
ShrlAlgoToolkit.RevitCore/Assists/KeyIntPtrHelper.cs
Normal 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,如果为松开 则设置成2,KEYEVENT_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
|
||||
}
|
||||
318
ShrlAlgoToolkit.RevitCore/Assists/KeyboardShortcutService.cs
Normal file
318
ShrlAlgoToolkit.RevitCore/Assists/KeyboardShortcutService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
ShrlAlgoToolkit.RevitCore/Assists/NativeModule.cs
Normal file
55
ShrlAlgoToolkit.RevitCore/Assists/NativeModule.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
18
ShrlAlgoToolkit.RevitCore/Assists/OnProjectDocument.cs
Normal file
18
ShrlAlgoToolkit.RevitCore/Assists/OnProjectDocument.cs
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
183
ShrlAlgoToolkit.RevitCore/Assists/OptionsBarAssist.cs
Normal file
183
ShrlAlgoToolkit.RevitCore/Assists/OptionsBarAssist.cs
Normal 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
|
||||
1241
ShrlAlgoToolkit.RevitCore/Assists/ParameterAssist.cs
Normal file
1241
ShrlAlgoToolkit.RevitCore/Assists/ParameterAssist.cs
Normal file
File diff suppressed because it is too large
Load Diff
520
ShrlAlgoToolkit.RevitCore/Assists/PublishAssist.cs
Normal file
520
ShrlAlgoToolkit.RevitCore/Assists/PublishAssist.cs
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
649
ShrlAlgoToolkit.RevitCore/Assists/RevitFileAssist.cs
Normal file
649
ShrlAlgoToolkit.RevitCore/Assists/RevitFileAssist.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
179
ShrlAlgoToolkit.RevitCore/Assists/RevitIniAssist.cs
Normal file
179
ShrlAlgoToolkit.RevitCore/Assists/RevitIniAssist.cs
Normal 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 = 0(0 = 以最后一次任务期间使用的窗口大小启动 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ShrlAlgoToolkit.RevitCore/Assists/ShortcutRepAttribute.cs
Normal file
16
ShrlAlgoToolkit.RevitCore/Assists/ShortcutRepAttribute.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
315
ShrlAlgoToolkit.RevitCore/Assists/SpatialAssist.cs
Normal file
315
ShrlAlgoToolkit.RevitCore/Assists/SpatialAssist.cs
Normal 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;
|
||||
}
|
||||
38
ShrlAlgoToolkit.RevitCore/Assists/StatusBarAssist.cs
Normal file
38
ShrlAlgoToolkit.RevitCore/Assists/StatusBarAssist.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
149
ShrlAlgoToolkit.RevitCore/Assists/UIAssist.cs
Normal file
149
ShrlAlgoToolkit.RevitCore/Assists/UIAssist.cs
Normal 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
|
||||
}
|
||||
}
|
||||
109
ShrlAlgoToolkit.RevitCore/Assists/Updater.cs
Normal file
109
ShrlAlgoToolkit.RevitCore/Assists/Updater.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
70
ShrlAlgoToolkit.RevitCore/Assists/ViewFilterAssist.cs
Normal file
70
ShrlAlgoToolkit.RevitCore/Assists/ViewFilterAssist.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
51
ShrlAlgoToolkit.RevitCore/Assists/VisualAssist.cs
Normal file
51
ShrlAlgoToolkit.RevitCore/Assists/VisualAssist.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user