CPF/CPF/ObjectExtenstions.cs

1000 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
public static bool Or<T>(this T obj, params T[] ps)
{
foreach (var item in ps)
{
if (obj.Equals(item))
{
return true;
}
}
return false;
}
}
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 };
}
}
}