Merge branch 'master' into Toolkit

This commit is contained in:
luxiaoqi 2023-11-29 11:00:41 +08:00
commit 6631fec5e6
24 changed files with 646 additions and 7280 deletions

View File

@ -657,6 +657,7 @@ namespace CPF.Windows
WM_USER = 0x0400,
WM_DISPATCH_WORK_ITEM = WM_USER,
WM_TOUCH = 0x0240,
WM_GESTURE = 0x0119,
//WM_PARENTNOTIFY = 0x0210,
WM_NCPOINTERUPDATE = 0x0241,
WM_NCPOINTERDOWN = 0x0242,

View File

@ -74,6 +74,7 @@ namespace CPF.Windows
}
WNDCLASSEX wc = new WNDCLASSEX();
bool touchMsg;
public WindowImpl()
{
if (Window == null)
@ -89,6 +90,10 @@ namespace CPF.Windows
{
isLayered = true;
}
if ((v.Major == 6 && v.Minor < 2))
{
touchMsg = RegisterTouchWindow(handle, RegisterTouchFlags.TWF_NONE);
}
_className = "CPFWindow-" + Guid.NewGuid();
// 初始化窗口类结构
wc.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
@ -707,60 +712,83 @@ namespace CPF.Windows
case WindowsMessage.WM_POINTERDOWN:
case WindowsMessage.WM_POINTERUP:
case WindowsMessage.WM_POINTERUPDATE:
var id = GetPointerId(wParam);
var position = GetPointerLocation(lParam);
position = PointToClient(position);
//Debug.WriteLine("id:" + id + " " + position);
POINTER_INFO pi = new POINTER_INFO();
if (GetPointerInfo(id, ref pi))
if (!touchMsg)
{
switch ((WindowsMessage)msg)
var id = GetPointerId(wParam);
var position = GetPointerLocation(lParam);
position = PointToClient(position);
//Debug.WriteLine("id:" + id + " " + position);
POINTER_INFO pi = new POINTER_INFO();
if (GetPointerInfo(id, ref pi))
{
case WindowsMessage.WM_POINTERDOWN:
//Debug.WriteLine("down");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchDown);
break;
case WindowsMessage.WM_POINTERUP:
//Debug.WriteLine("up");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchUp);
//root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchLeave);
break;
case WindowsMessage.WM_POINTERUPDATE:
//Debug.WriteLine("update");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchMove);
break;
switch ((WindowsMessage)msg)
{
case WindowsMessage.WM_POINTERDOWN:
//Debug.WriteLine("down");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchDown);
break;
case WindowsMessage.WM_POINTERUP:
//Debug.WriteLine("up");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchUp);
//root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchLeave);
break;
case WindowsMessage.WM_POINTERUPDATE:
//Debug.WriteLine("update");
root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchMove);
break;
}
}
}
break;
case WindowsMessage.WM_POINTERWHEEL:
Debug.WriteLine("WM_POINTERWHEEL");
break;
//case WindowsMessage.WM_TOUCH:
// {
// var touchInputCount = wParam.ToInt32();
// var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount];
// if (GetTouchInputInfo(lParam, (uint)touchInputCount, pTouchInputs, Marshal.SizeOf(typeof(TOUCHINPUT))))
// {
// for (int i = 0; i < touchInputCount; i++)
// {
// var touchInput = pTouchInputs[i];
case WindowsMessage.WM_GESTURE:
Debug.WriteLine("WM_GESTURE");
break;
case WindowsMessage.WM_TOUCH:
if (touchMsg)
{
var touchInputCount = wParam.ToInt32();
var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount];
if (GetTouchInputInfo(lParam, (uint)touchInputCount, pTouchInputs, Marshal.SizeOf(typeof(TOUCHINPUT))))
{
for (int i = 0; i < touchInputCount; i++)
{
var input = pTouchInputs[i];
// //Input?.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time,
// // _owner,
// // touchInput.Flags.HasAllFlags(TouchInputFlags.TOUCHEVENTF_UP) ?
// // RawPointerEventType.TouchEnd :
// // touchInput.Flags.HasAllFlags(TouchInputFlags.TOUCHEVENTF_DOWN) ?
// // RawPointerEventType.TouchBegin :
// // RawPointerEventType.TouchUpdate,
// // PointToClient(new PixelPoint(touchInput.X / 100, touchInput.Y / 100)),
// // WindowsKeyboardDevice.Instance.Modifiers,
// // touchInput.Id));
// }
// CloseTouchInputHandle(lParam);
// return IntPtr.Zero;
// }
// break;
// }
if ((input.Flags & TouchInputFlags.TOUCHEVENTF_DOWN) > 0)
{
Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
{
Id = (int)input.Id,
Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
}, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchDown);
}
else if ((input.Flags & TouchInputFlags.TOUCHEVENTF_UP) > 0)
{
Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
{
Id = (int)input.Id,
Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
}, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchUp);
}
else if ((input.Flags & TouchInputFlags.TOUCHEVENTF_MOVE) > 0)
{
Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
{
Id = (int)input.Id,
Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
}, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchMove);
}
}
CloseTouchInputHandle(lParam);
return IntPtr.Zero;
}
}
break;
case UnmanagedMethods.WindowsMessage.WM_CLOSE:
bool? preventClosing = Closing?.Invoke();
if (preventClosing == true)
@ -1523,7 +1551,7 @@ namespace CPF.Windows
}
invalidateRect.Intersect(all);//最后失效区域为在控件区域里面的相交区域
}
if (!paint && (timer == null ))//&& isLayered
if (!paint && (timer == null))//&& isLayered
{
paint = true;
BeginInvoke(a =>
@ -1692,7 +1720,6 @@ namespace CPF.Windows
public void SetVisible(bool visible)
{
//var v = IsWindowVisible(handle);
root.InputManager.TouchDevice.ClearPoints();
if (visible)
{
if (windowState == WindowState.Normal)

View File

@ -212,4 +212,104 @@ namespace CPF
return false;
}
}
/// <summary>
/// 绑定附加属性
/// </summary>
public class AttachedDescribe : BindingDescribe
{
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="sourceProperty"></param>
public AttachedDescribe(object value, string sourceProperty) : base(sourceProperty)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="sourceProperty"></param>
/// <param name="binding"></param>
public AttachedDescribe(object value, string sourceProperty, BindingMode binding) : base(sourceProperty, binding)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
/// <param name="binding"></param>
public AttachedDescribe(object value, object source, string sourceProperty, BindingMode binding) : base(source, sourceProperty, binding)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
public AttachedDescribe(object value, object source, string sourceProperty) : base(source, sourceProperty)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
/// <param name="convert"></param>
public AttachedDescribe(object value, object source, string sourceProperty, Func<object, object> convert) : base(source, sourceProperty, convert)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
/// <param name="binding"></param>
/// <param name="convert"></param>
public AttachedDescribe(object value, object source, string sourceProperty, BindingMode binding, Func<object, object> convert) : base(source, sourceProperty, binding, convert)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
/// <param name="binding"></param>
/// <param name="convert"></param>
/// <param name="convertBack"></param>
public AttachedDescribe(object value, object source, string sourceProperty, BindingMode binding, Func<object, object> convert, Func<object, object> convertBack) : base(source, sourceProperty, binding, convert, convertBack)
{
Value = value;
}
/// <summary>
/// 设置和绑定附加属性
/// </summary>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="sourceProperty"></param>
/// <param name="binding"></param>
/// <param name="convert"></param>
/// <param name="convertBack"></param>
/// <param name="SourceToTargetError"></param>
/// <param name="TargetToSourceError"></param>
public AttachedDescribe(object value, object source, string sourceProperty, BindingMode binding, Func<object, object> convert, Func<object, object> convertBack, Action<Binding, object, Exception> SourceToTargetError, Action<Binding, object, Exception> TargetToSourceError) : base(source, sourceProperty, binding, convert, convertBack, SourceToTargetError, TargetToSourceError)
{
Value = value;
}
public object Value { get; set; }
}
}

View File

@ -12,6 +12,7 @@ using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Linq.Expressions;
using System.Threading;
using System.Diagnostics;
namespace CPF
{
@ -236,6 +237,14 @@ namespace CPF
internal set;
}
/// <summary>
/// 链式绑定下标
/// </summary>
public int SourcePropertyIndex
{
get;
internal set;
} = 0;
/// <summary>
/// Owner被绑定的属性名
/// </summary>
public string TargetPropertyName
@ -353,6 +362,7 @@ namespace CPF
current.Push(this);
try
{
if (Source == null || !Source.IsAlive)
{
if (Owner.HasProperty(TargetPropertyName))
@ -366,29 +376,22 @@ namespace CPF
}
return;
}
//CpfObject s = Source.Target as CpfObject;
//if (s == null)
//{
// var p = Source.Target.GetType().GetProperty(SourcePropertyName);
// if (p == null)
// {
// throw new Exception("未找到" + Source.Target + "的属性:" + SourcePropertyName);
// }
// value = p.FastGetValue(Source.Target);
//}
//else
//{
// if (s.HasProperty(SourcePropertyName))
// {
// value = s.GetValue(SourcePropertyName);
// }
// else
// {
// var p = s.Type.GetProperty(SourcePropertyName);
// value = p.FastGetValue(Source.Target);
// }
//}
value = Source.Target.GetPropretyValue(SourcePropertyName);
/*var SourcePropertyNames = SourcePropertyName.Split('.');
if (SourcePropertyNames.Length > 1) {
value = Source.Target;
for (int i = 0; i < SourcePropertyNames.Length; i++)
{
value = value.GetPropretyValue(SourcePropertyNames[i]);
}
}
else
{
value = Source.Target.GetPropretyValue(SourcePropertyName);
}*/
value = GetPropertySource(SourcePropertyName, Source.Target);
if (value != null) {
value = value.GetPropretyValue(SourcePropertyName.Split('.').LastOrDefault());
}
if (Convert != null)
{
value = Convert(value);
@ -453,8 +456,23 @@ namespace CPF
{
if (!b.SetValue(nv, SourcePropertyName))
{
/*var SourcePropertyNames = SourcePropertyName.Split('.');
if (SourcePropertyNames.Length == 1)
{
b.SetValue(SourcePropertyName, nv);
}*/
/*var Target = b;
for (int i = 0; i < SourcePropertyNames.Length-1; i++)
{
Target = Target.GetPropretyValue(SourcePropertyNames[i]) as CpfObject;
}*/
var SourcePropertyNames = SourcePropertyName.Split('.');
var Target = GetPropertySource(SourcePropertyName, b);
if (Target != null) {
Target.SetValue(SourcePropertyNames.LastOrDefault(), nv);
}
//b.Type.GetProperty(SourcePropertyName).FastSetValue(b, nv);
b.SetValue(SourcePropertyName, nv);
}
}
else
@ -533,7 +551,8 @@ namespace CPF
}
void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (SourcePropertyName == e.PropertyName)
var Temp_SourcePropertyName = SourcePropertyName.Split('.').LastOrDefault();
if (Temp_SourcePropertyName == e.PropertyName)
{
//CPFObject s = sender as CPFObject;
//object value;
@ -557,6 +576,47 @@ namespace CPF
}
}
}
internal object GetPropertySource(string SourcePropertyName,object Source)
{
try
{
var SourcePropertyNames = SourcePropertyName.Split('.');
if (SourcePropertyNames.Length == 1)
{
return Source;
}
var Target = Source;
for (int i = 0; i < SourcePropertyNames.Length - 1; i++)
{
var Temp_Target = Target.GetPropretyValue(SourcePropertyNames[i]) as CpfObject;
if (Temp_Target == null)
{
return null;
}
Target = Temp_Target;
}
return Target;
}
catch (Exception ex)
{
throw new Exception($"错误:{ex}");
}
}
CpfObject SourceProperty = null;
private void Target_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//重新绑定
if (BindingMode == BindingMode.TwoWay ||
BindingMode == BindingMode.OneWay ||
BindingMode == BindingMode.OneTime)
{
var SourcePropertyNames = SourcePropertyName.Split('.');
var Temp_Target = GetPropertySource(SourcePropertyName, Source.Target);
var data = (Temp_Target as CpfObject)?.GetValue(SourcePropertyNames.LastOrDefault());
Owner.SetPropretyValue(TargetPropertyName, data);
}
RegisterPropertyChanged(sender as INotifyPropertyChanged);
}
internal void RegisterPropertyChanged(INotifyPropertyChanged notify)
{
@ -564,7 +624,25 @@ namespace CPF
//{
// throw new Exception("错误");
//}
RegisterPropertyChanged(notify, PropertyChanged);
var SourcePropertyNames = SourcePropertyName.Split('.');
if (SourcePropertyNames.Length == 1)
{
RegisterPropertyChanged(notify, PropertyChanged);
return;
}
var Target = Source.Target;
RegisterPropertyChanged(Target as CpfObject, Target_PropertyChanged);
for (int i = 0; i < SourcePropertyNames.Length - 1; i++)
{
var Temp_Target = Target.GetPropretyValue(SourcePropertyNames[i]) as CpfObject;
if (Temp_Target == null)
{
return;
}
RegisterPropertyChanged(Temp_Target, Target_PropertyChanged);
Target = Temp_Target;
}
RegisterPropertyChanged(Target as INotifyPropertyChanged, PropertyChanged);
}
internal void CancellationPropertyChanged(INotifyPropertyChanged notify)
{

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;
namespace CPF
{
@ -16,6 +17,7 @@ namespace CPF
{
PropertyName = sourceProperty;
}
public BindingDescribe(string sourceProperty, BindingMode binding)
{
PropertyName = sourceProperty;
@ -64,7 +66,7 @@ namespace CPF
this.TargetToSourceError = TargetToSourceError;
}
public BindingDescribe(Action<CpfObject, object> command)
public BindingDescribe(CommandDescribe command)
{
Command = command;
}
@ -78,7 +80,7 @@ namespace CPF
/// <summary>
/// 简化绑定命令的命令,如果设置了该属性,则使用命令绑定
/// </summary>
public Action<CpfObject, object> Command { get; set; }
public CommandDescribe Command { get; set; }
//public CpfObject Owner { get; internal set; }
@ -331,7 +333,14 @@ namespace CPF
}
return b;
}
//public static BindingDescribe operator *(BindingDescribe property1, string property2)
//{
// return null;
//}
//public static BindingDescribe operator *(BindingDescribe property1, BindingDescribe property2)
//{
// return null;
//}
public BindingMode BindingMode { get; set; } = BindingMode.OneWay;
public object Source { get; set; }
@ -343,7 +352,7 @@ namespace CPF
{
return new BindingDescribe { PropertyName = sourceProperty };
}
public static explicit operator BindingDescribe(Action<CpfObject, object> command)
public static implicit operator BindingDescribe(CommandDescribe command)
{
return new BindingDescribe { Command = command };
}
@ -386,5 +395,50 @@ namespace CPF
return base.GetHashCode();
}
}
/// <summary>
/// 命令绑定
/// </summary>
public class CommandDescribe
{
public Action<CpfObject, object> Action { get; set; }
public string MethodName { get; set; }
public object[] Parameters { get; set; }
public object Target { get; set; }
public Func<UIElement, UIElement> Find { get; set; }
/// <summary>
/// 用委托定义个命令绑定
/// </summary>
/// <param name="command"></param>
public CommandDescribe(Action<CpfObject, object> command)
{
Action = command;
}
/// <summary>
/// 定义个命令绑定
/// </summary>
/// <param name="methodName"></param>
/// <param name="obj"></param>
/// <param name="ps"></param>
public CommandDescribe(string methodName, object obj = null, params object[] ps)
{
MethodName = methodName;
Parameters = ps;
Target = obj;
}
/// <summary>
/// 查找元素并绑定命令
/// </summary>
/// <param name="methodName"></param>
/// <param name="find"></param>
/// <param name="ps"></param>
public CommandDescribe(string methodName, Func<UIElement, UIElement> find, params object[] ps)
{
MethodName = methodName;
Parameters = ps;
Find = find;
}
}
}

