988 lines
31 KiB
C#
988 lines
31 KiB
C#
![]() |
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) < 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 };
|
|||
|
}
|
|||
|
}
|
|||
|
}
|