Files
SzmediTools/Szmedi.RvKits/ModelManager/LevelSeparateViewModel.cs
2025-09-16 16:06:41 +08:00

812 lines
34 KiB
C#

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using ColorHelper;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace Szmedi.RvKits.ModelManager
{
public partial class LevelSeparateViewModel : ObservableObject
{
private ActionEventHandler handler = new();
//[ObservableProperty]
//private bool toFillLevelName;
private readonly UIDocument uidoc;
//private readonly ProcessEventHandler<KeyValuePair<Element, LevelItem>> process;
[ObservableProperty] private string projectName;
[ObservableProperty] private bool toFillColor;
public LevelSeparateViewModel(UIDocument uidoc)
{
ProjectName = uidoc.Document.Title.Replace(".rvt", string.Empty);
this.uidoc = uidoc;
//process = new ProcessEventHandler<KeyValuePair<Element, LevelItem>>(
// async (uiapp, dict, obj) =>
// {
// var color = new Color(dict.Value.Color.R, dict.Value.Color.G, dict.Value.Color.B);
// if (ToFillLevelName)
// {
// var param = dict.Key.GetParameters("所属楼层").FirstOrDefault();
// if (param != null && !param.IsReadOnly)
// {
// param.Set(dict.Value.LevelName);
// }
// }
// if (ToFillColor)
// {
// dict.Key.SetFillColor(color);
// }
// await Task.Delay(10);
// });
}
public bool? IsAllItemsSelected
{
get
{
var isVisible = Items?.Select(item => item.IsVisible).Distinct().ToList();
return isVisible == null ? false : isVisible.Count() == 1 ? isVisible.Single() : null;
}
set
{
if (value.HasValue)
{
SelectAll(value.Value, Items);
OnPropertyChanged();
}
}
}
public ObservableCollection<LevelItem> Items { get; set; } = [];
private static void SelectAll(bool isVisible, IEnumerable<LevelItem> models)
{
foreach (var item in models)
{
item.IsVisible = isVisible;
}
}
[RelayCommand]
private void ExportToCsv(LevelItem item)
{
FolderBrowserDialog browserWindow = new();
if (browserWindow.ShowDialog())
{
var selectedPath = browserWindow.SelectedPath;
if (!Directory.Exists(selectedPath))
{
MessageBox.Show("文件目录不存在!", "警告");
return;
}
var path = Path.Combine(selectedPath,
$"{ProjectName}-{item.LevelName}-{DateTime.Now:yyyy-mm-dd-HH-mm}.csv");
var sb = new StringBuilder();
sb.AppendLine("元素Id,族,类型,");
foreach (var instance in item.Elements)
{
var elem = uidoc.Document.GetElement(instance.GetTypeId());
var familyName = elem switch
{
ElementType elementType => elementType.FamilyName,
null => instance.Name,
_ => elem.Name
};
sb.AppendLine($"{instance.Id},{familyName},{instance.Name},");
}
File.WriteAllText(path, sb.ToString(), Encoding.UTF8);
var result = MessageBox.Show(
"是否打开导出的文件",
"导出完成",
MessageBoxButton.OKCancel,
MessageBoxImage.Information);
if (result == MessageBoxResult.OK)
{
Process.Start(path);
}
}
}
/// <summary>
/// 楼层划分
/// </summary>
[RelayCommand]
private async Task GroupByLevel()
{
await Task.Delay(300);
var result = MessageBox.Show("此操作会覆盖已经设置所属楼层的构件参数,是否知晓并继续?", "提示", MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.No)
{
return;
}
Items.Clear();
var unknownLevel = new LevelItem(handler)
{
LevelName = "{未知楼层}",
LevelId = ElementId.InvalidElementId,
Elements = [],
Color = ColorName.Red.ToRgb()
};
var doc = uidoc.Document;
var levelOutlines = doc.GetLevelRanges();
var n = 1;
//对比标高,得到实际分层位置
var allModels = doc.OfAllModelCollector();
if (allModels == null)
{
MessageBox.Show("没有找到模型元素", "错误");
return;
}
Debug.WriteLine($"模型总数:{allModels.Count()}");
foreach (var keyPair in levelOutlines)
{
//Debug.WriteLine($"---------------------");
BoundingBoxIsInsideFilter insideFilter = new(keyPair.Value);
//所有模型中得到在标高范围内的元素
//除了墙、梁、板、柱、楼梯
var insideList = doc.OfAllModelCollector()
.WherePasses(insideFilter)
.Where(
e => e is not (Stairs or Wall) &&
e.Category.ToBuiltInCategory() is not (BuiltInCategory.OST_StructuralFraming
or BuiltInCategory.OST_Floors
or BuiltInCategory.OST_StructuralColumns
or BuiltInCategory.OST_Columns));
var level = keyPair.Key;
//Debug.WriteLine($"{level.Name}上的范围元素:{insideList.Count()}");
//var ids = insideList.Where(dict => dict.get_BoundingBox(null) != null);
LevelItem item = new(handler)
{
LevelName = level.Name,
LevelId = level.Id,
Elements = new ObservableCollection<Element>(insideList),
Color = ((ColorName)n).ToRgb()
};
var ids = insideList.Select(e => e.Id);
//排除范围内的元素
if (ids.Any())
{
allModels.Excluding(ids.ToList());
}
Items.Add(item);
//Debug.WriteLine($"{level.Name}上合计构件数:{item.Elements.Count}");
n++;
}
foreach (var item in Items)
{
//所有模型中通过模型自身的主体、参照获取标高,墙、梁、板、柱、楼梯
var checkedList = new List<ElementId>();
foreach (var model in allModels)
{
var modelLevelId = model.GetLevelId();
if (modelLevelId == item.LevelId)
{
item.Elements.Add(model);
checkedList.Add(model.Id);
}
else
{
if (model is FamilyInstance ins && ins.Host != null)
{
var hostLevel = ins.Host.GetLevelId();
if (item.LevelId == hostLevel)
{
item.Elements.Add(model);
checkedList.Add(model.Id);
}
}
if (model is Railing railing && railing.HasHost)
{
var host = doc.GetElement(railing.HostId);
var hostLevel = host.GetLevelId();
if (item.LevelId == hostLevel)
{
item.Elements.Add(model);
checkedList.Add(model.Id);
}
}
}
}
Debug.WriteLine($"{item.LevelName}上的特殊构件:{checkedList.Count}");
if (checkedList.Any())
{
allModels.Excluding(checkedList);
}
}
//存在未知标高的构件时
if (allModels.Any())
{
unknownLevel.Elements = new ObservableCollection<Element>(allModels);
Items.Add(unknownLevel);
}
if (ToFillColor)
{
handler.Raise(
uiapp =>
{
uiapp.ActiveUIDocument.Document
.Invoke(
ts =>
{
foreach (var item in Items)
{
var color = new Color(item.Color.R, item.Color.G, item.Color.B);
foreach (var elem in item.Elements)
{
elem.SetFillColor(color);
}
}
},
"填充颜色");
});
}
//Export();
//if (ToFillLevelName || ToFillColor)
//{
// var manager = new ProgressBarManager<KeyValuePair<Element, LevelItem>>(process, dict);
// manager.ProgressModeless();
//}
}
private void Export()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
$"{ProjectName}-{DateTime.Now:yyyy-mm-dd-HH-mm}.csv");
var sb = new StringBuilder();
sb.AppendLine("楼层,元素Id,族,类型,");
foreach (var item in Items)
{
foreach (var instance in item.Elements)
{
var elem = uidoc.Document.GetElement(instance.GetTypeId());
var familyName = elem switch
{
ElementType elementType => elementType.FamilyName,
null => instance.Name,
_ => elem.Name
};
sb.AppendLine($"{item.LevelName},{instance.Id},{familyName},{instance.Name},");
}
}
File.WriteAllText(path, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// 楼层划分
/// </summary>
//[RelayCommand]
//private void GroupByLevelBackup()
//{
// Items.Clear();
// handler.Raise(
// uiapp =>
// {
// var unknownLevel = new LevelItem(handler)
// {
// LevelName = "<未知楼层>",
// LevelId = ElementId.InvalidElementId,
// Count = 0,
// Elements = [],
// Color = ColorName.Red.ToRgb(),
// };
// var doc = uiapp.ActiveUIDocument.Document;
// var levelOutlines = doc.GetLevelRanges();
// doc.Invoke(
// ts =>
// {
// var n = 1;
// //对比标高,得到实际分层位置
// var allModels = doc.OfModelCollector();
// foreach (var keyPair in levelOutlines)
// {
// BoundingBoxIsInsideFilter insideFilter = new(keyPair.Value);
// //所有模型中得到在标高范围内的元素
// //除了墙、梁、板、柱、楼梯
// var insideList = doc.OfModelCollector()
// .WherePasses(insideFilter)
// .Where(
// e => e is not (Stairs or Wall) &&
// e.Category.ToBuiltInCategory() is not (BuiltInCategory.OST_StructuralFraming
// or BuiltInCategory.OST_Floors
// or BuiltInCategory.OST_StructuralColumns
// or BuiltInCategory.OST_Columns));
// var level = keyPair.Key;
// //var ids = insideList.Where(dict => dict.get_BoundingBox(null) != null);
// LevelItem level = new(handler)
// {
// LevelName = level.Name,
// LevelId = level.Id,
// Count = insideList.Count(),
// Elements = new ObservableCollection<Element>(insideList),
// Color = ((ColorHelper.ColorName)n).ToRgb(),
// };
// var ids = insideList.Select(e => e.Id);
// //排除范围内的元素
// if (ids.Any())
// {
// allModels.Excluding(ids.ToList());
// }
// //所有模型中通过模型自身的主体、参照获取标高,墙、梁、板、柱、楼梯
// var checkedList = new List<ElementId>();
// foreach (var level in allModels)
// {
// var modelLevelId = level.GetLevelId();
// if (modelLevelId == level.LevelId)
// {
// level.Elements.Add(level);
// checkedList.Add(level.Id);
// }
// else
// {
// if (level is FamilyInstance roof && roof.Host != null)
// {
// var hostLevel = roof.Host.GetLevelId();
// if (level.LevelId == hostLevel)
// {
// level.Elements.Add(level);
// checkedList.Add(level.Id);
// }
// }
// if (level is Railing railing && railing.HasHost)
// {
// var host = doc.GetElement(railing.HostId);
// var hostLevel = host.GetLevelId();
// if (level.LevelId == hostLevel)
// {
// level.Elements.Add(level);
// checkedList.Add(level.Id);
// }
// }
// }
// }
// try
// {
// if (checkedList.Any())
// {
// allModels.Excluding(checkedList);
// }
// }
// catch (Exception)
// {
// throw;
// }
// level.Count = level.Elements.Count;
// Items.Add(level);
// if (ToFillLevelName || ToFillColor)
// {
// doc.InvokeSub(
// sub =>
// {
// var color = new Color(level.Color.R, level.Color.G, level.Color.B);
// foreach (var dict in level.Elements)
// {
// if (ToFillLevelName)
// {
// dict.GetParameters("所属楼层").FirstOrDefault()?.Set(level.LevelName);
// }
// if (ToFillColor)
// {
// dict.SetFillColor(color);
// }
// }
// });
// }
// n++;
// }
// //存在未知标高的构件时
// if (allModels.Any())
// {
// unknownLevel.Elements = new ObservableCollection<Element>(allModels);
// unknownLevel.Count = unknownLevel.Elements.Count;
// Items.Add(unknownLevel);
// }
// },
// "根据楼层划分");
// });
//}
[RelayCommand]
private void ReadCurrentModel()
{
//await Task.Delay(300);
Items.Clear();
var allModels = uidoc.Document
.OfAllModelCollector();
if (allModels == null)
{
MessageBox.Show("当前项目中没有模型!", "提示");
return;
}
Debug.WriteLine($"模型总数:{allModels?.Count()}");
var instances = allModels.ToList()
.Where(e => e is FamilyInstance ins && ins.GetSubComponentIds().Count > 0)
.Cast<FamilyInstance>().ToList();
var curtainWalls = allModels.ToList().Where(e => e is Wall wall && wall.CurtainGrid != null).Cast<Wall>().ToList();
var curtainRoofs = allModels.ToList().Where(e => e is FootPrintRoof roof && roof.CurtainGrids != null)
.Cast<FootPrintRoof>().ToList();
var stairs = allModels.ToList().Where(e => e is Stairs).Cast<Stairs>().ToList();
var railings = allModels.ToList().Where(e => e is Railing).Cast<Railing>().ToList();
//var railings = allModels.ToList().Where(e => e is Railing).Cast<Wall>();
handler.Raise(
uiapp =>
{
var doc = uiapp.ActiveUIDocument.Document;
doc.Invoke(
ts =>
{
//共享嵌套族处理
foreach (var ins in instances)
{
var param = ins.GetParameters("所属楼层").FirstOrDefault();
var value = string.Empty;
if (param != null && !param.IsReadOnly)
{
value = param.AsString();
}
foreach (var id in ins.GetSubComponentIds())
{
var sub = doc.GetElement(id);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
}
foreach (var roof in curtainRoofs)
{
var param = roof.GetParameters("所属楼层").FirstOrDefault();
var value = string.Empty;
if (param != null && !param.IsReadOnly)
{
value = param.AsString();
}
foreach (CurtainGrid grid in roof.CurtainGrids)
{
foreach (var id in grid.GetMullionIds())
{
var sub = doc.GetElement(id);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
foreach (var id in grid.GetPanelIds())
{
var sub = doc.GetElement(id);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
}
}
//幕墙子类别处理
foreach (var wall in curtainWalls)
{
var param = wall.GetParameters("所属楼层").FirstOrDefault();
var value = string.Empty;
if (param != null && !param.IsReadOnly)
{
value = param.AsString();
}
foreach (var id in wall.CurtainGrid.GetMullionIds())
{
var sub = doc.GetElement(id);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
foreach (var id in wall.CurtainGrid.GetPanelIds())
{
var sub = doc.GetElement(id);
if (sub is Panel panel && panel.FindHostPanel() != ElementId.InvalidElementId)
{
var hostId = panel.FindHostPanel();
var host = doc.GetElement(hostId);
var hostP = host.GetParameters("所属楼层").FirstOrDefault();
if (hostP != null && !hostP.IsReadOnly)
{
var b = hostP.Set(value);
}
}
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
var b = p.Set(value);
}
}
}
foreach (var stair in stairs)
{
var runs = stair.GetStairsRuns();
var landings = stair.GetStairsLandings();
var supports = stair.GetStairsSupports();
var param = stair.GetParameters("所属楼层").FirstOrDefault();
var value = string.Empty;
if (param != null && !param.IsReadOnly)
{
value = param.AsString();
}
SetSubComponent(doc, runs, value);
SetSubComponent(doc, landings, value);
SetSubComponent(doc, supports, value);
}
foreach (var railing in railings)
{
var param = railing.GetParameters("所属楼层").FirstOrDefault();
var value = string.Empty;
if (param != null && !param.IsReadOnly)
{
value = param.AsString();
}
var topRailId = railing.TopRail;
if (topRailId != ElementId.InvalidElementId)
{
var sub = doc.GetElement(topRailId);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
}
},
"更新子构件的所属楼层");
var groups = allModels.GroupBy(e =>
{
var param = e.GetParameters("所属楼层").FirstOrDefault();
var value = param?.AsString();
if (string.IsNullOrEmpty(value))
{
return null;
}
return param.AsString();
});
var n = 1;
handler = new ActionEventHandler();
foreach (var group in groups)
{
//Debug.WriteLine($"{group.Key}模型数量:{group.Count()}");
//var instances = group.ToList();
LevelItem item =
new(handler)
{
LevelName = group.Key ?? "{未知楼层}",
Elements = [.. group],
Color = ((ColorName)n).ToRgb()
};
Items.Add(item);
n++;
}
if (ToFillColor)
{
uiapp.ActiveUIDocument.Document
.Invoke(
ts =>
{
foreach (var item in Items)
{
var color = new Color(item.Color.R, item.Color.G, item.Color.B);
foreach (var elem in item.Elements)
{
elem.SetFillColor(color);
}
}
},
"填充颜色");
}
});
//Export();
}
private static void SetSubComponent(Document doc, ICollection<ElementId> ids, string value)
{
foreach (var id in ids)
{
var sub = doc.GetElement(id);
var p = sub.GetParameters("所属楼层").FirstOrDefault();
if (p != null && !p.IsReadOnly)
{
p.Set(value);
}
}
}
[RelayCommand]
private void SetColorOverride(object obj)
{
handler.Raise(
uiapp =>
{
if (obj is LevelItem levelItem)
{
var doc = uiapp.ActiveUIDocument.Document;
var color = new Color(levelItem.Color.R, levelItem.Color.G, levelItem.Color.B);
doc.Invoke(
ts =>
{
foreach (var elem in levelItem.Elements)
{
elem.SetFillColor(color);
}
},
"填充颜色");
}
});
}
[RelayCommand]
private void ResetColorOverride()
{
handler.Raise(
uiapp =>
{
var doc = uiapp.ActiveUIDocument.Document;
doc.Invoke(
ts =>
{
OverrideGraphicSettings overrideGraphicSettings = new();
var elems = doc.OfAllModelCollector();
if (elems == null)
{
MessageBox.Show("没有找到模型元素", "错误");
return;
}
foreach (var elem in elems)
{
doc.ActiveView.SetElementOverrides(elem.Id, overrideGraphicSettings);
doc.Regenerate();
}
},
"重置外观");
});
}
[RelayCommand]
private void SelectElements(object obj)
{
if (obj is LevelItem item)
{
uidoc.Selection.SetElementIds(item.Elements.Select(e => e.Id).ToList());
}
}
}
public partial class LevelItem : ObservableObject
{
private readonly ActionEventHandler handler;
[ObservableProperty] private RGB color;
[ObservableProperty] private bool isVisible = true;
[ObservableProperty] private string levelName;
public LevelItem(ActionEventHandler handler)
{
this.handler = handler;
}
public ObservableCollection<Element> Elements { get; set; } = [];
public ElementId LevelId { get; set; } = ElementId.InvalidElementId;
partial void OnIsVisibleChanged(bool oldValue, bool newValue)
{
if (oldValue != newValue)
{
handler.Raise(
uiapp =>
{
var doc = uiapp.ActiveUIDocument.Document;
doc.Invoke(
_ =>
{
if (newValue)
{
doc.ActiveView
.UnhideElements(
Elements.Where(e => e.IsHidden(doc.ActiveView)).Select(e => e.Id).ToList());
}
else
{
doc.ActiveView
.HideElements(
Elements.Where(e => e.CanBeHidden(doc.ActiveView))
.Select(e => e.Id)
.ToList());
}
},
$"显隐{LevelName}");
});
}
}
partial void OnLevelNameChanged(string oldValue, string newValue)
{
if (oldValue != newValue && newValue != "{未知楼层}")
{
handler.Raise(
uiapp =>
{
var doc = uiapp.ActiveUIDocument.Document;
doc.Invoke(
_ =>
{
foreach (var elem in Elements)
{
var param = elem.GetParameters("所属楼层").FirstOrDefault();
if (param != null && !param.IsReadOnly)
{
param.Set(newValue);
}
}
},
$"所属楼层{oldValue}->{newValue}");
});
}
}
}
}