CPF/CPF/ObjectExtenstions.cs

988 lines
31 KiB
C#
Raw Normal View History

2023-11-21 23:05:03 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using CPF.Reflection;
using System.Linq;
using System.Data;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
using CPF.Threading;
using CPF.Controls;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace CPF
{
/// <summary>
/// Object数据转换扩展
/// </summary>
public static class ObjectExtenstions
{
/// <summary>
/// 分配一个变量,用来在绑定的时候使用,比如 new Button{Content="test"}.Assign(out var btn)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="o"></param>
/// <param name="v"></param>
/// <returns></returns>
public static T Assign<T>(this T o,out T v)
{
v = o;
return o;
}
/// <summary>
/// 观察事件或者属性变化用于Reactive 开发模式,代替 Observable.FromEventPattern
/// </summary>
/// <typeparam name="TArgs"></typeparam>
/// <typeparam name="TSender"></typeparam>
/// <param name="source"></param>
/// <param name="eventName">事件名或者属性名</param>
/// <returns></returns>
public static IObservable<EventObserver<TArgs, TSender>> Observe<TSender, TArgs>(this TSender source, string eventName) where TSender : CpfObject where TArgs : EventArgs
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (string.IsNullOrWhiteSpace(eventName))
{
throw new ArgumentNullException("eventName");
}
return new EO<TArgs, TSender> { Observable = source, eventName = eventName };
}
/// <summary>
/// 设置控件模板就是对Control的Template 属性进行泛型包装
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="control"></param>
/// <param name="action"></param>
public static T SetTemplate<T>(this T control, Action<T, UIElementCollection> action) where T : Control
{
control.Template = (ctl, children) =>
{
action(ctl as T, children);
};
return control;
}
/// <summary>
/// 延迟设置属性值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="V"></typeparam>
/// <param name="obj"></param>
/// <param name="timeSpan"></param>
/// <param name="expression">a=>a.Property</param>
/// <param name="value"></param>
public static void Delay<T, V>(this T obj, TimeSpan timeSpan, Expression<Func<T, V>> expression, V value)
{
MemberExpression member = (MemberExpression)expression.Body;
var property = member.Member.Name;
DispatcherTimer timer = new DispatcherTimer { Interval = timeSpan, IsEnabled = true };
timer.Tick += delegate
{
timer.Dispose();
obj.SetPropretyValue(property, value);
};
}
/// <summary>
/// 延迟操作
/// </summary>
/// <param name="obj"></param>
/// <param name="timeSpan"></param>
/// <param name="action"></param>
/// <param name="count">循环次数</param>
public static void Delay(this object obj, TimeSpan timeSpan, Action action, uint count = 1)
{
if (action == null || count == 0)
{
return;
}
int c = 0;
DispatcherTimer timer = new DispatcherTimer { Interval = timeSpan, IsEnabled = true };
timer.Tick += delegate
{
c++;
if (c >= count)
{
timer.Dispose();
}
action();
};
}
/// <summary>
/// 延迟操作
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="timeSpan"></param>
/// <param name="action"></param>
/// <param name="count"></param>
/// <returns></returns>
public static T Delay<T>(this T obj, TimeSpan timeSpan, Action<T> action, uint count = 1)
{
if (action == null || count == 0)
{
return obj;
}
int c = 0;
DispatcherTimer timer = new DispatcherTimer { Interval = timeSpan, IsEnabled = true };
timer.Tick += delegate
{
c++;
if (c >= count)
{
timer.Dispose();
}
action(obj);
};
return obj;
}
public static string GetCreationCode(this object obj)
{
if (obj == null)
{
return "null";
}
var type = obj.GetType();
if (type.IsEnum)
{
return type.Name + "." + Enum.GetName(type, obj);
}
if (obj is string str)
{
return $"\"{str.Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r")}\"";
}
if (obj is Design.ISerializerCode code)
{
return code.GetCreationCode();
}
if (obj is int || obj is double || obj is byte || obj is short || obj is uint || obj is ulong || obj is decimal || obj is ushort)
{
return obj.ToString();
}
if (obj is char c)
{
if (c == '\'')
{
return "'\\''";
}
return "'" + obj.ToString() + "'";
}
if (obj is float)
{
return ((float)obj).ToString("0.#") + "f";
}
if (type.GetMethod("op_Implicit", new Type[] { typeof(string) }) != null)
{
return $"\"{obj}\"";
}
if (obj is bool)
{
return ((bool)obj) ? "true" : "false";
}
if (obj is DateTime)
{
var date = (DateTime)obj;
return $"new DateTime({date.Ticks},DateTimeKind.{date.Kind})";
}
return $"new {type.Name}()";
}
//internal static T ListFirstOrDefault<T>(this IList<T> list, Func<T, bool> func)
//{
// var c = list.Count;
// for (int i = 0; i < c; i++)
// {
// var item = list[i];
// if (func(item))
// {
// return item;
// }
// }
// return default;
//}
/// <summary>
/// 转成IList用于Items属性设置
/// </summary>
/// <param name="dataTable"></param>
/// <returns></returns>
public static IList ToItems(this DataTable dataTable)
{
return new DataRows(dataTable);
}
/// <summary>
/// 必须是DataTable转换为IList的才行
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable GetDataTable(this IList list)
{
if (list is DataRows dataRows)
{
return dataRows.DataTable;
}
throw new Exception("必须是DataTable.ToItems()获取的IList才能使用GetDataTable()");
}
/// <summary>
/// 转成IList用于Items属性设置
/// </summary>
/// <returns></returns>
public static IList ToItems(this IEnumerable list)
{
return new Enumerable(list);
}
/// <summary>
/// 是否大约相等 Math.Abs(f1 - f2) &lt; 0.0001
/// </summary>
/// <param name="f1"></param>
/// <param name="f2"></param>
/// <returns></returns>
public static bool Equal(this float f1, float f2)
{
return Math.Abs(f1 - f2) < 0.0001;
}
/// <summary>
/// 对象是否相等
/// </summary>
/// <param name="obj1"></param>
/// <param name="obj2"></param>
/// <returns></returns>
public static bool Equal(this object obj1, object obj2)
{
return (obj1 == obj2) || (obj1 != null && obj1.Equals(obj2));
}
/// <summary>
/// 字符串转具体的数据
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public static object Parse(this Type type, string value)
{
object v = null;
var op_Implicit = type.GetMethod("op_Implicit", new Type[] { typeof(string) });
if (op_Implicit != null)
{
v = op_Implicit.FastInvoke(null, value);
}
else
{
var Parse = type.GetMethod("Parse", new Type[] { typeof(string) });
if (Parse != null)
{
v = Parse.FastInvoke(null, value);
}
else
{
v = TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value);
}
}
return v;
}
/// <summary>
/// 针对Grid添加控件元素并设置所在的单元格位置
/// </summary>
/// <param name="elements"></param>
/// <param name="element"></param>
/// <param name="col">列</param>
/// <param name="row">行</param>
/// <param name="colSpan">跨列</param>
/// <param name="rowSpan">跨行</param>
public static T Add<T>(this UIElementCollection elements, T element, int col = 0, int row = 0, int colSpan = 1, int rowSpan = 1) where T : UIElement
{
elements.Add(element);
var grid = elements.Owner as Controls.Grid;
if (grid != null)
{
if (col > 0)
{
Controls.Grid.ColumnIndex(element, col);
}
if (row > 0)
{
Controls.Grid.RowIndex(element, row);
}
if (colSpan > 1)
{
Controls.Grid.ColumnSpan(element, colSpan);
}
if (rowSpan > 1)
{
Controls.Grid.RowSpan(element, rowSpan);
}
}
else
{
throw new Exception("不是Grid控件");
}
return element;
}
/// <summary>
/// 添加Point
/// </summary>
/// <param name="points"></param>
/// <param name="x"></param>
/// <param name="y"></param>
public static void Add(this CPF.Collection<Drawing.Point> points, float x, float y)
{
points.Add(new Drawing.Point(x, y));
}
public static T[] ToArray<T>(this System.Collections.ObjectModel.ObservableCollection<T> list)
{
var a = new T[list.Count];
list.CopyTo(a, 0);
return a;
}
/// <summary>
/// 获取附加属性的名称
/// </summary>
/// <typeparam name="Value"></typeparam>
/// <param name="attached"></param>
/// <returns></returns>
public static string GetAttachedPropertyName<Value>(this Attached<Value> attached)
{
var type = attached.Target.GetType();
var field = type.GetField("propertyName");
return field.FastGetValue(attached.Target).ToString();
}
internal static readonly HashSet<Type> ConvertTypes = new HashSet<Type>() {
//typeof(System.Empty),
typeof(Object),
typeof(System.DBNull),
typeof(Boolean),
typeof(Char),
typeof(SByte),
typeof(Byte),
typeof(Int16),
typeof(UInt16),
typeof(Int32),
typeof(UInt32),
typeof(Int64),
typeof(UInt64),
typeof(Single),
typeof(Double),
typeof(Decimal),
typeof(DateTime),
typeof(Object), //TypeCode is discontinuous so we need a placeholder.
typeof(String)
};
static Type strType = typeof(string);
/// <summary>
/// 类型转换
/// </summary>
/// <param name="obj"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object ConvertTo(this object obj, Type type)
{
if (obj == null)
{
return null;
}
try
{
if (ConvertTypes.Contains(type))
{
return Convert.ChangeType(obj, type);
}
}
catch (Exception e)
{
throw new Exception(obj.GetType() + "无法转换成" + type, e);
}
var vType = obj.GetType();
if (vType == type || vType.IsSubclassOf(type))
{
return obj;
}
if (type.IsEnum)
{
try
{
return Enum.Parse(type, obj.ToString());
}
catch (Exception e)
{
throw new Exception(vType + "无法转换成" + type, e);
}
}
var parse = type.GetMethod("op_Implicit", new Type[] { vType });
if (parse == null)
{
if (type.Name == "Nullable`1")
{
if (vType == strType && "null".Equals(obj))
{
return null;
}
//var sType = type.GenericTypeArguments[0];
var sType = type.GetGenericArguments()[0];
var v = ConvertTo(obj, sType);
return type.GetConstructor(new Type[] { sType }).FastInvoke(v);
}
throw new Exception(vType + "无法转换成" + type);
}
else
{
try
{
if (vType == strType && "null".Equals(obj))
{
return null;
}
return parse.FastInvoke(null, obj);
}
catch (Exception e)
{
throw new Exception(vType + "无法转换成" + type, e);
}
}
}
/// <summary>
/// 获取对象属性值
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static object GetPropretyValue(this object obj, string propertyName)
{
//if (obj == null)
//{
// return null;
//}
if (obj is CpfObject cpf)
{
if (cpf.HasProperty(propertyName))
{
return cpf.GetValue(propertyName);
}
}
//var p = obj.GetType().GetProperty(propertyName);
//if (p == null)
//{
// throw new Exception("未找到属性:" + propertyName);
//}
//return p.FastGetValue(obj);
return obj.GetValue(propertyName);
}
/// <summary>
/// 设置属性值
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public static void SetPropretyValue(this object obj, string propertyName, object value)
{
var b = obj as CpfObject;
if (b != null)
{
if (!b.SetValue(value, propertyName))
{
b.SetValue(propertyName, value);
}
}
else
{
obj.SetValue(propertyName, value);
}
}
//public static void Add(this IList list)
//{
//}
/// <summary>
/// 定义一个加载动画你可以将耗时操作放到work委托里可以异步等待返回一个值。里面可以执行多个分段的任务并且刷新Message。
/// var r = await this.ShowLoading("开始加载...",a =>
/// {
/// System.Threading.Thread.Sleep(1000);
/// a.Message = "加载组件1...";
/// System.Threading.Thread.Sleep(1000);
/// a.Message = "加载组件2...";
/// System.Threading.Thread.Sleep(1000);
/// return "结果";
/// });
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="root"></param>
/// <param name="message"></param>
/// <param name="work"></param>
/// <returns></returns>
public static async Task<T> ShowLoading<T>(this UIElement root, string message, Func<LoadingBox, T> work)
{
var loadingBox = new LoadingBox { Message = message };
var layer = new LayerDialog
{
Name = "loadingDialog",
Content = loadingBox,
ShowCloseButton = false,
Background = null,
};
layer.ShowDialog(root);
return await
#if Net4
TaskEx
#else
Task
#endif
.Run(() =>
{
var r = work(loadingBox);
loadingBox.Invoke(layer.CloseDialog);
return r;
});
}
/// <summary>
/// 定义一个加载动画你可以将耗时操作放到work委托里可以异步等待返回一个值。里面可以执行多个分段的任务并且刷新Message。
/// await this.ShowLoading("开始加载...",a =>
/// {
/// System.Threading.Thread.Sleep(1000);
/// a.Message = "加载组件1...";
/// System.Threading.Thread.Sleep(1000);
/// a.Message = "加载组件2...";
/// System.Threading.Thread.Sleep(1000);
/// });
/// </summary>
/// <param name="root"></param>
/// <param name="message"></param>
/// <param name="work"></param>
/// <returns></returns>
public static async Task ShowLoading(this UIElement root, string message, Action<LoadingBox> work)
{
var loadingBox = new LoadingBox { Message = message };
var layer = new LayerDialog
{
Name = "loadingDialog",
Content = loadingBox,
ShowCloseButton = false,
Background = null,
};
layer.ShowDialog(root);
await
#if Net4
TaskEx
#else
Task
#endif
.Run(() =>
{
work(loadingBox);
loadingBox.Invoke(layer.CloseDialog);
});
}
/// <summary>
/// 循环创建子元素,
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="panel"></param>
/// <param name="count">循环次数</param>
/// <param name="func">循环中的索引,返回创建结果</param>
/// <returns></returns>
public static T LoopCreate<T>(this T panel, int count, Func<int, UIElement> func) where T : Panel
{
for (int i = 0; i < count; i++)
{
var e = func(i);
if (e != null)
{
panel.Children.Add(e);
}
}
return panel;
}
/// <summary>
/// 循环创建子元素,
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="panel"></param>
/// <param name="count">循环次数</param>
/// <param name="func">循环中的索引,当前容器,返回创建结果</param>
/// <returns></returns>
public static T LoopCreate<T>(this T panel, int count, Func<int, T, UIElement> func) where T : Panel
{
for (int i = 0; i < count; i++)
{
var e = func(i, panel);
if (e != null)
{
panel.Children.Add(e);
}
}
return panel;
}
/// <summary>
/// 一般用来设置元素属性初始化阶段比css优先级高
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="element"></param>
/// <param name="action"></param>
/// <returns></returns>
public static T AfterStyle<T>(this T element, Action<T> action) where T : UIElement
{
if (element.Root != null && element.Root.IsInitialized)
{
if (element is Control control && control.IsInitialized)
{
action(element);
}
else
{
element.afterStyle = action;
}
}
else
{
element.afterStyle = action;
}
return element;
}
}
class Enumerable : IList
{
IEnumerable list;
public Enumerable(IEnumerable list)
{
this.list = list;
}
public object this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool IsFixedSize => throw new NotImplementedException();
public bool IsReadOnly => throw new NotImplementedException();
public int Count => throw new NotImplementedException();
public bool IsSynchronized => throw new NotImplementedException();
public object SyncRoot => throw new NotImplementedException();
public int Add(object value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(object value)
{
throw new NotImplementedException();
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public IEnumerator GetEnumerator()
{
return list.GetEnumerator();
}
public int IndexOf(object value)
{
throw new NotImplementedException();
}
public void Insert(int index, object value)
{
throw new NotImplementedException();
}
public void Remove(object value)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
}
class DataRows : IList, INotifyCollectionChanged
{
DataTable dataTable;
Dictionary<DataRow, DataRowObject> rows = new Dictionary<DataRow, DataRowObject>();
public DataTable DataTable
{
get { return dataTable; }
}
public DataRows(DataTable dataTable)
{
this.dataTable = dataTable;
foreach (DataRow item in dataTable.Rows)
{
rows.Add(item, new DataRowObject(item));
}
dataTable.RowChanged += DataTable_RowChanged;
dataTable.RowDeleting += DataTable_RowDeleting;
dataTable.TableClearing += DataTable_TableClearing;
dataTable.ColumnChanged += DataTable_ColumnChanged;
dataTable.RowDeleted += DataTable_RowDeleted;
}
private void DataTable_RowDeleted(object sender, DataRowChangeEventArgs e)
{
rows.Remove(e.Row);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.Row, deleteIndex));
}
private void DataTable_TableClearing(object sender, DataTableClearEventArgs e)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void DataTable_ColumnChanged(object sender, DataColumnChangeEventArgs e)
{
if (rows.TryGetValue(e.Row, out var row))
{
row.NotifyPropertyChanged(e.Column.ColumnName);
}
}
int deleteIndex = -1;
private void DataTable_RowDeleting(object sender, DataRowChangeEventArgs e)
{
deleteIndex = dataTable.Rows.IndexOf(e.Row);
}
private void DataTable_RowChanged(object sender, DataRowChangeEventArgs e)
{
switch (e.Action)
{
case DataRowAction.Add:
int index;
if (e.Row != dataTable.Rows[dataTable.Rows.Count - 1])
{
index = dataTable.Rows.IndexOf(e.Row);
}
else
{
index = dataTable.Rows.Count - 1;
}
var r = new DataRowObject(e.Row);
rows.Add(e.Row, r);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, r, index));
break;
case DataRowAction.Change:
break;
case DataRowAction.ChangeCurrentAndOriginal:
break;
case DataRowAction.ChangeOriginal:
break;
case DataRowAction.Commit:
break;
case DataRowAction.Delete:
break;
case DataRowAction.Nothing:
break;
case DataRowAction.Rollback:
break;
default:
break;
}
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
Events[nameof(CollectionChanged)]?.Invoke(this, e);
//if (CollectionChanged != null)
//{
// CollectionChanged(this, e);
//}
}
public DataRowObject this[int index]
{
get
{
return rows[dataTable.Rows[index]];
}
set => throw new NotImplementedException();
}
object IList.this[int index] { get { return rows[dataTable.Rows[index]]; } set => throw new NotImplementedException(); }
public int Count { get { return dataTable.Rows.Count; } }
public bool IsReadOnly { get { return dataTable.Rows.IsReadOnly; } }
public bool IsFixedSize { get { return false; } }
public bool IsSynchronized { get { return false; } }
public object SyncRoot { get { return dataTable.Rows.SyncRoot; } }
WeakEventHandlerList events;
/// <summary>
/// 事件列表,用于优化事件订阅内存
/// </summary>
protected WeakEventHandlerList Events
{
get
{
if (events == null)
{
events = new WeakEventHandlerList();
}
return events;
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add { Events.AddHandler(value); }
remove { Events.RemoveHandler(value); }
}
public void Add(DataRow item)
{
dataTable.Rows.Add(item);
}
int IList.Add(object value)
{
dataTable.Rows.Add((DataRow)value);
return dataTable.Rows.Count - 1;
}
public void Clear()
{
dataTable.Rows.Clear();
}
public bool Contains(DataRow item)
{
return dataTable.Rows.Contains(item.ItemArray);
}
public bool Contains(object value)
{
return dataTable.Rows.Contains(value);
}
public void CopyTo(DataRow[] array, int arrayIndex)
{
dataTable.Rows.CopyTo(array, arrayIndex);
}
public void CopyTo(Array array, int index)
{
dataTable.Rows.CopyTo(array, index);
}
int IList.IndexOf(object value)
{
if (value is DataRowObject dataRowObject)
{
return dataTable.Rows.IndexOf(dataRowObject.row);
}
else
{
return dataTable.Rows.IndexOf((DataRow)value);
}
}
public void Insert(int index, DataRow item)
{
dataTable.Rows.InsertAt(item, index);
}
public void Insert(int index, object value)
{
dataTable.Rows.InsertAt((DataRow)value, index);
}
public void Remove(DataRow item)
{
dataTable.Rows.Remove(item);
}
public void Remove(object value)
{
dataTable.Rows.Remove((DataRow)value);
}
public void RemoveAt(int index)
{
dataTable.Rows.RemoveAt(index);
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (var item in rows.Values)
{
yield return item;
}
}
}
class DataRowObject : CpfObject
{
public DataRow row;
public DataRowObject(DataRow row)
{
this.row = row;
}
public override object GetValue([CallerMemberName] string propertyName = null)
{
//if (row.Table.Columns.Contains(propertyName))
//{
return row[propertyName];
//}
//return base.GetValue(propertyName);
}
public override bool SetValue<T>(T value, [CallerMemberName] string propertyName = null)
{
//if (row.Table.Columns.Contains(propertyName))
//{
row[propertyName] = value;
return true;
//}
//return base.SetValue(value, propertyName);
}
//internal void Raise(string propertyName)
//{
// PropertyChangedEventHandler handler = (PropertyChangedEventHandler)Events["INotifyPropertyChanged"];
// if (handler != null)
// {
// handler(this, new PropertyChangedEventArgs(propertyName));
// }
//}
public override bool HasProperty(string propertyName)
{
return row.Table.Columns.Contains(propertyName);
}
public override PropertyMetadataAttribute GetPropertyMetadata(string propertyName)
{
//return base.GetPropertyMetadata(propertyName);
return new PropertyMetadataAttribute() { PropertyName = propertyName, PropertyType = row.Table.Columns[propertyName].DataType };
}
}
}