添加项目文件。

This commit is contained in:
GG Z
2024-09-22 11:05:41 +08:00
parent fb5d55723a
commit 49ceaae6a8
764 changed files with 78850 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
using System.Windows;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI;
using Nice3point.Revit.Toolkit.External;
namespace Sai.RvKits.RvFamily.FamilyLibrary;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class FamilyLibraryCmd : ExternalCommand
{
public override void Execute()
{
WinDialogHelper.ShowModeless<FamilyLibraryView>(new FamilyLibraryViewModel());
}
}

View File

@@ -0,0 +1,315 @@
<ex:FluentWindowEx
x:Class="Sai.RvKits.RvFamily.FamilyLibrary.FamilyLibraryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ex="https://github.com/sherlockforrest/Wpf.Ui.Extend"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:Sai.RvKits.RvFamily.FamilyLibrary"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="本地族库"
Width="1050"
Height="600"
MinWidth="1050"
MinHeight="600"
d:DataContext="{d:DesignInstance Type=local:FamilyLibraryViewModel}"
ResizeMode="CanResizeWithGrip"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Sai.RvKits;component/WPFUI.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="220" />
</Grid.ColumnDefinitions>
<ui:Button
Grid.Row="0"
Margin="5"
HorizontalAlignment="Stretch"
Command="{Binding SelectPathCommand}"
Content="打开族目录"
FontFamily="{StaticResource BoxIcons}"
Icon="{ui:FontIcon Glyph=&#xEADF;,
FontFamily={StaticResource BoxIcons}}"
ToolTip="{Binding FamilyPath, Mode=OneWay, StringFormat=族目录:{}{0}}" />
<TreeView
x:Name="FolderTreeView"
Grid.Row="1"
d:ItemsSource="{d:SampleData}"
ItemsSource="{Binding FolderTreeViewItems}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectDirTreeNodeCommand}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type TreeView}, Mode=FindAncestor}}" />
<!--<behaviors:InvokeCommandAction Command="{Binding SetSelectedItem}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TreeView}}" />-->
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<GridSplitter
Grid.Row="1"
Grid.Column="1"
Width="1"
HorizontalAlignment="Center"
ShowsPreview="True" />
<ui:TextBox
x:Name="SearchBox"
Grid.Row="0"
Grid.Column="2"
Margin="5"
Cursor="IBeam"
PlaceholderText="请输入搜索关键词">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<i:InvokeCommandAction Command="{Binding SearchFamilyCommand}" CommandParameter="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}, Mode=FindAncestor}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ui:TextBox>
<ui:ListView
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="2"
d:ItemsSource="{d:SampleData}"
ItemsSource="{Binding FamilyPageListViewItems}"
SelectedItem="{Binding SelectedFamily}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding LvSelectionChangedCommand}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type ListView}, Mode=FindAncestor}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<!--<ListView.View>
<GridView>
<GridViewColumn Header="缩略图" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding ImageData}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Title}" Header="名称" />
<GridViewColumn DisplayMemberBinding="{Binding Path}" Header="路径" />
<GridViewColumn DisplayMemberBinding="{Binding RevitVersion}" Header="版本" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button
Command="{Binding DataContext.ImportFamilyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding}"
Content="&#xedb7;载入"
FontFamily="{StaticResource BoxIcons}"
ToolTip="将族载入到当前项目" />
<Button
Command="{Binding DataContext.LocationFamilyFileCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding}"
Content="&#xea90;浏览"
FontFamily="{StaticResource BoxIcons}"
ToolTip="在资源管理器中浏览文件" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>-->
<ui:ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border
x:Name="border"
Margin="5"
Background="{TemplateBinding Background}"
BorderBrush="Transparent"
BorderThickness="2"
CornerRadius="5">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="border" Property="BorderBrush" Value="Gray" />
</MultiTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="LightSkyBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ui:ListView.ItemContainerStyle>
<ui:ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType={x:Type ScrollContentPresenter}}}" />
</ItemsPanelTemplate>
</ui:ListView.ItemsPanel>
<ui:ListView.ItemTemplate>
<DataTemplate>
<ui:Card Padding="0">
<Grid Width="170" Height="210">
<Grid.RowDefinitions>
<RowDefinition Height="170" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
Width="128"
Height="128"
Source="{Binding ImageData}"
Stretch="Uniform"
ToolTip="{Binding ToolTip}" />
<StackPanel
Grid.Row="0"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<ui:Button
Appearance="Primary"
Command="{Binding DataContext.ImportFamilyCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"
CommandParameter="{Binding}"
Content="&#xEDB7;载入"
FontFamily="{StaticResource BoxIcons}"
ToolTip="将族载入到当前项目" />
<ui:Button
Appearance="Primary"
Command="{Binding DataContext.LocationFamilyFileCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"
CommandParameter="{Binding}"
Content="&#xEA90;定位"
FontFamily="{StaticResource BoxIcons}"
ToolTip="在资源管理器中浏览文件" />
<!--
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Mode=Self}}" 等价于 CommandParameter="{Binding}"
-->
</StackPanel>
<TextBlock
Grid.Row="1"
Width="128"
Text="{Binding Title}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow" />
</Grid>
</ui:Card>
</DataTemplate>
</ui:ListView.ItemTemplate>
<ui:ListView.Template>
<ControlTemplate TargetType="{x:Type ListView}">
<ScrollViewer
x:Name="scr"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ScrollChanged">
<i:InvokeCommandAction Command="{Binding LvScrollCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}, Mode=FindAncestor}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<WrapPanel IsItemsHost="True" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</ControlTemplate>
</ui:ListView.Template>
</ui:ListView>
<GridSplitter
Grid.Row="1"
Grid.Column="3"
Width="1"
HorizontalAlignment="Center"
ShowsPreview="True" />
<ui:DataGrid
Grid.Row="1"
Grid.Column="4"
Margin="5"
d:ItemsSource="{d:SampleData}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
ItemsSource="{Binding SelectedItem.Parameters, ElementName=CbbSymbols, Mode=TwoWay}">
<ui:DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="属性名" />
<DataGridTextColumn Binding="{Binding Value}" Header="属性值" />
</ui:DataGrid.Columns>
</ui:DataGrid>
<!--<StackPanel
Grid.Row="1"
Grid.Column="2"
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<hc:ButtonGroup Style="{StaticResource ButtonGroupSolid}">
<RadioButton IsChecked="True" Style="{StaticResource RadioGroupItemHorizontalFirst}">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="Checked">
<hc:EventToCommand Command="{Binding ModifyLayout}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=RadioButton}}" />
</hc:EventTrigger>
</hc:Interaction.Triggers>
</RadioButton>
<RadioButton Style="{StaticResource RadioGroupItemHorizontalLast}">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="Checked">
<hc:EventToCommand Command="{Binding ModifyLayout}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=RadioButton}}" />
</hc:EventTrigger>
</hc:Interaction.Triggers>
</RadioButton>
</hc:ButtonGroup>
</StackPanel>-->
<ex:StackPanelEx
Grid.Row="0"
Grid.Column="4"
Orientation="Horizontal">
<ex:ComboBoxEx
x:Name="CbbSymbols"
ex:StackPanelEx.Fill="Fill"
DisplayMemberPath="Name"
ItemsSource="{Binding SymbolTypes, Mode=TwoWay}"
PlaceholderText="选择族类型"
SelectedItem="{Binding SelectedSymbolType, UpdateSourceTrigger=PropertyChanged}" />
<Button
Grid.Column="1"
Margin="5"
ex:StackPanelEx.Fill="Auto"
Command="{Binding ImportFamilySymbolCommand}"
Content="&#xEB1F;"
FontFamily="{StaticResource BoxIcons}"
ToolTip="载入类型" />
</ex:StackPanelEx>
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="5"
Text="{Binding FamilyCount, Mode=OneWay, StringFormat=共计{}{0}个}" />
<ui:InfoBar
Grid.Row="2"
Grid.Column="2"
IsOpen="{Binding ActiveSnackbar}"
Message="{Binding Message}" />
<TextBlock
Grid.Row="2"
Grid.Column="4"
Text="{Binding RevitVersion, Mode=OneWay, StringFormat=当前文件版本Revit \{0\}}" />
</Grid>
</ex:FluentWindowEx>