View File

@ -7,6 +7,7 @@ using System.Linq;
using CPF.Controls;
using System.Linq.Expressions;
using System.Reflection;
using System.Diagnostics;
namespace CPF
{
@ -102,6 +103,7 @@ namespace CPF
else
{
//((INotifyPropertyChanged)source).PropertyChanged += b.PropertyChanged;
b.RegisterPropertyChanged((INotifyPropertyChanged)source);
b.SourceToTarget();
}
@ -114,6 +116,7 @@ namespace CPF
{
b.SourceToTarget();
}
//Debug.WriteLine($"sourcePropertyName:{sourcePropertyName},bindingMode:{bindingMode}");
return b;
}
/// <summary>

View File

@ -558,8 +558,9 @@ namespace CPF.Controls
if (codeTextView.Text.Length >= caretIndex)
{
//Text = codeTextView.Text.Insert((int)caretIndex, e.Text);
codeTextView.InsertText(caretIndex, e.Text);
var caretIndex_old = caretIndex;
caretIndex = caretIndex + (uint)e.Text.Length;
codeTextView.InsertText(caretIndex_old, e.Text);
selectionEnd = caretIndex;
}
else

View File

@ -11,9 +11,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace CPF.Controls
{
[Browsable(false)]
public class LineNumber : Control
{

View File

@ -149,6 +149,25 @@ namespace CPF.Controls
{
base.OnOverrideMetadata(overridePropertys);
overridePropertys.Override(nameof(Height), new UIPropertyMetadataAttribute((FloatField)"100%", UIPropertyOptions.AffectsMeasure));
//overridePropertys.Override(nameof(DataContext), new UIPropertyMetadataAttribute(null, false));
}
//protected override void OnLayoutUpdated()
//{
// base.OnLayoutUpdated();
// if (Root.LayoutManager.IsVisible(this))
// {
// DataContext = Row.DataContext;
// }
//}
//protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue, PropertyMetadataAttribute propertyMetadata)
//{
// if (propertyName == nameof(DataContext))
// {
// }
// base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata);
//}
}
}

