修改命名空间
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions
|
||||
{
|
||||
public static class CommonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前枚举值的描述,没有描述则返回空字符串
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static T GetAttribute<T>(this Enum value) where T : Attribute
|
||||
{
|
||||
var fieldInfo = value.GetType().GetField(value.ToString());
|
||||
return fieldInfo.GetCustomAttribute<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 自定义Distinct扩展方法
|
||||
/// </summary>
|
||||
public static class DistinctExtensions
|
||||
{
|
||||
public static IEnumerable<T> Distinct<T>(
|
||||
this IEnumerable<T> source, Func<T, T, bool> comparer)
|
||||
where T : class
|
||||
=> source.Distinct(new Extensions.DistinctExtensions.DynamicEqualityComparer<T>(comparer));
|
||||
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
|
||||
=> source.Distinct(new Extensions.DistinctExtensions.CommonEqualityComparer<T, V>(keySelector));
|
||||
private sealed class DynamicEqualityComparer<T> : IEqualityComparer<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly Func<T, T, bool> _func;
|
||||
|
||||
public DynamicEqualityComparer(Func<T, T, bool> func)
|
||||
{
|
||||
_func = func;
|
||||
}
|
||||
|
||||
public bool Equals(T x, T y) => _func(x, y);
|
||||
|
||||
public int GetHashCode(T obj) => 0;
|
||||
}
|
||||
|
||||
private sealed class CommonEqualityComparer<T, V> : IEqualityComparer<T>
|
||||
{
|
||||
private readonly Func<T, V> keySelector;
|
||||
|
||||
public CommonEqualityComparer(Func<T, V> keySelector)
|
||||
{
|
||||
this.keySelector = keySelector;
|
||||
}
|
||||
|
||||
public bool Equals(T x, T y)
|
||||
{
|
||||
return EqualityComparer<V>.Default.Equals(keySelector(x), keySelector(y));
|
||||
}
|
||||
|
||||
public int GetHashCode(T obj)
|
||||
{
|
||||
return EqualityComparer<V>.Default.GetHashCode(keySelector(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions;
|
||||
|
||||
public static class ImageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 位图转图片
|
||||
/// </summary>
|
||||
/// <param name="bitmap"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static BitmapImage ToBitmapImage(this Bitmap bitmap)
|
||||
{
|
||||
if (bitmap is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bitmap));
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
bitmap.Save(ms, ImageFormat.Png);
|
||||
|
||||
ms.Position = 0;
|
||||
|
||||
var result = new BitmapImage();
|
||||
|
||||
result.BeginInit();
|
||||
|
||||
result.StreamSource = ms;
|
||||
|
||||
result.CacheOption = BitmapCacheOption.OnLoad;
|
||||
|
||||
result.EndInit();
|
||||
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// 位图转像素集
|
||||
/// </summary>
|
||||
/// <param name="bitmap"></param>
|
||||
/// <returns></returns>
|
||||
public static BitmapSource ToBitmapSource(this Bitmap bitmap)
|
||||
{
|
||||
return bitmap == null
|
||||
? null
|
||||
: Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Icon转BitmapSource
|
||||
/// </summary>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public static BitmapSource ToBitmapSource(this Icon icon)
|
||||
{
|
||||
return Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
new Int32Rect(0, 0, icon.Width, icon.Height),
|
||||
BitmapSizeOptions.FromWidthAndHeight(icon.Width, icon.Height)
|
||||
);
|
||||
}
|
||||
public static ImageSource ConvertBitmapToImageSource(this Bitmap bitmap)
|
||||
{
|
||||
using (MemoryStream memory = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
BitmapImage bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using MiniExcelLibs;
|
||||
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions
|
||||
{
|
||||
public static class MiniExcelExtensions
|
||||
{
|
||||
// --- 写入部分 (Write) ---
|
||||
|
||||
/// <summary>
|
||||
/// 开始流式配置 Excel 写入
|
||||
/// </summary>
|
||||
public static Extensions.ExcelWriter<T> AsExcel<T>(this IEnumerable<T> data)
|
||||
{
|
||||
return new Extensions.ExcelWriter<T>(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速保存多 Sheet 字典到文件
|
||||
/// </summary>
|
||||
public static void SaveAsExcel(this Dictionary<string, object> sheetsData, string filePath, bool overwrite = true)
|
||||
{
|
||||
// 自动清理 Sheet 名称中的非法字符
|
||||
var sanitizedData = new Dictionary<string, object>();
|
||||
foreach (var kvp in sheetsData)
|
||||
{
|
||||
sanitizedData.Add(SanitizeSheetName(kvp.Key), kvp.Value);
|
||||
}
|
||||
MiniExcel.SaveAs(filePath, sanitizedData, overwriteFile: overwrite);
|
||||
}
|
||||
|
||||
// --- 读取部分 (Read) ---
|
||||
|
||||
/// <summary>
|
||||
/// 从文件路径直接读取数据为强类型列表
|
||||
/// </summary>
|
||||
public static IEnumerable<T> ReadExcel<T>(this string filePath, string sheetName = null) where T : class, new()
|
||||
{
|
||||
if (!File.Exists(filePath)) throw new FileNotFoundException("Excel文件不存在", filePath);
|
||||
return MiniExcel.Query<T>(filePath, sheetName: sheetName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从文件路径读取为动态类型 (Dictionary/dynamic)
|
||||
/// </summary>
|
||||
public static IEnumerable<dynamic> ReadExcelDynamic(this string filePath, string sheetName = null)
|
||||
{
|
||||
if (!File.Exists(filePath)) throw new FileNotFoundException("Excel文件不存在", filePath);
|
||||
return MiniExcel.Query(filePath, sheetName: sheetName);
|
||||
}
|
||||
|
||||
// --- 工具方法 ---
|
||||
|
||||
/// <summary>
|
||||
/// 清理 Excel Sheet 名称中的非法字符
|
||||
/// </summary>
|
||||
public static string SanitizeSheetName(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) return "Sheet1";
|
||||
char[] invalidChars = { '\\', '/', '?', '*', '[', ']', ':' };
|
||||
foreach (var c in invalidChars) name = name.Replace(c, '_');
|
||||
return name.Length > 31 ? name.Substring(0, 31) : name;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExcelWriter<T>
|
||||
{
|
||||
private readonly IEnumerable<T> data;
|
||||
private string sheetName = "Sheet1";
|
||||
private bool overwrite = true;
|
||||
|
||||
public ExcelWriter(IEnumerable<T> data) => this.data = data;
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Sheet 名称
|
||||
/// </summary>
|
||||
public Extensions.ExcelWriter<T> WithSheetName(string name)
|
||||
{
|
||||
sheetName = Extensions.MiniExcelExtensions.SanitizeSheetName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置是否覆盖
|
||||
/// </summary>
|
||||
public Extensions.ExcelWriter<T> Overwrite(bool canOverwrite = true)
|
||||
{
|
||||
overwrite = canOverwrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行保存到文件
|
||||
/// </summary>
|
||||
public void SaveTo(string filePath)
|
||||
{
|
||||
MiniExcel.SaveAs(filePath, data, sheetName: sheetName, overwriteFile: overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为字节数组 (用于 Web 下载或内存操作)
|
||||
/// </summary>
|
||||
public byte[] ToBytes()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
ms.SaveAs(data, sheetName: sheetName);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions;
|
||||
#region 格式化
|
||||
|
||||
//StringFormat={}{0:C} $123.46
|
||||
//StringFormat={}{0:C1} $123.5
|
||||
//StringFormat=单价:{0:C} 单价:$123.46
|
||||
//StringFormat={}{0}元 123.45678元
|
||||
//StringFormat={}{0:D6} 086723
|
||||
//StringFormat={}{0:F4} 28768234.9329
|
||||
//StringFormat={}{0:N3} 28,768,234.933
|
||||
//StringFormat={}{0:P1} 78.9 %
|
||||
//StringFormat={}{0:0000.00} 0123.46
|
||||
//StringFormat={}{0:####.##} 123.46
|
||||
//StringFormat={}{0:d} 5/4/2015
|
||||
//StringFormat={}{0:D} Monday, May 04, 2015
|
||||
//StringFormat={}{0:f} Monday, May 04, 2015 5:46 PM
|
||||
//StringFormat={}{0:F} Monday, May 04, 2015 5:46:56 PM
|
||||
//StringFormat={}{0:g} 5/4/2015 5:46 PM
|
||||
//StringFormat={}{0:G} 5/4/2015 5:46:56 PM
|
||||
//StringFormat={}{0:m} May 04
|
||||
//StringFormat={}{0:Distinct} May 04
|
||||
//StringFormat={}{0:t} 5:46 PM
|
||||
//StringFormat={}{0:Command} 5:46:56 PM
|
||||
//StringFormat={}{0:yyyy年MM月dd日} 2015年05月04日
|
||||
//StringFormat={}{0:yyyy-MM-dd} 2015-05-04
|
||||
//StringFormat={}{0:yyyy-MM-dd HH:mm} 2015-05-04 17:46
|
||||
//StringFormat={}{0:yyyy-MM-dd HH:mm:ss},,ConverterCulture=zh-CN||StringFormat='yyyy:MM:dd HH:mm:ss',,ConverterCulture=zh-CN 2015-05-04 17:46:56
|
||||
//< TextBox.Text >
|
||||
// < MultiBinding StringFormat = "姓名:{0}	{1}" >
|
||||
// < Binding Path = "FristName" />
|
||||
// < Binding Path = "LastName" />
|
||||
// </ MultiBinding >
|
||||
//</ TextBox.Text >
|
||||
// < !--
|
||||
// \a  BEL
|
||||
// \b  BS - Backspace
|
||||
// \f  FF - Formfeed
|
||||
// \n 
 LF, NL - Linefeed, New Line
|
||||
// \r 
 CR - Carriage return
|
||||
// \t 	 HT - Tab, Horizontal Tabelator
|
||||
// \v  VT - Vertical Tabelator
|
||||
|
||||
#endregion
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 取得中文字符
|
||||
/// </summary>
|
||||
/// <param name="oriText"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetChineseWord(this string oriText)
|
||||
{
|
||||
var x = @"[\u4E00-\u9FFF]+";
|
||||
var matches = Regex.Matches(oriText, x, RegexOptions.IgnoreCase);
|
||||
var sb = new StringBuilder();
|
||||
foreach (Match nextMatch in matches)
|
||||
{
|
||||
sb.Append(nextMatch.Value);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 固定间隔插入字符
|
||||
/// </summary>
|
||||
/// <param name="input">源字符串</param>
|
||||
/// <param name="interval">间隔</param>
|
||||
/// <param name="value">插入字符</param>
|
||||
/// <returns></returns>
|
||||
public static string InsertFormat(this string input, int interval, string value)
|
||||
{
|
||||
for (var i = interval - 1; i < input.Length; i += interval + 1)
|
||||
{
|
||||
input = input.Insert(i, value);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断更严谨,包含的有空串("")、空白符(空格""," ",制表符"\t",回车符"\r","\n"等)以及null值;
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsBlank(this string str)
|
||||
{
|
||||
int strLen;
|
||||
if (str == null || (strLen = str.Length) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < strLen; i++)
|
||||
{
|
||||
if (char.IsWhiteSpace(str.ElementAt(i)) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为Double类型
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsDouble(this string expression)
|
||||
{
|
||||
return expression != null && Regex.IsMatch(expression.ToString(), @"^([0-9])[0-9]*(\.\w*)?$");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断对象是否为Int32类型的数字
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression<see cref="string" /></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNumeric(this string expression)
|
||||
{
|
||||
if (expression != null)
|
||||
{
|
||||
var str = expression;
|
||||
if (str.Length > 0 && str.Length <= 11 && Regex.IsMatch(str, @"^[-]?[0-9]*[.]?[0-9]*$"))
|
||||
{
|
||||
if (str.Length < 10 || (str.Length == 10 && str[0] == '1') || (str.Length == 11 && str[0] == '-' && str[1] == '1'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较字符串相似度
|
||||
/// </summary>
|
||||
/// <param name="str1"></param>
|
||||
/// <param name="str2"></param>
|
||||
/// <returns></returns>
|
||||
public static float Levenshtein(this string str1, string str2)
|
||||
{
|
||||
var len1 = str1.Length;
|
||||
var len2 = str2.Length;
|
||||
var dif = new int[len1 + 1, len2 + 1];
|
||||
for (var a = 0; a <= len1; a++)
|
||||
{
|
||||
dif[a, 0] = a;
|
||||
}
|
||||
|
||||
for (var a = 0; a <= len2; a++)
|
||||
{
|
||||
dif[0, a] = a;
|
||||
}
|
||||
|
||||
int temp;
|
||||
for (var i = 1; i <= len1; i++)
|
||||
{
|
||||
for (var j = 1; j <= len2; j++)
|
||||
{
|
||||
temp = str1.ElementAt(i - 1) == str2.ElementAt(j - 1) ? 0 : 1;
|
||||
|
||||
dif[i, j] = Min(dif[i - 1, j - 1] + temp, dif[i, j - 1] + 1, dif[i - 1, j] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var similarity = 1 - ((float)dif[len1, len2] / Math.Max(len1, len2));
|
||||
return similarity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最短长度
|
||||
/// </summary>
|
||||
/// <param name="arr"></param>
|
||||
/// <returns></returns>
|
||||
private static int Min(params int[] arr)
|
||||
{
|
||||
var min = int.MaxValue;
|
||||
foreach (var i in arr)
|
||||
{
|
||||
if (min > i)
|
||||
{
|
||||
min = i;
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数字转字母
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static string NumberToLetter(int index)
|
||||
{
|
||||
var str = string.Empty;
|
||||
if (index >= "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Length)
|
||||
{
|
||||
var num = index / "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Length;
|
||||
index %= "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Length;
|
||||
str += num.ToString();
|
||||
}
|
||||
|
||||
return str + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定字符串的固定长度,如果字符串小于固定长度,
|
||||
/// 则在字符串的前面补足零,可设置的固定长度最大为9位
|
||||
/// </summary>
|
||||
/// <param name="text">原始字符串</param>
|
||||
/// <param name="limitedLength">字符串的固定长度</param>
|
||||
/// <returns>The <see cref="string" /></returns>
|
||||
public static string RepairZero(this string text, int limitedLength)
|
||||
{
|
||||
return text.PadRight(limitedLength, '0');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定字符集将string转换成byte[]
|
||||
/// </summary>
|
||||
/// <param name="text">要转换的字符串</param>
|
||||
/// <param name="encoding">字符编码</param>
|
||||
public static byte[] StringToBytes(this string text, Encoding encoding)
|
||||
{
|
||||
return encoding.GetBytes(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// string型转换为decimal型
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression<see cref="string" /></param>
|
||||
/// <param name="defValue">缺省值</param>
|
||||
/// <returns>转换后的decimal类型结果</returns>
|
||||
public static decimal StrToDecimal(this string expression, decimal defValue)
|
||||
{
|
||||
if (expression == null || expression.Length > 10)
|
||||
{
|
||||
return defValue;
|
||||
}
|
||||
|
||||
var intValue = defValue;
|
||||
{
|
||||
var isDecimal = Regex.IsMatch(expression, @"^([-]|[0-9])[0-9]*(\.\w*)?$");
|
||||
if (isDecimal)
|
||||
{
|
||||
decimal.TryParse(expression, out intValue);
|
||||
}
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// string型转换为float型
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression<see cref="string" /></param>
|
||||
/// <param name="defValue">缺省值</param>
|
||||
/// <returns>转换后的int类型结果</returns>
|
||||
public static float StrToFloat(this string expression, float defValue)
|
||||
{
|
||||
if (expression == null || expression.Length > 10)
|
||||
{
|
||||
return defValue;
|
||||
}
|
||||
|
||||
var intValue = defValue;
|
||||
{
|
||||
var isFloat = Regex.IsMatch(expression, @"^([-]|[0-9])[0-9]*(\.\w*)?$");
|
||||
if (isFloat)
|
||||
{
|
||||
float.TryParse(expression, out intValue);
|
||||
}
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串转换为Int32类型
|
||||
/// </summary>
|
||||
/// <param name="expression">要转换的字符串</param>
|
||||
/// <param name="defValue">缺省值</param>
|
||||
/// <returns>转换后的int类型结果</returns>
|
||||
public static int StrToInt(this string expression, int defValue)
|
||||
{
|
||||
return string.IsNullOrEmpty(expression) || expression.Trim().Length >= 11 || !Regex.IsMatch(expression.Trim(), @"^([-]|[0-9])[0-9]*(\.\w*)?$")
|
||||
? defValue
|
||||
: int.TryParse(expression, out var rv)
|
||||
? rv
|
||||
: Convert.ToInt32(StrToFloat(expression, defValue));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user