View File

@@ -0,0 +1,13 @@
namespace Sai.RvKits.RvFamily.FamilyLibrary
{
/// <summary>
/// LocalFamily.xaml 的交互逻辑.
/// </summary>
public partial class FamilyLibraryView
{
public FamilyLibraryView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,479 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Autodesk.Revit.DB;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FuzzySharp;
using Nice3point.Revit.Toolkit.External.Handlers;
using Wpf.Ui.Extend.Controls;
using Settings = Sai.RvKits.Properties.Settings;
namespace Sai.RvKits.RvFamily.FamilyLibrary;
public partial class FamilyLibraryViewModel : ObservableObject
{
public FamilyLibraryViewModel()
{
loadFamilyHandler = new ActionEventHandler();
loadFamilySymbolHandler = new ActionEventHandler();
#if REVIT2018
familyPath = Settings.Default.FamilyPath_2018;
#elif REVIT2019
familyPath = Settings.Default.FamilyPath_2019;
#elif REVIT2020
familyPath = Settings.Default.FamilyPath_2020;
#elif REVIT2021
familyPath = Settings.Default.FamilyPath_2021;
#endif
PageSize = 20;
Initialize();
}
private readonly ActionEventHandler loadFamilyHandler;
private readonly ActionEventHandler loadFamilySymbolHandler;
[ObservableProperty]
private string familyPath;
[ObservableProperty]
private int pageCount;
[ObservableProperty]
private int pageSize;
[ObservableProperty]
private string revitVersion;
[ObservableProperty]
private FamilyModel selectedFamily;
[ObservableProperty]
private FamilySymbolType selectedSymbolType;
[ObservableProperty]
private List<FamilySymbolType> symbolTypes;
[ObservableProperty]
private bool activeSnackbar;
[ObservableProperty]
private Wpf.Ui.Controls.InfoBar messageQueue = new();
[ObservableProperty]
private string message;
partial void OnMessageChanged(string value)
{
ActiveSnackbar = true;
}
public int FamilyCount => FamilyDataSource.Count;
/// <summary>
/// 选择树路径时的源
/// </summary>
public ObservableCollection<FamilyModel> FamilyDataSource { get; } = new();
/// <summary>
/// 单页显示的源
/// </summary>
public ObservableCollection<FamilyModel> FamilyPageListViewItems { get; set; } = new();
public ObservableCollection<TreeViewItem> FolderTreeViewItems { get; set; } = new();
[RelayCommand]
private void LvScroll(ScrollViewer scrollViewer)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (!isLoadingData && scrollViewer.VerticalOffset + scrollViewer.ViewportHeight == scrollViewer.ExtentHeight)
{
LoadMoreData();
}
}
private const int BatchSize = 10;
private bool isLoadingData;
/// <summary>
/// 加载目录树节点的初始化数据
/// </summary>
private void LoadInitialData()
{
isLoadingData = true;
FamilyPageListViewItems.Clear();
var size = BatchSize;
if (FamilyDataSource.Count < BatchSize)
{
size = FamilyDataSource.Count;
}
for (var i = 0; i < size; i++)
{
FamilyPageListViewItems.Add(FamilyDataSource[i]);
}
isLoadingData = false;
}
/// <summary>
/// 懒加载
/// </summary>
private void LoadMoreData()
{
isLoadingData = true;
var startIndex = FamilyPageListViewItems.Count;
var end = startIndex + BatchSize;
if (end > FamilyDataSource.Count)
{
end = FamilyDataSource.Count;
}
if (startIndex < end + 1)
{
for (var i = startIndex; i < end; i++)
{
FamilyPageListViewItems.Add(FamilyDataSource[i]);
}
}
isLoadingData = false;
}
/// <summary>
/// 递归获取路径下的所有族文件
/// </summary>
/// <param name="fullPath"></param>
private void GetFamilyFile(string fullPath)
{
DirectoryInfo dir = new(fullPath);
try
{
if (!dir.Exists)
{
return;
}
//文件系统,包含文件夹和文件
var files = dir.GetFileSystemInfos();
foreach (var fSys in files)
{
//FSys如果是文件则fileinfo不为空
if (fSys is FileInfo fileInfo)
{
if (fileInfo.Extension == ".rfa")
{
var sfInfo = new FileInfo(fileInfo.DirectoryName + "\\" + fileInfo.Name);
//RevitFileUtil.ParserRevitFile(sfInfo.FullName);
var model = new FamilyModel
{
FileInfo = sfInfo,
Path = sfInfo.FullName,
Title = Path.GetFileNameWithoutExtension(sfInfo.FullName),
ToolTip = sfInfo.Name
//RevitVersion = RevitFileUtil.RevitVersion
};
FamilyDataSource.Add(model);
}
}
else //当fileinfo为空即为文件夹向下查找
{
GetFamilyFile($"{fullPath}\\{fSys}");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// 将族加载到当前文件中
/// </summary>
[RelayCommand]
private void ImportFamily(FamilyModel model)
{
//将model的绑定数据传给外部事件
loadFamilyHandler.Raise(app =>
{
try
{
var doc = app.ActiveUIDocument.Document;
Family family;
FamilySymbol fs = null;
doc.Invoke(
_ =>
{
family = doc.GetOrLoadedFamily(model.Path);
fs = doc.GetElement(family.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
//if (fs != null && !fs.IsActive)
//{
// fs.Activate();
//}
//if (family == null)
//{
// doc.LoadFamily(FileLibModel.Path, familyLoadOption, out family);
// fs = doc.GetElement(family.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
//
//}
//else
//{
// MessageBox.ShowAhead("族已载入或载入失败。", "提示");
//}
},
"载入族-" + Path.GetFileNameWithoutExtension(model.Path)
);
if (fs != null && app.ActiveUIDocument.CanPlaceElementType(fs))
{
app.ActiveUIDocument.PromptForFamilyInstancePlacement(fs);
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
catch (Exception e)
{
Message = e.Message;
e.Message.ToLog();
}
});
}
[RelayCommand]
private void ImportFamilySymbol()
{
if (SelectedFamily == null || SelectedSymbolType == null)
{
return;
}
loadFamilySymbolHandler.Raise(app =>
{
var uidoc = app.ActiveUIDocument;
var doc = uidoc.Document;
//当前选择的族类型
FamilySymbol fs = null;
if (SelectedSymbolType.Name == null)
{
return;
}
doc.Invoke(
_ =>
{
var fam = doc.GetOrLoadedFamily(SelectedFamily.Path);
doc.Regenerate();
//if (fam == null)
//{
// doc.LoadFamilySymbol(FamilyPath, FamilySymbolName, out fs);
//}
//else
//{
var x = fam.GetFamilySymbolIds()
.FirstOrDefault(id => doc.GetElement(id).Name == SelectedSymbolType.Name);
fs = doc.GetElement(x) as FamilySymbol;
//}
if (fs?.IsActive == false)
{
fs.Activate();
}
},
"载入族-" + Path.GetFileNameWithoutExtension(SelectedFamily.Path) + " 类型:" + SelectedSymbolType.Name
);
try
{
if (fs != null && uidoc.CanPlaceElementType(fs))
{
app.ActiveUIDocument.PromptForFamilyInstancePlacement(fs);
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
catch (Exception e)
{
e.Message.ToLog();
}
});
}
private void Initialize()
{
PopulateTreeView(FamilyPath);
GetFamilyFile(FamilyPath);
//CurrentPageIndex = 0;
LoadInitialData();
//PageCount = GetPageCount();
//PageOrderNumberCollection = GetOrderIntCollection();
//CollectionViewSource.GetDefaultView(FamilyPageListViewItems);
}
[RelayCommand]
private static void LocationFamilyFile(object obj)
{
//System.Windows.Controls.Button btn = obj as System.Windows.Controls.Button;
if (obj is FamilyModel familyInfo)
{
var proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "explorer";
//打开资源管理器
proc.StartInfo.Arguments = "/select," + familyInfo.Path;
proc.Start();
}
}
[RelayCommand]
private void LvSelectionChanged(object obj)
{
if (obj is FamilyModel familyInfo)
{
RevitFileUtil.ParserRevitFile(familyInfo.Path);
SymbolTypes = RevitFileUtil.SymbolTypes;
SelectedSymbolType = SymbolTypes.FirstOrDefault();
RevitVersion = RevitFileUtil.RevitVersion;
}
}
/// <summary>
/// 绑定TreeView数据源.
/// </summary>
/// <param name="path">The Path<see cref="string" />.</param>
private void PopulateTreeView(string path)
{
FolderTreeViewItems?.Clear();
var info = new DirectoryInfo(path);
if (!info.Exists)
{
return;
}
var rootNode = new TreeViewItem { Header = info.Name, Tag = info.FullName };
/* 项目“Sai.RvKits (net481)”的未合并的更改
在此之前:
SubDirectories(info.GetDirectories(), rootNode);
在此之后:
FamilyLibraryViewModel.SubDirectories(info.GetDirectories(), rootNode);
*/
SubDirectories(info.GetDirectories(), rootNode);
//dirTree.DataContext = rootNode;
FolderTreeViewItems!.Add(rootNode);
rootNode.IsExpanded = true;
}
[RelayCommand]
private void SearchFamily(object obj)
{
//ListViewItem foundItem =
// textListView.FindItemWithText(searchBox.Text, false, 0, true);
//if (foundItem != null)
//{
// textListView.TopItem = foundItem;
//}
//Func<string, bool> searchFunc = BreakCurve;
//Task<bool> task = new Task<bool>(() => BreakCurve(obj.ToString()));
//task.Start();
//await task;
if (string.IsNullOrEmpty(obj.ToString()))
{
LoadInitialData();
//FamilyPageListViewItems.Clear();
//itemsLoaded.ForEach(item => FamilyPageListViewItems.Add(item));
}
else
{
//itemsLoaded.Clear();
//FamilyPageListViewItems.ForEach(item => itemsLoaded.Add(item));
FamilyPageListViewItems.Clear();
foreach (var item in FamilyDataSource)
{
if (Fuzz.PartialRatio(item.Title,obj.ToString())>70)
{
FamilyPageListViewItems.Add(item);
}
//if (item.Title.Contains(obj.ToString()))
//{
//}
}
}
}
/// <summary>
/// 选择树状节点
/// </summary>
/// <param name="obj"></param>
[RelayCommand]
private void SelectDirTreeNode(object obj)
{
//e.OriginalSource 就是TreeViewItem对象你可以将其保存到窗体类的某个私有字段中或者直接使用它如下所示
FamilyDataSource.Clear();
if (obj is TreeViewItem selectedItem)
{
GetFamilyFile(selectedItem.Tag.ToString());
}
LoadInitialData();
SymbolTypes = null;
}
/// <summary>
/// 路径选择.
/// </summary>
[RelayCommand]
private void SelectPath()
{
var dialog = new VistaFolderBrowserDialog();
if (dialog.ShowDialog() == true)
{
try
{
#if REVIT2018
Settings.Default.FamilyPath_2018 = dialog.SelectedPath;
#elif REVIT2019
Settings.Default.FamilyPath_2019 = dialog.SelectedPath;
#elif REVIT2020
Settings.Default.FamilyPath_2020 = dialog.SelectedPath;
#elif REVIT2021
Settings.Default.FamilyPath_2021 = dialog.SelectedPath;
#endif
PopulateTreeView(dialog.SelectedPath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
/// <summary>
/// 递归添加目录结构到TreeView根节点.
/// </summary>
/// <param name="subDirs">.</param>
/// <param name="nodeToAddTo">根节点.</param>
private static void SubDirectories(DirectoryInfo[] subDirs, TreeViewItem nodeToAddTo)
{
//父节点
foreach (var subDir in subDirs)
{
var aNode = new TreeViewItem { Header = subDir.Name, Tag = subDir.FullName };
//获取子目录
var subSubDirs = subDir.GetDirectories();
if (subSubDirs.Length != 0)
{
/* 项目“Sai.RvKits (net481)”的未合并的更改
在此之前:
SubDirectories(subSubDirs, aNode);
在此之后:
FamilyLibraryViewModel.SubDirectories(subSubDirs, aNode);
*/
SubDirectories(subSubDirs, aNode);
}
nodeToAddTo.Items.Add(aNode);
}
}
}

View File

@@ -0,0 +1,88 @@
using System.IO;
using System.Windows.Media.Imaging;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Sai.Toolkit.Core.Extensions;
namespace Sai.RvKits.RvFamily.FamilyLibrary;
public partial class FamilyModel : ObservableObject
{
/// <summary>
/// 族库缩略图
/// </summary>
public BitmapSource ImageData => ImageExtensions.LoadFileImage(Path, 128, 128);
/// <summary>
/// 最近使用面板
/// </summary>
public BitmapImage BitmapImage { get; set; }
public FileInfo FileInfo { get; set; }
/// <summary>
/// 族库文件标题
/// </summary>
public string Title { get; set; }
public Family ReferenceFamily { get; set; }
public string ToolTip { get; set; }
public UIApplication UiApplication { get; set; }
public string Path { get; set; }
public string RevitVersion { get; set; }
public ElementId FamilyId => ReferenceFamily.Id;
//private bool CanPlace()
//{
// uiApplication.ActiveUIDocument.Document.is
// return ReferenceFamily.IsValidObject;
// .IsValidObject = true;
//}
[RelayCommand]
private static void PlaceRecentFamily(object obj)
{
try
{
if (obj is FamilyModel { ReferenceFamily: { } family } info)
{
var uidoc = info.UiApplication.ActiveUIDocument;
var doc = uidoc.Document;
if (family.IsValidObject && doc.PathName == family.Document.PathName)
{
var fs = doc.GetElement(family.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol;
uidoc.PostRequestForElementTypePlacement(fs);
}
//else
//{
// var docs = info.uiApplication.Application.Documents;
// foreach (Document d in docs)
// {
// if (d.PathName == family.Document.PathName)
// {
// List<ElementId> list = new List<ElementId>()
// {
// family.ViewId
// };
// doc.Invoke(ts =>
// {
// ElementTransformUtils.CopyElements(d, list, doc, null, new CopyPasteOptions());
// },"复制族");
// break;
// }
// }
//}
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
catch (Exception e)
{
e.ToLog();
}
}
}

View File

@@ -0,0 +1,17 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace Sai.RvKits.RvFamily.FamilyLibrary
{
public class LoadedFamilyDropHandler : IDropHandler
{
public void Execute(UIDocument document, object data)
{
ElementId familySymbolId = (ElementId)data;
if (document.Document.GetElement(familySymbolId) is FamilySymbol symbol)
{
document.PromptForFamilyInstancePlacement(symbol);
}
}
}
}

View File

@@ -0,0 +1,16 @@
namespace Sai.RvKits.RvFamily.FamilyLibrary
{
public class FamilySymbolType
{
public List<Parameter> Parameters { get; set; } = new();
public string Name { get; set; }
public string Value { get; set; }
}
public class Parameter
{
public string Name { get; set; }
public string Value { get; set; }
public string TypeOfParameter { get; set; }
}
}

View File

@@ -0,0 +1,645 @@
using System;
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 Sai.RvKits.RvFamily.FamilyLibrary
{
/// <summary>
/// 直接读取文件的信息不依赖RevitAPI
/// </summary>
internal static class RevitFileUtil
{
private static string _fileName;
public static Image PreviewImage { get; set; }
public static List<FamilySymbolType> SymbolTypes { get; set; }
public static StringBuilder FileInfo { get; set; }
public static StringBuilder Content { get; set; }
public static StringBuilder TransMissionData { get; set; }
public static string SafeName { get; set; }
public static string Product { get; set; }
public static string RevitVersion { get; set; }
public static string UpdateTime { get; set; }
/// <summary>
/// 图片资源转字节
/// </summary>
/// <param name="bitmapSource"></param>
/// <returns></returns>
public static byte[] BitSourceToArray(BitmapSource bitmapSource)
{
BitmapEncoder encoder = new JpegBitmapEncoder();
using (MemoryStream ms = new MemoryStream())
{
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(ms);
return ms.ToArray();
}
}
public static StringBuilder GetFamilyInfo(List<FamilySymbolType> symbols)
{
StringBuilder sb = new StringBuilder();
foreach (FamilySymbolType symbol in symbols)
{
sb.Append($"族类型:{symbol.Name}{symbol.Value}\r\n");
foreach (Parameter param in symbol.Parameters)
{
sb.Append($" {param.Name}:{param.Value}\r\n");
}
}
return sb;
}
public static int GetPngStartingOffset(byte[] previewData)
{
bool markerFound = false;
int startingOffset = 0;
int previousValue = 0;
using (MemoryStream ms = new MemoryStream(previewData))
{
for (int i = 0; i < previewData.Length; i++)
{
int 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>
public static Image GetPreviewAsImage(byte[] previewData)
{
if (previewData == null || previewData.Length <= 0)
{
using (Bitmap newBitmap = new Bitmap(128, 128))
{
return newBitmap.Clone() as Bitmap;
}
}
// read past the Revit metadata to the start of the PNG image
int startingOffset = GetPngStartingOffset(previewData);
if (startingOffset == 0)
{
using (Bitmap newBitmap = new Bitmap(128, 128))
{
return newBitmap.Clone() as Bitmap;
}
}
try
{
byte[] pngDataBuffer = new byte[previewData.GetUpperBound(0) - startingOffset + 1];
// read the PNG image data into a byte array
using (MemoryStream 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 (MemoryStream ms = new MemoryStream(pngDataBuffer))
{
PngBitmapDecoder decoder = new PngBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
decoderData = BitSourceToArray(decoder.Frames[0]);
}
}
// if the decoded data is valie
if (decoderData != null && decoderData.Length > 0)
{
// use another memory stream to create a Bitmap
// and then an Image from that Bitmap
using (MemoryStream ms = new MemoryStream(decoderData))
{
using (Bitmap newBitmap = new Bitmap(ms))
{
using (Image newImage = newBitmap)
{
return newImage.Clone() as Image;
}
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
using (Bitmap newBitmap = new Bitmap(128, 128))
{
return newBitmap.Clone() as Bitmap;
}
}
public static string ParseDetailInfo(string detailInfo)
{
try
{
detailInfo = detailInfo.Trim();
int index = detailInfo.IndexOf(":");
string detailValue = detailInfo.Substring(detailInfo.IndexOf(":") + 1);
string detailKey = detailInfo.Substring(0, detailInfo.IndexOf(":"));
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.Unknown;
// 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.Unknown;
// 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;
//}
}
public static StringBuilder ParseFileInfo(string unicodeData)
{
StringBuilder s = new StringBuilder();
string[] basicFileInfoParts = unicodeData.Split('\0');
foreach (string basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
string[] detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (string detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
/// <summary>
/// 解析参数
/// </summary>
/// <param name="streamInfo"></param>
public static List<FamilySymbolType> ParseParameter(StreamInfo streamInfo)
{
XmlDocument document = new XmlDocument();
string xmlStr;
byte[] streamData;
using (Stream 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);
//读取参数信息
XmlElement root = document.DocumentElement;
//节点前缀的命名空间
string nameSpace = root.GetNamespaceOfPrefix("A");
//string nameSpace = root.NamespaceURI;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
;
nsmgr.AddNamespace("entry", nameSpace);
//族类型
XmlNodeList xnlist = document.GetElementsByTagName("A:part");
XmlNodeList 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;
List<FamilySymbolType> symbols = new List<FamilySymbolType>();
foreach (XmlNode xn in xnlist)
{
//XmlAttributeCollection attriCol = xn.Attributes;
//foreach (XmlAttribute xmlAttri in attriCol)
//{
// string name = xmlAttri.Name;
// string value = xmlAttri.Value;
//}
FamilySymbolType symbol = new FamilySymbolType();
if (xn.HasChildNodes)
{
foreach (XmlNode child in xn.ChildNodes)
{
Parameter p = new Parameter();
if (child.Name == "title")
{
symbol.Name = child.InnerText;
continue;
}
//族类型节点
p.Name = child.Name;
//族名称
p.Value = child.InnerText;
symbol.Parameters.Add(p);
}
}
symbols.Add(symbol);
}
return symbols;
}
}
public static void ParserRevitFile(string filename)
{
if (filename.EndsWith(".rvt") || filename.EndsWith(".rte") || filename.EndsWith(".rfa") ||
filename.EndsWith(".rft"))
{
_fileName = filename;
//获取Storageroot Object
BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.InvokeMethod;
Type 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
};
StorageInfo Storage = (StorageInfo)storageRootType.InvokeMember("Open", bindingFlags, null, null, file);
//if (Storage != null)
//{
// storageRootType.InvokeMember("CloseTrigger", bindingFlags, null, Storage, file);
// //Console.Write($"{filename}文件无法作为结构存储,并打开打开");
//}
//var x = Storage.ThumbnailImage.GetPreviewAsImage();
//读取结构化存储文件
StreamInfo[] streams = Storage.GetStreams();
foreach (StreamInfo streamInfo in streams)
{
string unicodeData;
byte[] streamData = null;
using (Stream streamReader = streamInfo.GetStream(FileMode.Open, FileAccess.Read))
{
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 = ParseParameter(streamInfo);
//Console.WriteLine("族参数:\r\n" + GetFamilyInfo(symbols));
}
}
if (streamInfo.Name.ToUpper().Equals("TRANSMISSIONDATA"))
{
TransMissionData = ParseTransMissionData(unicodeData);
}
if (streamInfo.Name.ToUpper().Equals("REVITPREVIEW4.0"))
{
PreviewImage = ParsePreview(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);
// }
//}
}
public static StringBuilder ParseTransMissionData(string unicodeData)
{
StringBuilder s = new StringBuilder();
string[] basicFileInfoParts = unicodeData.Split('\0');
foreach (string basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n") >= 0)
{
string[] detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (string detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
public static void ParseXmlDocument(XmlDocument document)
{
XmlNode rootNode = document.SelectSingleNode("A");
XmlNodeList xmlNodeList = rootNode?.ChildNodes;
if (xmlNodeList == null) return;
foreach (XmlNode xn in xmlNodeList)
{
if (true)
{
}
XmlAttributeCollection xnAttributes = xn.Attributes;
if (xnAttributes != null)
foreach (XmlAttribute attribute in xnAttributes)
{
string name = attribute.Name;
string value = attribute.Value;
}
if (xn.HasChildNodes)
{
}
}
}
/// <summary>
/// 清理无效字符
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
public static string PurgeUnprintableCharacters(string oldValue)
{
StringBuilder sb = new StringBuilder();
char[] oldValueArray = oldValue.ToCharArray();
foreach (char letter in oldValueArray)
{
int decimalValue = letter;
if (decimalValue >= 32 && decimalValue <= 126)
{
sb.Append(letter);
}
}
oldValue = sb.ToString();
sb.Length = 0;
sb.Capacity = 0;
sb = null;
return oldValue;
}
private static StringBuilder ParseContents(string unicodeData)
{
StringBuilder s = new StringBuilder();
string[] basicFileInfoParts = unicodeData.Split('\0');
foreach (string basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
string[] detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (string detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
private static StringBuilder ParsePartAtom(string unicodeData)
{
StringBuilder s = new StringBuilder();
string[] basicFileInfoParts = unicodeData.Split('\0');
foreach (string basicFileInfoPart in basicFileInfoParts)
{
if (basicFileInfoPart.IndexOf("\r\n", StringComparison.Ordinal) >= 0)
{
string[] detailInfoParts = basicFileInfoPart.Split(new[] { "\r\n" }, new StringSplitOptions());
foreach (string detailPart in detailInfoParts)
{
s.Append(ParseDetailInfo(detailPart) + "\r\n");
}
}
}
return s;
}
private static Image ParsePreview(StreamInfo stream)
{
byte[] streamData = null;
using (Stream streamReader = stream.GetStream(FileMode.Open, FileAccess.Read))
{
streamData = new byte[streamReader.Length];
streamReader.Read(streamData, 0, streamData.Length);
//unicodeData = Encoding.Unicode.GetString(streamData);
//unicodeData = Encoding.UTF8.GetString(streamData);
}
return GetPreviewAsImage(streamData);
}
}
}