View File

@ -282,6 +282,7 @@ namespace CPF.Controls
}
else
{
textSize = new Size();
return base.ArrangeOverride(finalSize);
}
}

View File

@ -860,6 +860,7 @@ namespace CPF.Controls
base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata);
if (propertyName == nameof(Visibility))
{
InputManager.TouchDevice.ClearPoints();
viewImpl.SetVisible((Visibility)newValue == Visibility.Visible);
}
if (!onChecked && radioButtonGroupProperty != null && radiobuttons != null && radioButtonGroupProperty.TryGetValue(propertyName, out var group))

File diff suppressed because it is too large Load Diff

View File

@ -25,17 +25,20 @@ namespace CPF
static ConcurrentDictionary<Type, ObjInfo> typeCache = new ConcurrentDictionary<Type, ObjInfo>();
static ConcurrentDictionary<Type, PropertyMetadataAttribute[]> typePropertyCache = new ConcurrentDictionary<Type, PropertyMetadataAttribute[]>();
static ConcurrentDictionary<Type, HashSet<string>> inheritsProperties = new ConcurrentDictionary<Type, HashSet<string>>();
static ConcurrentDictionary<Type, HashSet<string>> breakInheritsProperties = new ConcurrentDictionary<Type, HashSet<string>>();
static ConcurrentDictionary<Type, List<MethodInfo>> propertyChangedMethods = new ConcurrentDictionary<Type, List<MethodInfo>>();
static KeyValuePair<Type, ObjInfo>[] saveTypeCache;
static KeyValuePair<Type, PropertyMetadataAttribute[]>[] saveTypePropertyCache;
static KeyValuePair<Type, HashSet<string>>[] saveInheritsProperties;
static KeyValuePair<Type, HashSet<string>>[] saveBreakInheritsProperties;
static KeyValuePair<Type, List<MethodInfo>>[] savePropertyChangedMethods;
public static void SetTypeCache()
{
saveTypeCache = typeCache.ToArray();
saveTypePropertyCache = typePropertyCache.ToArray();
saveInheritsProperties = inheritsProperties.ToArray();
saveBreakInheritsProperties = breakInheritsProperties.ToArray();
FastReflectionExtensions.SetTypeCache();
savePropertyChangedMethods = propertyChangedMethods.ToArray();
}
@ -68,6 +71,15 @@ namespace CPF
}
saveInheritsProperties = null;
}
if (saveBreakInheritsProperties != null)
{
breakInheritsProperties.Clear();
foreach (var item in saveBreakInheritsProperties)
{
breakInheritsProperties.TryAdd(item.Key, item.Value);
}
saveBreakInheritsProperties = null;
}
if (savePropertyChangedMethods != null)
{
propertyChangedMethods.Clear();
@ -88,9 +100,13 @@ namespace CPF
internal HybridDictionary<string, object> attachedValues;
/// <summary>
/// 继承属性的特性
/// 继承属性不同继承类型分支下来的属性名可能相同但是属性ID不同只能保存属性名
/// </summary>
internal HashSet<string> inheritsPropertyName;
/// <summary>
/// 终止继承属性不同继承类型分支下来的属性名可能相同但是属性ID不同只能保存属性名
/// </summary>
internal HashSet<string> breakInheritsPropertyName;
AttachedProperties attached;
/// <summary>
@ -178,7 +194,21 @@ namespace CPF
{
if (value.Command != null)
{
Commands.Add(propertyName, value.Command);
if (value.Command.Action != null)
{
Commands.Add(propertyName, value.Command.Action);
}
else if (!string.IsNullOrWhiteSpace(value.Command.MethodName))
{
if (value.Command.Find != null)
{
Commands.Add(propertyName, value.Command.MethodName, value.Command.Find, value.Command.Parameters);
}
else
{
Commands.Add(propertyName, value.Command.MethodName, value.Command.Target, value.Command.Parameters);
}
}
}
else
{
@ -217,8 +247,20 @@ namespace CPF
var type = attached.GetType();
try
{
var p = typeof(OptionalParameter<>).MakeGenericType(type.GetGenericArguments()[0]);
attached.Method.FastInvoke(attached.Target, this, Activator.CreateInstance(p, value));
if (value is AttachedDescribe describe)
{
var p = typeof(OptionalParameter<>).MakeGenericType(type.GetGenericArguments()[0]);
attached.Method.FastInvoke(attached.Target, this, Activator.CreateInstance(p, describe.Value));
var targetType = attached.Target.GetType();
var field = targetType.GetField("propertyName");
var name = field.FastGetValue(attached.Target).ToString();
AttachedNotify.Bindings.Add(name, describe.PropertyName, describe.Source, describe.BindingMode, describe.Convert, describe.ConvertBack, describe.SourceToTargetError, describe.TargetToSourceError);
}
else
{
var p = typeof(OptionalParameter<>).MakeGenericType(type.GetGenericArguments()[0]);
attached.Method.FastInvoke(attached.Target, this, Activator.CreateInstance(p, value));
}
}
catch (Exception e)
{
@ -327,18 +369,31 @@ namespace CPF
typePropertyCache.TryAdd(type, propertyList.ToArray());
var l = new HashSet<string>();
var b = new HashSet<string>();
foreach (var item in objInfo)
{
if (item.Value is UIPropertyMetadataAttribute attribute && attribute.Inherits)
if (item.Value is UIPropertyMetadataAttribute attribute)
{
l.Add(attribute.PropertyName);
if (attribute.Inherits)
{
l.Add(attribute.PropertyName);
}
else
{
b.Add(attribute.PropertyName);
}
}
}
if (l.Count == 0)
{
l = null;
}
if (b.Count == 0)
{
b = null;
}
inheritsProperties.TryAdd(type, l);
breakInheritsProperties.TryAdd(type, b);
//Type tt = typeof(PropertyChangedAttribute);
//var ms = type.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, (a, b) => a.CustomAttributes.Any(c => c.AttributeType == tt), null);
@ -444,6 +499,8 @@ namespace CPF
//valueIndexs = new ByteArray((byte)objInfo.Count);
valueIndexs = new byte[objInfo.Count];
inheritsPropertyName = inheritsProperties[type];
breakInheritsPropertyName = breakInheritsProperties[type];
if (objInfo.Computed != null)
{
foreach (var item in objInfo.Computed)

View File

@ -213,7 +213,6 @@ namespace CPF.Design
public void SetVisible(bool visible)
{
root.InputManager.TouchDevice.ClearPoints();
root.LayoutManager.ExecuteLayoutPass();
root.Invalidate();
}

View File

@ -99,7 +99,7 @@ namespace CPF
{
if (item.IsDisposed)
{
break;
continue;
}
var rect = GetRect(item.Parent, item.GetContentBounds(), item);
rect.Intersect(new Rect(new Point(), item.ActualSize));

53
CPF/Obx.cs Normal file
View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
namespace CPF
{
public class Obx<T>: BindingDescribe where T : CpfObject, new()
{
public Obx(Expression<Func<T, object>> sourceProperty)
{
var expression = GetMemberExpression(sourceProperty.Body);
PropertyName = GetMemberPath(expression);
}
public Obx(Expression<Func<T, object>> sourceProperty, BindingMode binding)
{
var expression = GetMemberExpression(sourceProperty.Body);
PropertyName = GetMemberPath(expression);
BindingMode = binding;
}
public Obx(Expression<Func<T, object>> sourceProperty, BindingMode binding, Func<object, object> convert)
{
var expression = GetMemberExpression(sourceProperty.Body);
PropertyName = GetMemberPath(expression);
BindingMode = binding;
Convert = convert;
}
private static MemberExpression GetMemberExpression(Expression expression)
{
if (expression is MemberExpression memberExpression)
{
return memberExpression;
}
else if (expression is UnaryExpression unaryExpression)
{
return (MemberExpression)unaryExpression.Operand;
}
else
{
throw new ArgumentException("Invalid expression. Expected a MemberExpression or UnaryExpression.");
}
}
private static string GetMemberPath(MemberExpression expression)
{
if (expression.Expression is MemberExpression memberExpression)
{
return GetMemberPath(memberExpression) + "." + expression.Member.Name;
}
return expression.Member.Name;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1372,29 +1372,41 @@ namespace CPF
}
}
}
/// <summary>
/// 继承属性需要一级级传递,直到没有元素或者取消继承为止
/// </summary>
/// <param name="element"></param>
/// <param name="propertyName"></param>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
void Inherits(UIElement element, string propertyName, object oldValue, object newValue)
{
if (!element.HasLocalOrStyleValue(propertyName, out var p))
{
element.inheritsSet = true;
//var p = element.GetPropertyMetadata(propertyName);
if (p != null)
var ui = p as UIPropertyMetadataAttribute;
if (ui != null && ui.Inherits)
{
if (p is UIPropertyMetadataAttribute ui && ui.Inherits)
{
element.inheritsValues.Remove(propertyName);
element.inheritsValues.Add(propertyName, new InheritsValue { Value = newValue, ValueForm = ValueFrom.Property });
}
element.inheritsSet = true;
element.inheritsValues.Remove(propertyName);
element.inheritsValues.Add(propertyName, new InheritsValue { Value = newValue, ValueForm = ValueFrom.Property });
element.OnPropertyChanged(propertyName, oldValue, newValue, p);
element.inheritsSet = false;
for (int i = 0; i < element.Children.Count; i++)
{
Inherits(element.children[i], propertyName, oldValue, newValue);
}
}
else if (ui == null)
{
for (int i = 0; i < element.Children.Count; i++)
{
Inherits(element.children[i], propertyName, oldValue, newValue);
}
}
element.inheritsSet = false;
//if (propertyName != nameof(DataContext) || !element.HasLocalOrStyleValue(propertyName, out p))
//{
for (int i = 0; i < element.Children.Count; i++)
{
Inherits(element.children[i], propertyName, oldValue, newValue);
}
//}
}
}
@ -2480,6 +2492,9 @@ namespace CPF
viewRenderRect = true;
}
}
/// <summary>
/// 是否需要更新渲染区域
/// </summary>
[NotCpfProperty]
internal bool viewRenderRect { get { return GetFlag(CoreFlags.viewRenderRect); } set { SetFlag(CoreFlags.viewRenderRect, value); } }
//protected virtual void ArrangeCore(in Rect finalRect)
@ -3441,7 +3456,7 @@ namespace CPF
}
break;
}
else if (p.HasLocalOrStyleValue(item, out _))
else if (p.HasLocalOrStyleValue(item, out var pa))
{
var value = p.GetValue(item);
var old = GetValue(item);
@ -3457,6 +3472,10 @@ namespace CPF
}
}
}
if (p.breakInheritsPropertyName != null && p.breakInheritsPropertyName.Contains(item))
{
break;
}
p = p.Parent;
}
}

