如何保持Revit插件中_start_state字典值不随文档修改变更?
问题:Revit中存储的初始Location随元素修改而变更
需求与问题
- 需求:
- Revit打开文档时,将所有追踪元素的
ElementId与Location存入_start_state字典 - 文档发生变更时,通过修改元素的
ElementId从_start_state中获取原始Location
- Revit打开文档时,将所有追踪元素的
- 问题:触发
CtrlApp_DocumentChanged事件(元素被修改)时,_start_state中对应ElementId的Location值会同步变化,无法保留打开文档时的初始状态
相关代码
Dictionary<ElementId, Location> _start_state; List<ElementId> startKeys; public void CtrlApp_DocumentOpened(object sender, DocumentOpenedEventArgs e) { // 获取当前文档 Document doc = e.Document; IEnumerable<Element> a = GetTrackedElements(doc); Dictionary<ElementId, Location> start_state; start_state = GetSnapshot(a); _start_state = new Dictionary<ElementId, Location>(start_state); startKeys = _start_state.Keys.ToList(); } public void CtrlApp_DocumentChanged(object sender, DocumentChangedEventArgs e) { ICollection<ElementId> modifiedElem = e.GetModifiedElementIds(); foreach (ElementId id in modifiedElem) { if (startKeys.Contains(id)) // 此处获取到的是修改后的Location而非原始值 { Dictionary<ElementId, Location> dict = new Dictionary<ElementId, Location>(); List<Location> locList = new List<Location>(); locList.Add(_start_state[id]); foreach (Location loc in locList) { send_baseLocation(loc); } } } }
可行解决方案建议
核心原因
问题本质是Location是Revit元素的绑定实例,属于引用类型。你存储的是该实例的引用,当元素位置修改时,Location实例的内部属性(如坐标、曲线)会被Revit更新,导致字典中存储的引用指向的对象也跟着变化。
方案1:提取并存储位置的不可变数据(推荐)
不要直接存储Location对象,而是提取其内部的具体值(值类型或克隆后的不可变对象),用自定义类保存:
- 定义存储位置信息的类:
public class SavedLocation { // 存储点位置(XYZ是值类型,赋值时会复制) public XYZ Point { get; set; } // 存储曲线位置(克隆曲线避免引用原对象) public Curve Curve { get; set; } // 标记当前位置类型 public LocationType Type { get; set; } } public enum LocationType { Point, Curve }
- 修改
GetSnapshot方法,转换并保存不可变数据:
public Dictionary<ElementId, SavedLocation> GetSnapshot(IEnumerable<Element> elements) { var snapshot = new Dictionary<ElementId, SavedLocation>(); foreach (var elem in elements) { var loc = elem.Location; if (loc is LocationPoint pointLoc) { snapshot.Add(elem.Id, new SavedLocation { Type = LocationType.Point, Point = pointLoc.Point // XYZ是值类型,直接复制 }); } else if (loc is LocationCurve curveLoc) { snapshot.Add(elem.Id, new SavedLocation { Type = LocationType.Curve, Curve = curveLoc.Curve.Clone() // 克隆曲线,生成独立实例 }); } } return snapshot; }
- 更新全局字典类型及事件处理逻辑:
// 修改全局字典类型 Dictionary<ElementId, SavedLocation> _start_state; // CtrlApp_DocumentOpened中对应修改 _start_state = GetSnapshot(a); // CtrlApp_DocumentChanged中获取原始位置 foreach (ElementId id in modifiedElem) { if (_start_state.ContainsKey(id)) { var savedLoc = _start_state[id]; if (savedLoc.Type == LocationType.Point) { // 处理点位置 send_baseLocation(savedLoc.Point); } else { // 处理曲线位置 send_baseLocation(savedLoc.Curve); } } }
方案2:结合ImmutableDictionary(仅辅助保证字典结构不可变)
单独使用ImmutableDictionary无法解决问题,因为它仅保证字典的键值对结构不可修改,但存储的Location引用仍会指向可变的Revit实例。必须配合方案1的值类型存储一起使用:
// 使用ImmutableDictionary存储SavedLocation ImmutableDictionary<ElementId, SavedLocation> _start_state; // 初始化时转换为不可变字典 _start_state = GetSnapshot(a).ToImmutableDictionary();
这样既保证字典结构不会被意外修改,又保证存储的位置数据是独立的初始值。
内容的提问来源于stack exchange,提问作者VCCCCxz
相关产品推荐
相关产品推荐