View File

@ -1,24 +0,0 @@
#if Net4||Net2
namespace System
{
/// <summary>
/// 值元组
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
[Serializable]
public struct ValueTuple<T1, T2>
{
public ValueTuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
public T1 Item1;
public T2 Item2;
}
}
#endif

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net40;net5;net6</TargetFrameworks>
<TargetFrameworks>netcoreapp3.0;net40;net5;net6;net8</TargetFrameworks>
<ApplicationIcon />
<StartupObject />
<OutputType>WinExe</OutputType>
@ -110,6 +110,7 @@
<PropertyGroup>
<DefineConstants Condition="'$(TargetFramework)'=='net40'">Net4</DefineConstants>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<Nullable>disable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netcoreapp3.0|AnyCPU'">
<Optimize>false</Optimize>

View File

@ -3,11 +3,81 @@ using System;
using System.Collections.Generic;
using System.Text;
using CPF.Controls;
using static System.Net.Mime.MediaTypeNames;
namespace ConsoleApp1
{
public class data : CpfObject
{
public data test {
get
{
return (data)GetValue();
}
set
{
SetValue(value);
}
}
public data()
{
test = this;
}
[PropertyMetadata(null)]
public string Name
{
get
{
return (string)GetValue();
}
set
{
SetValue(value);
}
}
}
public class dataa : CpfObject
{
public data test
{
get
{
return (data)GetValue();
}
set
{
SetValue(value);
}
}
public dataa()
{
// test = new data();
}
[PropertyMetadata(null)]
public string Name
{
get
{
return (string)GetValue();
}
set
{
SetValue(value);
}
}
}
class MainModel : CpfObject
{
public dataa Test1 {
get
{
return (dataa)GetValue();
}
set
{
SetValue(value);
}
}
[PropertyMetadata("默认值")]
public string Test
{
@ -28,6 +98,7 @@ namespace ConsoleApp1
public MainModel()
{
Test1 = new dataa();
Items = new Collection<(string, string)>();
TestItems = new Collection<(string, int)>();

View File

@ -188,6 +188,7 @@ namespace ConsoleApp1
{
MarginTop = 0,
Content = "点击生成pdf",
[nameof(Button.Content)]= new Obx<MainModel>(a => a.Test1.test.test.test.test.Name),
Commands =
{
{
@ -235,7 +236,7 @@ namespace ConsoleApp1
Content="另外一个演示窗体😍",
MarginTop=20,
MarginLeft=20,
[nameof(Button.Click)]=(BindingDescribe)((s,e)=>
[nameof(Button.Click)]=new CommandDescribe((s,e)=>
{
var w = new Window1();
w.DataContext = model;
@ -1386,7 +1387,8 @@ namespace ConsoleApp1
MarginTop = 76,
Height = 23,
Width = 219,
[nameof(Slider.Value)]=new BindingDescribe(null, nameof(MainModel.ColumnWidth),BindingMode.OneWayToSource,null,a=>new GridLength((float)(double)a))
//[nameof(Slider.Value)]= new Obx<MainModel>(a => a.Type.Name),
[nameof(Slider.Value)]= new BindingDescribe(null, nameof(MainModel.ColumnWidth),BindingMode.OneWayToSource,null,a=>new GridLength((float)(double)a))
},
}
}
@ -2419,10 +2421,10 @@ new TabItemTemplate{
},
new TabItemTemplate
{
Header="布局",
Header="多级绑定",
Content=new Panel
{
Name = "布局",
Name = "多级绑定",
PresenterFor = this,
Width="100%",
Height="100%",
@ -2435,306 +2437,54 @@ new TabItemTemplate{
Orientation= Orientation.Vertical,
Children=
{
new Button
new TextBlock
{
Content="StackPanel的Vertical"
},
new Button
{
Content="按钮"
},
new Button
{
Content="按钮"
},
new Button
{
Content="按钮"
},
}
},
new StackPanel
{
BorderStroke = "5,Solid",
BorderFill = "#B4B4B4",
MarginLeft=80,
MarginTop=50,
Orientation= Orientation.Horizontal,
Children=
{
new Button
{
Content="StackPanel的Horizontal"
},
new Button
{
Content="按钮"
},
new Button
{
Content="Margin调间距",
MarginLeft=5
},
new Button
{
Content="按钮"
},
}
},
new WrapPanel
{
MarginRight=10,
MarginTop=10,
Width="50%",
Orientation= Orientation.Horizontal,
Children=
{
new Button
{
Content="WrapPanel的Horizontal"
},
new Button
{
Content="按钮"
},
new Button
{
Content="Margin调间距",
MarginLeft=5
},
new Button
{
Content="按钮"
},
new Button
{
Content="宽度不够"
},
new Button
{
Content="可以自动换行"
},
}
},
new Grid
{
RenderTransform=new RotateTransform(10),
Name="testGrid",
Background="#999",
Width="80%",
Height="60%",
MarginTop=120,
MarginLeft=20,
ColumnDefinitions=
{
new ColumnDefinition
{
Width="40*"
},
new ColumnDefinition
{
Width = "30*"
},
new ColumnDefinition
{
Width="200",
[nameof(ColumnDefinition.Width)]=nameof(MainModel.ColumnWidth)
},
},
RowDefinitions=
{
new RowDefinition
{
Height="30*"
},
new RowDefinition
{
Height="30*"
},
new RowDefinition
{
Height="30*"
}
},
Children=
{
new WrapPanel
{
Name="test",
Background="#a2f",
Width="100%",
Height="100%",
Children=
{
new Button
{
Content="水平浮动布局231"
},
new Button
{
Content="按钮2"
},
new Button
{
Content="按钮3"
},
new Button
{
Content="按钮4"
},
new Button
{
Content="按钮5"
},
}
},
{
new WrapPanel
{
Orientation= Orientation.Vertical,
Background="#27a",
Width="100%",
Height="100%",
Children=
{
new Button
{
Content="垂直浮动布局"
},
new Button
{
Content="按钮2"
},
new Button
{
Content="按钮3"
},
new Button
{
Content="按钮4"
},
new Button
{
Content="按钮5"
},
}
},
1,
1
},
{
new TextBlock
{
Background="#ac2",
Width="100%",
Height="100%",
Text="Grid布局。。。"
},
2,
1
},
{
new Panel
{
Background="#b1a",
MarginLeft=0,
MarginRight=0,
Children=
{
new Button
{
Content="跨列",
Width="50%"
}
}
},
0,
2,
2
},
{
new TextBlock
{
Background="#186",
Height="100%",
Text="跨行"
},
2,
1,
1,
2
[nameof(TextBlock.Text)]= new Obx<MainModel>(a => a.Test1.test.test.test.test.Name,
BindingMode.OneWay),
Name = "hmbb"
},
new TextBox
{
MarginLeft=10,
Size=SizeField.Fill,
Text="元素变换,可以旋转,倾斜,缩放等操作",
Attacheds=
{
{
Grid.ColumnIndex,
1
}
}
Width = 130,
Height= 60,
Background =Color.Gray,
[nameof(TextBox.Text)]= new Obx<MainModel>(a => a.Test1.test.test.test.test.Name,
BindingMode.OneWayToSource),
},
new Button
{
Content=new SVG("res://ConsoleApp1/test.svg")
Content="创建对象",
[nameof(Button.Click)]=new CommandDescribe((s,e)=>
{
MarginLeft = 0,
MarginTop = 0,
Height = 85,
Width=170,
Stretch= Stretch.Uniform,
},
Width=104,
Height=55,
MarginLeft=60,
MarginTop=120,
Commands=
{
{
nameof(Button.Click),
(s,e)=> Animation((Button)s)
}
}
}
},
},
new DockPanel
{
LastChildFill = false,
Width=200,
Height=200,
MarginRight=0,
MarginTop=50,
Background="#f00",
Children =
{
data a = new data();
a.test.test.test.Name = "666666";
(DataContext as MainModel).Test1.test = a;
})
},
new Button
{
Content="Right",
Height="100%",
Attacheds =
Content="删除对象",
[nameof(Button.Click)]=new CommandDescribe((s,e)=>
{
{
DockPanel.Dock,
Dock.Right
}
}
data a = new data();
a.test.test.Name = "666666";
(DataContext as MainModel).Test1.test.test = null;
})
},
new Button
{
Content="添加对象",
[nameof(Button.Click)]=new CommandDescribe((s,e)=>
{
data a = new data();
a.test.test.Name = "666666";
(DataContext as MainModel).Test1.test.test = a;
})
},
}
},
new Slider
{
Maximum = 300,
Value = 200,
MarginLeft = 252,
MarginTop = 76,
Height = 23,
Width = 219,
[nameof(Slider.Value)]=new BindingDescribe(null, nameof(MainModel.ColumnWidth),BindingMode.OneWayToSource,null,a=>new GridLength((float)(double)a))
},
}
}
},

BIN
Other/yunchaobi.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

View File

@ -3,12 +3,12 @@
#### 介绍
C#跨平台UI框架
提供NETStandard2.0和net4的库通过Netcore可以跨平台支持Windows、Mac、LinuxNet4的可以支持XP。各个平台运行效果一致不依赖系统控件。
支持窗体控件任意透明支持异形窗体支持SVG图标显示。
支持动画数据绑定Mvvm模式CSS等简化依赖属性数据绑定的写法提供数据上下文和命令上下文来绑定直接用CSS和C#代码描述。
提供设计器生成C#代码和开发者工具查看和调试元素
和WPF一样的理念任何控件都可以任意设计模板来实现各种效果
除了使用平台相关API之外基本可以实现一次编写到处运行
提供NETStandard2.0和net4的库通过Netcore可以跨平台支持Windows、Mac、LinuxNet4的可以支持XP。各个平台运行效果一致不依赖系统控件。<br/>
支持窗体控件任意透明支持异形窗体支持SVG图标显示。<br/>
支持动画数据绑定Mvvm模式CSS等简化依赖属性数据绑定的写法提供数据上下文和命令上下文来绑定直接用CSS和C#代码描述。<br/>
提供设计器生成C#代码和开发者工具查看和调试元素<br/>
和WPF一样的理念任何控件都可以任意设计模板来实现各种效果<br/>
除了使用平台相关API之外基本可以实现一次编写到处运行<br/>
全面支持国产化支持国产Linux + 龙芯、飞腾、兆芯、海光等CPU平台
![输入图片说明](Other/2image.png)
@ -17,7 +17,9 @@ C#跨平台UI框架
![输入图片说明](Other/image.png)
Apache License 2.0
![输入图片说明](Other/yunchaobi.gif)
**Apache License 2.0**
#### 软件架构
软件架构说明
@ -29,6 +31,14 @@ CPF为主要框架CPF.Skia为用skia做图形适配CPF.Windows、CPF.Linux
直接克隆/下载就可以编译直接启动ConsoleApp1看运行效果
CPF使用文档 http://cpf.cskin.net/Course/#/
#### 扩展库
https://gitee.com/csharpui/cpf.cef 使用CPF对cef的封装跨平台浏览器控件
https://gitee.com/csharpui/cpf.vlc 使用CPF对vlc的封装跨平台视频播放控件
#### 关于设计器
设计器不开源设计器是需要另外收费的免费模式可以刷新和预览不能拖拽和审查元素可以免费试用VIP一个月
@ -51,3 +61,4 @@ CPF为主要框架CPF.Skia为用skia做图形适配CPF.Windows、CPF.Linux
3. 提交代码
4. 新建 Pull Request
QQ群894952004