From fed94611e9da7e06f2659442df9a2697501af9b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E7=BA=A2=E5=B8=BD?= <761716178@qq.com>
Date: Tue, 2 Jan 2024 13:45:12 +0000
Subject: [PATCH 1/6] update README.md.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 小红帽 <761716178@qq.com>
---
README.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/README.md b/README.md
index a41b3b3..94c079c 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,9 @@ C#跨平台UI框架
除了使用平台相关API之外,基本可以实现一次编写,到处运行
全面支持国产化,支持国产Linux + 龙芯、飞腾、兆芯、海光等CPU平台
+ **gitee** :https://gitee.com/csharpui/CPF
+ **github** :https://github.com/wsxhm/CPF
+


@@ -47,6 +50,17 @@ https://gitee.com/csharpui/cpf.vlc 使用CPF对vlc的封装,跨平台视频


+### Nugets
+
+| Packages | Version |
+|----------|-----------------------------------------|
+| Xhm.CPF | [](https://www.nuget.org/packages/Xhm.CPF/)|
+| Xhm.CPF.Windows | [](https://www.nuget.org/packages/Xhm.CPF.Windows/)|
+| Xhm.CPF.Skia | [](https://www.nuget.org/packages/Xhm.CPF.Skia/)|
+| Xhm.CPF.Linux | [](https://www.nuget.org/packages/Xhm.CPF.Linux/)|
+| Xhm.CPF.Mac | [](https://www.nuget.org/packages/Xhm.CPF.Mac/)|
+
+
#### 参与贡献
打赏/捐赠
From 1305f55fef6b5eb751d4ae31de0910777158d4fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E7=BA=A2=E5=B8=BD?= <761716178@qq.com>
Date: Fri, 5 Jan 2024 00:02:58 +0800
Subject: [PATCH 2/6] =?UTF-8?q?=E8=A7=A3=E5=86=B3JSON=E5=BA=8F=E5=88=97?=
=?UTF-8?q?=E5=8C=96=E7=9A=84BUG=E5=92=8CPInvoke=E5=87=BD=E6=95=B0?=
=?UTF-8?q?=E4=B8=8EUnity=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CPF.Windows/UnmanagedMethods.cs | 6 ++++--
CPF.Windows/WindowImpl.cs | 4 ++--
CPF/Binding.cs | 2 +-
CPF/Json/Reflection.cs | 34 ++++++++++++++++++++++----------
CPF/Platform/Application.cs | 5 +++++
CPF/Threading/DispatcherTimer.cs | 4 ++--
6 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/CPF.Windows/UnmanagedMethods.cs b/CPF.Windows/UnmanagedMethods.cs
index 07982fe..a087b14 100644
--- a/CPF.Windows/UnmanagedMethods.cs
+++ b/CPF.Windows/UnmanagedMethods.cs
@@ -1490,8 +1490,10 @@ BLENDFUNCTION blendFunction // alpha-blending function
//[DllImport("shell32.dll", CharSet = CharSet.Auto)]
//public static extern int DragQueryFile(HandleRef hDrop, int iFile, StringBuilder lpszFile, int cch);
- [DllImport("dwmapi.dll", PreserveSig = false)]
- public static extern bool DwmIsCompositionEnabled();
+ //[DllImport("dwmapi.dll", PreserveSig = false)]
+ //public static extern bool DwmIsCompositionEnabled();
+ [DllImport("dwmapi.dll", BestFitMapping = false)]
+ public static extern int DwmIsCompositionEnabled(out Int32 enabled);
[DllImport("dwmapi.dll")]
public static extern int DwmEnableBlurBehindWindow(IntPtr hWnd, ref DWM_BLURBEHIND pBlurBehind);
diff --git a/CPF.Windows/WindowImpl.cs b/CPF.Windows/WindowImpl.cs
index 1a02deb..eeb31fe 100644
--- a/CPF.Windows/WindowImpl.cs
+++ b/CPF.Windows/WindowImpl.cs
@@ -83,7 +83,7 @@ namespace CPF.Windows
}
posttime = Application.Elapsed;
var v = System.Environment.OSVersion.Version;
- if (v.Major >= 6 && DwmIsCompositionEnabled() && !Application.DesignMode)
+ if (v.Major >= 6 && DwmIsCompositionEnabled(out var e) == 0 && e == 1 && !Application.DesignMode)
{
}
else
@@ -92,7 +92,7 @@ namespace CPF.Windows
}
if ((v.Major == 6 && v.Minor < 2))
{
- touchMsg = RegisterTouchWindow(handle, RegisterTouchFlags.TWF_NONE);
+ touchMsg = RegisterTouchWindow(handle, RegisterTouchFlags.TWF_NONE);
}
_className = "CPFWindow-" + Guid.NewGuid();
// 初始化窗口类结构
diff --git a/CPF/Binding.cs b/CPF/Binding.cs
index d72013f..d217983 100644
--- a/CPF/Binding.cs
+++ b/CPF/Binding.cs
@@ -620,7 +620,7 @@ namespace CPF
throw new Exception($"错误:{ex}");
}
}
- CpfObject SourceProperty = null;
+ //CpfObject SourceProperty = null;
private void Target_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//重新绑定
diff --git a/CPF/Json/Reflection.cs b/CPF/Json/Reflection.cs
index 0ddda5d..e5a9892 100644
--- a/CPF/Json/Reflection.cs
+++ b/CPF/Json/Reflection.cs
@@ -163,6 +163,10 @@ namespace CPF.Json
{
sd = new Dictionary();
var bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
+ if (type.Name == "Nullable`1")
+ {
+ type = type.GetGenericArguments()[0];
+ }
PropertyInfo[] pr = type.GetProperties(bf);
foreach (PropertyInfo p in pr)
{
@@ -317,16 +321,26 @@ namespace CPF.Json
}
else // structs
{
- DynamicMethod dynMethod = new DynamicMethod("_", typeof(object), null);
- ILGenerator ilGen = dynMethod.GetILGenerator();
- var lv = ilGen.DeclareLocal(objtype);
- ilGen.Emit(OpCodes.Ldloca_S, lv);
- ilGen.Emit(OpCodes.Initobj, objtype);
- ilGen.Emit(OpCodes.Ldloc_0);
- ilGen.Emit(OpCodes.Box, objtype);
- ilGen.Emit(OpCodes.Ret);
- c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
- _constrcache.Add(objtype, c);
+ if (objtype.Name == "Nullable`1")
+ {
+ var sType = objtype.GetGenericArguments()[0];
+ var create = objtype.GetConstructor(new Type[] { sType });
+ c = () => create.Invoke(new object[] { null });
+ _constrcache.Add(objtype, c);
+ }
+ else
+ {
+ DynamicMethod dynMethod = new DynamicMethod("_", typeof(object), null);
+ ILGenerator ilGen = dynMethod.GetILGenerator();
+ var lv = ilGen.DeclareLocal(objtype);
+ ilGen.Emit(OpCodes.Ldloca_S, lv);
+ ilGen.Emit(OpCodes.Initobj, objtype);
+ ilGen.Emit(OpCodes.Ldloc_0);
+ ilGen.Emit(OpCodes.Box, objtype);
+ ilGen.Emit(OpCodes.Ret);
+ c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
+ _constrcache.Add(objtype, c);
+ }
}
return c();
}
diff --git a/CPF/Platform/Application.cs b/CPF/Platform/Application.cs
index 1fe0532..1c8deb1 100644
--- a/CPF/Platform/Application.cs
+++ b/CPF/Platform/Application.cs
@@ -228,6 +228,11 @@ namespace CPF.Platform
get;
set;
}
+ ///
+ /// 某些平台下不能使用CPF的消息循环,需要将该属性改成false
+ ///
+ public static bool NeedRunLoop { get; set; } = true;
+
//#if NETSTANDARD
// ///
// /// 启用AOT功能,net5以及以上版本才能使用,其中包括,禁用弱引用事件功能,一般在Aot的release发布的时候设置。如果不用Aot,请勿设置,否则可能有内存泄露风险,而且会导致性能降低。 以及切换windows的com包装
diff --git a/CPF/Threading/DispatcherTimer.cs b/CPF/Threading/DispatcherTimer.cs
index 15b234f..169aaba 100644
--- a/CPF/Threading/DispatcherTimer.cs
+++ b/CPF/Threading/DispatcherTimer.cs
@@ -46,7 +46,7 @@ namespace CPF.Threading
if (timeThread == null)
{
timers = new List();
- tempTimers = new List();
+ tempTimers = new List();
timeThread = new Thread(SetTime) { IsBackground = true, Name = "定时器线程" };
timeThread.Start();
}
@@ -100,7 +100,7 @@ namespace CPF.Threading
}
if (tempTimers.Count > 0)
{
- if (CPF.Platform.Application.Main != null)
+ if ((CPF.Platform.Application.NeedRunLoop && CPF.Platform.Application.Main != null) || !CPF.Platform.Application.NeedRunLoop)
{
Dispatcher.MainThread.Invoke(() =>
{
From 9423dc2d1da91c689ef0d80c9bd93ebbfe8e6622 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E7=BA=A2=E5=B8=BD?= <761716178@qq.com>
Date: Mon, 8 Jan 2024 10:55:10 +0800
Subject: [PATCH 3/6] =?UTF-8?q?Razor=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CPF.Razor/CPF.Razor.csproj | 37 ++
CPF.Razor/Controls/Element.cs | 40 ++
CPF.Razor/Controls/Panel.cs | 50 ++
CPF.Razor/Controls/TestElement.cs | 153 ++++++
CPF.Razor/Core/AttributesBuilder.cs | 44 ++
CPF.Razor/Core/ElementHandlerFactory.cs | 22 +
.../Core/ElementHandlerFactoryContext.cs | 20 +
CPF.Razor/Core/ElementHandlerRegistry.cs | 31 ++
CPF.Razor/Core/ElementManager.cs | 20 +
CPF.Razor/Core/ElementManagerOfElementType.cs | 54 ++
CPF.Razor/Core/IElementHandler.cs | 27 +
CPF.Razor/Core/IHandleChildContentText.cs | 18 +
CPF.Razor/Core/INonChildContainerElement.cs | 16 +
CPF.Razor/Core/NativeComponentAdapter.cs | 465 ++++++++++++++++
CPF.Razor/Core/NativeComponentRenderer.cs | 134 +++++
CPF.Razor/Core/NativeControlComponentBase.cs | 68 +++
...eCollectionAdditionalServicesExtensions.cs | 35 ++
CPF.Razor/Core/TextSpanContainer.cs | 51 ++
CPF.Razor/CpfDispatcher.cs | 54 ++
CPF.Razor/CpfElementManager.cs | 78 +++
CPF.Razor/CpfExtensions.cs | 41 ++
CPF.Razor/CpfHost.cs | 38 ++
CPF.Razor/CpfRenderer.cs | 29 +
CPF.Razor/ElementHandler.cs | 64 +++
CPF.Razor/ICpfElementHandler.cs | 12 +
ConsoleApp1.sln | 19 +
CpfRazorSample/CpfRazorSample.csproj | 35 ++
CpfRazorSample/Program.cs | 34 ++
CpfRazorSample/Stylesheet1.css | 502 ++++++++++++++++++
CpfRazorSample/Test.razor | 7 +
CpfRazorSample/Window1.cs | 52 ++
CpfRazorSample/_Imports.razor | 3 +
32 files changed, 2253 insertions(+)
create mode 100644 CPF.Razor/CPF.Razor.csproj
create mode 100644 CPF.Razor/Controls/Element.cs
create mode 100644 CPF.Razor/Controls/Panel.cs
create mode 100644 CPF.Razor/Controls/TestElement.cs
create mode 100644 CPF.Razor/Core/AttributesBuilder.cs
create mode 100644 CPF.Razor/Core/ElementHandlerFactory.cs
create mode 100644 CPF.Razor/Core/ElementHandlerFactoryContext.cs
create mode 100644 CPF.Razor/Core/ElementHandlerRegistry.cs
create mode 100644 CPF.Razor/Core/ElementManager.cs
create mode 100644 CPF.Razor/Core/ElementManagerOfElementType.cs
create mode 100644 CPF.Razor/Core/IElementHandler.cs
create mode 100644 CPF.Razor/Core/IHandleChildContentText.cs
create mode 100644 CPF.Razor/Core/INonChildContainerElement.cs
create mode 100644 CPF.Razor/Core/NativeComponentAdapter.cs
create mode 100644 CPF.Razor/Core/NativeComponentRenderer.cs
create mode 100644 CPF.Razor/Core/NativeControlComponentBase.cs
create mode 100644 CPF.Razor/Core/ServiceCollectionAdditionalServicesExtensions.cs
create mode 100644 CPF.Razor/Core/TextSpanContainer.cs
create mode 100644 CPF.Razor/CpfDispatcher.cs
create mode 100644 CPF.Razor/CpfElementManager.cs
create mode 100644 CPF.Razor/CpfExtensions.cs
create mode 100644 CPF.Razor/CpfHost.cs
create mode 100644 CPF.Razor/CpfRenderer.cs
create mode 100644 CPF.Razor/ElementHandler.cs
create mode 100644 CPF.Razor/ICpfElementHandler.cs
create mode 100644 CpfRazorSample/CpfRazorSample.csproj
create mode 100644 CpfRazorSample/Program.cs
create mode 100644 CpfRazorSample/Stylesheet1.css
create mode 100644 CpfRazorSample/Test.razor
create mode 100644 CpfRazorSample/Window1.cs
create mode 100644 CpfRazorSample/_Imports.razor
diff --git a/CPF.Razor/CPF.Razor.csproj b/CPF.Razor/CPF.Razor.csproj
new file mode 100644
index 0000000..449f4b6
--- /dev/null
+++ b/CPF.Razor/CPF.Razor.csproj
@@ -0,0 +1,37 @@
+
+
+
+ netstandard2.0
+ 9.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/CPF.Razor/Controls/Element.cs b/CPF.Razor/Controls/Element.cs
new file mode 100644
index 0000000..fd6fa35
--- /dev/null
+++ b/CPF.Razor/Controls/Element.cs
@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Components;
+//using Microsoft.MobileBlazorBindings.Core;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CPF.Razor.Controls
+{
+ public abstract class Element : NativeControlComponentBase where T : UIElement, new()
+ {
+ [Parameter] public string MarginLeft { get; set; }
+ [Parameter] public string MarginTop { get; set; }
+ [Parameter] public string Width { get; set; }
+ [Parameter] public string Height { get; set; }
+
+ //public CPF.UIElement NativeControl => ((ICpfElementHandler)ElementHandler).Element;
+
+ protected override void RenderAttributes(AttributesBuilder builder)
+ {
+ base.RenderAttributes(builder);
+
+ if (MarginLeft != null)
+ {
+ builder.AddAttribute(nameof(MarginLeft), MarginLeft);
+ }
+ if (MarginTop != null)
+ {
+ builder.AddAttribute(nameof(MarginTop), MarginTop);
+ }
+ if (Height != null)
+ {
+ builder.AddAttribute(nameof(Height), Height);
+ }
+ if (Width != null)
+ {
+ builder.AddAttribute(nameof(Width), Width);
+ }
+ }
+ }
+}
diff --git a/CPF.Razor/Controls/Panel.cs b/CPF.Razor/Controls/Panel.cs
new file mode 100644
index 0000000..a49199f
--- /dev/null
+++ b/CPF.Razor/Controls/Panel.cs
@@ -0,0 +1,50 @@
+using Microsoft.AspNetCore.Components;
+//using Microsoft.MobileBlazorBindings.Core;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CPF.Razor.Controls
+{
+ public partial class Panel : Element
+ {
+ //static Panel()
+ //{
+ // ElementHandlerRegistry.RegisterElementHandler();
+ //}
+
+ [Parameter] public string Background { get; set; }
+
+#pragma warning disable CA1721 // Property names should not match get methods
+ [Parameter] public RenderFragment ChildContent { get; set; }
+#pragma warning restore CA1721 // Property names should not match get methods
+ protected override void RenderAttributes(AttributesBuilder builder)
+ {
+ base.RenderAttributes(builder);
+
+ if (Background != null)
+ {
+ builder.AddAttribute(nameof(Background), Background);
+ }
+ }
+ protected override RenderFragment GetChildContent() => ChildContent;
+
+ public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
+ {
+ //switch (attributeName)
+ //{
+ // //case nameof(AutoScroll):
+ // // AutoScroll = AttributeHelper.GetBool(attributeValue);
+ // // break;
+ // default:
+
+ // break;
+ //}
+ var p = Element.GetPropertyMetadata(attributeName);
+ if (p != null)
+ {
+ Element.SetValue(attributeValue.ConvertTo(p.PropertyType), attributeName);
+ }
+ }
+ }
+}
diff --git a/CPF.Razor/Controls/TestElement.cs b/CPF.Razor/Controls/TestElement.cs
new file mode 100644
index 0000000..c24aa36
--- /dev/null
+++ b/CPF.Razor/Controls/TestElement.cs
@@ -0,0 +1,153 @@
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CPF.Razor.Controls
+{
+ public class TestElement : Microsoft.AspNetCore.Components.IComponent, IHandleEvent, IHandleAfterRender, ICustomTypeDescriptor
+ {
+ [Parameter]
+ public string Test { get; set; }
+
+ public void Attach(RenderHandle renderHandle)
+ {
+ throw new NotImplementedException();
+ }
+
+ public AttributeCollection GetAttributes()
+ {
+ throw new NotImplementedException();
+ }
+
+ public string GetClassName()
+ {
+ return "TestElement";
+ }
+
+ public string GetComponentName()
+ {
+ return "GetComponentName";
+ }
+
+ public TypeConverter GetConverter()
+ {
+ throw new NotImplementedException();
+ }
+
+ public EventDescriptor GetDefaultEvent()
+ {
+ throw new NotImplementedException();
+ }
+
+ public PropertyDescriptor GetDefaultProperty()
+ {
+ throw new NotImplementedException();
+ }
+
+ public object GetEditor(Type editorBaseType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public EventDescriptorCollection GetEvents()
+ {
+ throw new NotImplementedException();
+ }
+
+ public EventDescriptorCollection GetEvents(Attribute[] attributes)
+ {
+ throw new NotImplementedException();
+ }
+
+ public PropertyDescriptorCollection GetProperties()
+ {
+ return new PropertyDescriptorCollection(new CpfPropertyDescriptor[] { new CpfPropertyDescriptor("Pro1", false, typeof(string), null) });
+ }
+
+ public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
+ {
+ throw new NotImplementedException();
+ }
+
+ public object GetPropertyOwner(PropertyDescriptor pd)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task HandleEventAsync(EventCallbackWorkItem item, object arg)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task OnAfterRenderAsync()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task SetParametersAsync(ParameterView parameters)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ class CpfPropertyDescriptor : PropertyDescriptor
+ {
+ public CpfPropertyDescriptor(string name, bool readOnly, Type type, Attribute[] attributes) : base(name, attributes)
+ {
+ isreadonly = readOnly;
+ pType = type;
+ }
+ public string FileTypes { get; set; }
+ public bool IsAttached { get; set; }
+ public bool IsDependency { get; set; }
+
+ bool isreadonly;
+ Type pType;
+ public override Type ComponentType => typeof(TestElement);
+
+ public override bool IsReadOnly => isreadonly;
+
+ public override Type PropertyType => pType;
+
+ public override bool CanResetValue(object component)
+ {
+ return false;
+ }
+
+ public override object GetValue(object component)
+ {
+ if (component is UIElement element)
+ {
+ return element.GetPropretyValue(Name);
+ }
+ return null;
+ }
+
+ public override void ResetValue(object component)
+ {
+ //if (component is UIElement element)
+ //{
+ // element.ResetValue(Name);
+ //}
+ }
+
+ public override void SetValue(object component, object value)
+ {
+ if (component is UIElement element)
+ {
+ element.SetPropretyValue(Name, value);
+ }
+ }
+
+ public override bool ShouldSerializeValue(object component)
+ {
+ return false;
+ }
+ public override string ToString()
+ {
+ return this.Name;
+ }
+ }
+}
diff --git a/CPF.Razor/Core/AttributesBuilder.cs b/CPF.Razor/Core/AttributesBuilder.cs
new file mode 100644
index 0000000..159649f
--- /dev/null
+++ b/CPF.Razor/Core/AttributesBuilder.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.AspNetCore.Components.Rendering;
+
+namespace CPF.Razor
+{
+ // This wraps a RenderTreeBuilder in such a way that consumers
+ // can only call the desired AddAttribute method, can't supply
+ // sequence numbers, and can't leak the instance outside their
+ // position in the call stack.
+
+#pragma warning disable CA1815 // Override equals and operator equals on value types; these instances are never compared
+ public readonly ref struct AttributesBuilder
+#pragma warning restore CA1815 // Override equals and operator equals on value types
+ {
+ private readonly RenderTreeBuilder _underlyingBuilder;
+
+ public AttributesBuilder(RenderTreeBuilder underlyingBuilder)
+ {
+ _underlyingBuilder = underlyingBuilder;
+ }
+
+ public void AddAttribute(string name, object value)
+ {
+ // Using a fixed sequence number is allowed for attribute frames,
+ // and causes the diff algorithm to use a dictionary to match old
+ // and new values.
+ _underlyingBuilder.AddAttribute(0, name, value);
+ }
+
+ public void AddAttribute(string name, bool value)
+ {
+ // Using a fixed sequence number is allowed for attribute frames,
+ // and causes the diff algorithm to use a dictionary to match old
+ // and new values.
+
+ // bool values are converted to ints (which later become strings) to ensure that
+ // all values are always rendered, not only 'true' values. This ensures that the
+ // element handlers will see all property changes and can handle them as needed.
+ _underlyingBuilder.AddAttribute(0, name, value ? 1 : 0);
+ }
+ }
+}
diff --git a/CPF.Razor/Core/ElementHandlerFactory.cs b/CPF.Razor/Core/ElementHandlerFactory.cs
new file mode 100644
index 0000000..3876ca8
--- /dev/null
+++ b/CPF.Razor/Core/ElementHandlerFactory.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+
+namespace CPF.BlazorBindings
+{
+ internal class ElementHandlerFactory
+ {
+ private readonly Func _callback;
+
+ public ElementHandlerFactory(Func callback)
+ {
+ _callback = callback ?? throw new ArgumentNullException(nameof(callback));
+ }
+
+ public IElementHandler CreateElementHandler(ElementHandlerFactoryContext context)
+ {
+ return _callback(context.Renderer, context.ParentHandler);
+ }
+ }
+}
diff --git a/CPF.Razor/Core/ElementHandlerFactoryContext.cs b/CPF.Razor/Core/ElementHandlerFactoryContext.cs
new file mode 100644
index 0000000..98a6607
--- /dev/null
+++ b/CPF.Razor/Core/ElementHandlerFactoryContext.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+
+namespace CPF.BlazorBindings
+{
+ internal class ElementHandlerFactoryContext
+ {
+ public ElementHandlerFactoryContext(NativeComponentRenderer renderer, IElementHandler parentHandler)
+ {
+ Renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
+ ParentHandler = parentHandler;
+ }
+
+ public IElementHandler ParentHandler { get; }
+
+ public NativeComponentRenderer Renderer { get; }
+ }
+}
diff --git a/CPF.Razor/Core/ElementHandlerRegistry.cs b/CPF.Razor/Core/ElementHandlerRegistry.cs
new file mode 100644
index 0000000..e26dfec
--- /dev/null
+++ b/CPF.Razor/Core/ElementHandlerRegistry.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+using System.Collections.Generic;
+
+namespace CPF.BlazorBindings
+{
+ public static class ElementHandlerRegistry
+ {
+ //internal static Dictionary ElementHandlers { get; }
+ // = new Dictionary();
+
+ //public static void RegisterElementHandler(
+ // Func factory) where TComponent : NativeControlComponentBase
+ //{
+ // ElementHandlers.Add(typeof(TComponent).FullName, new ElementHandlerFactory(factory));
+ //}
+
+ //public static void RegisterElementHandler(
+ // Func factory) where TComponent : NativeControlComponentBase
+ //{
+ // ElementHandlers.Add(typeof(TComponent).FullName, new ElementHandlerFactory((renderer, _) => factory(renderer)));
+ //}
+
+ //public static void RegisterElementHandler() where TComponent : NativeControlComponentBase where TControlHandler : class, IElementHandler, new()
+ //{
+ // RegisterElementHandler((_, __) => new TControlHandler());
+ //}
+ }
+}
diff --git a/CPF.Razor/Core/ElementManager.cs b/CPF.Razor/Core/ElementManager.cs
new file mode 100644
index 0000000..3f7050e
--- /dev/null
+++ b/CPF.Razor/Core/ElementManager.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+namespace CPF.Razor
+{
+ ///
+ /// Utilities needed by the system to manage native controls. Implementations
+ /// of native rendering systems have their own quirks in terms of dealing with
+ /// parent/child relationships, so each must implement this given the constraints
+ /// and requirements of their systems.
+ ///
+ public abstract class ElementManager
+ {
+ public abstract void AddChildElement(IElementHandler parentHandler, IElementHandler childHandler, int physicalSiblingIndex);
+ public abstract int GetPhysicalSiblingIndex(IElementHandler handler);
+ public abstract bool IsParented(IElementHandler handler);
+ public abstract bool IsParentOfChild(IElementHandler parentHandler, IElementHandler childHandler);
+ public abstract void RemoveElement(IElementHandler handler);
+ }
+}
diff --git a/CPF.Razor/Core/ElementManagerOfElementType.cs b/CPF.Razor/Core/ElementManagerOfElementType.cs
new file mode 100644
index 0000000..5aa2fc5
--- /dev/null
+++ b/CPF.Razor/Core/ElementManagerOfElementType.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+
+namespace CPF.Razor
+{
+ ///
+ /// Utility intermediate class to make it easier to strongly-type a derived .
+ ///
+ ///
+ public abstract class ElementManager : ElementManager
+ {
+ private static TElementType ConvertToType(IElementHandler elementHandler, string parameterName)
+ {
+ if (!(elementHandler is TElementType))
+ {
+ throw new ArgumentException($"Expected parameter value of type '{elementHandler.GetType().FullName}' to be convertible to type '{typeof(TElementType).FullName}'.", parameterName);
+ }
+ return (TElementType)elementHandler;
+ }
+
+ public sealed override void AddChildElement(IElementHandler parentHandler, IElementHandler childHandler, int physicalSiblingIndex)
+ {
+ AddChildElement(ConvertToType(parentHandler, nameof(parentHandler)), ConvertToType(childHandler, nameof(childHandler)), physicalSiblingIndex);
+ }
+
+ public sealed override int GetPhysicalSiblingIndex(IElementHandler handler)
+ {
+ return GetPhysicalSiblingIndex(ConvertToType(handler, nameof(handler)));
+ }
+
+ public sealed override bool IsParented(IElementHandler handler)
+ {
+ return IsParented(ConvertToType(handler, nameof(handler)));
+ }
+
+ public sealed override bool IsParentOfChild(IElementHandler parentHandler, IElementHandler childHandler)
+ {
+ return IsParentOfChild(ConvertToType(parentHandler, nameof(parentHandler)), ConvertToType(childHandler, nameof(childHandler)));
+ }
+
+ public sealed override void RemoveElement(IElementHandler handler)
+ {
+ RemoveElement(ConvertToType(handler, nameof(handler)));
+ }
+
+ protected abstract void AddChildElement(TElementType elementType1, TElementType elementType2, int physicalSiblingIndex);
+ protected abstract int GetPhysicalSiblingIndex(TElementType elementType);
+ protected abstract bool IsParented(TElementType elementType);
+ protected abstract bool IsParentOfChild(TElementType elementType1, TElementType elementType2);
+ protected abstract void RemoveElement(TElementType elementType);
+ }
+}
diff --git a/CPF.Razor/Core/IElementHandler.cs b/CPF.Razor/Core/IElementHandler.cs
new file mode 100644
index 0000000..c340f03
--- /dev/null
+++ b/CPF.Razor/Core/IElementHandler.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+namespace CPF.Razor
+{
+ ///
+ /// Represents a container for native element.
+ ///
+ public interface IElementHandler
+ {
+ ///
+ /// Sets an attribute named on the represented by
+ /// this handler to value .
+ ///
+ ///
+ ///
+ ///
+ ///
+ void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName);
+
+ ///
+ /// The native element represented by this handler. This is often a native UI component, but can be any type
+ /// of component used by the native system.
+ ///
+ object TargetElement { get; }
+ }
+}
diff --git a/CPF.Razor/Core/IHandleChildContentText.cs b/CPF.Razor/Core/IHandleChildContentText.cs
new file mode 100644
index 0000000..a58b2bb
--- /dev/null
+++ b/CPF.Razor/Core/IHandleChildContentText.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+namespace CPF.Razor
+{
+ ///
+ /// Defines a mechanism for an to accept inline text.
+ ///
+ public interface IHandleChildContentText
+ {
+ ///
+ /// This method is called to process inline text found in a component.
+ ///
+ /// the index of the string within a group of text strings.
+ /// The text to handle. This text may contain whitespace at the start and end of the string.
+ void HandleText(int index, string text);
+ }
+}
diff --git a/CPF.Razor/Core/INonChildContainerElement.cs b/CPF.Razor/Core/INonChildContainerElement.cs
new file mode 100644
index 0000000..e3459b5
--- /dev/null
+++ b/CPF.Razor/Core/INonChildContainerElement.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+namespace CPF.Razor
+{
+ ///
+ /// Marker interface to indicate that this element is a container of elements that are not
+ /// true children of their parent. For example, a host for elements that go in a modal dialog
+ /// are not true children of their parent.
+ ///
+#pragma warning disable CA1040 // Avoid empty interfaces
+ public interface INonChildContainerElement
+#pragma warning restore CA1040 // Avoid empty interfaces
+ {
+ }
+}
diff --git a/CPF.Razor/Core/NativeComponentAdapter.cs b/CPF.Razor/Core/NativeComponentAdapter.cs
new file mode 100644
index 0000000..1658948
--- /dev/null
+++ b/CPF.Razor/Core/NativeComponentAdapter.cs
@@ -0,0 +1,465 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.RenderTree;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+
+namespace CPF.Razor
+{
+ ///
+ /// Represents a "shadow" item that Blazor uses to map changes into the live native UI tree.
+ ///
+ [DebuggerDisplay("{DebugName}")]
+ internal sealed class NativeComponentAdapter : IDisposable
+ {
+ private static volatile int DebugInstanceCounter;
+
+ public NativeComponentAdapter(NativeComponentRenderer renderer, IElementHandler closestPhysicalParent, IElementHandler knownTargetElement = null)
+ {
+ Renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
+ _closestPhysicalParent = closestPhysicalParent;
+ _targetElement = knownTargetElement;
+
+ // Assign unique counter value. This *should* all be done on one thread, but just in case, make it thread-safe.
+ _debugInstanceCounterValue = Interlocked.Increment(ref DebugInstanceCounter);
+ }
+
+ private readonly int _debugInstanceCounterValue;
+
+ private string DebugName => $"[#{_debugInstanceCounterValue}] {Name}";
+
+ public NativeComponentAdapter Parent { get; private set; }
+ public List Children { get; } = new List();
+
+ private readonly IElementHandler _closestPhysicalParent;
+ private IElementHandler _targetElement;
+ private IComponent _targetComponent;
+
+ public NativeComponentRenderer Renderer { get; }
+
+ ///
+ /// Used for debugging purposes.
+ ///
+ public string Name { get; internal set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(NativeComponentAdapter)}: Name={Name ?? ">"}, Target={_targetElement?.GetType().Name ?? ""}, #Children={Children.Count}";
+ }
+
+ internal void ApplyEdits(int componentId, ArrayBuilderSegment edits, ArrayRange referenceFrames, RenderBatch batch)
+ {
+ Renderer.Dispatcher.AssertAccess();
+
+ if (edits.Count == 0)
+ {
+ // TODO: Without this check there's a NullRef in ArrayBuilderSegment? Possibly a Blazor bug?
+ return;
+ }
+
+ foreach (var edit in edits)
+ {
+ switch (edit.Type)
+ {
+ case RenderTreeEditType.PrependFrame:
+ ApplyPrependFrame(batch, componentId, edit.SiblingIndex, referenceFrames.Array, edit.ReferenceFrameIndex);
+ break;
+ case RenderTreeEditType.RemoveFrame:
+ ApplyRemoveFrame(edit.SiblingIndex);
+ break;
+ case RenderTreeEditType.SetAttribute:
+ ApplySetAttribute(ref referenceFrames.Array[edit.ReferenceFrameIndex]);
+ break;
+ case RenderTreeEditType.RemoveAttribute:
+ // TODO: See whether siblingIndex is needed here
+ ApplyRemoveAttribute(edit.RemovedAttributeName);
+ break;
+ case RenderTreeEditType.UpdateText:
+ {
+ var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex];
+ if (_targetElement is IHandleChildContentText handleChildContentText)
+ {
+ handleChildContentText.HandleText(edit.SiblingIndex, frame.TextContent);
+ }
+ else
+ {
+ throw new Exception("Cannot set text content on child that doesn't handle inner text content.");
+ }
+ break;
+ }
+ case RenderTreeEditType.StepIn:
+ {
+ // TODO: Need to implement this. For now it seems safe to ignore.
+ break;
+ }
+ case RenderTreeEditType.StepOut:
+ {
+ // TODO: Need to implement this. For now it seems safe to ignore.
+ break;
+ }
+ case RenderTreeEditType.UpdateMarkup:
+ {
+ var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex];
+ if (_targetElement is IHandleChildContentText handleChildContentText)
+ {
+ handleChildContentText.HandleText(edit.SiblingIndex, frame.MarkupContent);
+ }
+ else
+ {
+ throw new Exception("Cannot set markup content on child that doesn't handle inner text content.");
+ }
+ break;
+ }
+ case RenderTreeEditType.PermutationListEntry:
+ throw new NotImplementedException($"Not supported edit type: {edit.Type}");
+ case RenderTreeEditType.PermutationListEnd:
+ throw new NotImplementedException($"Not supported edit type: {edit.Type}");
+ default:
+ throw new NotImplementedException($"Invalid edit type: {edit.Type}");
+ }
+ }
+ }
+
+ private void ApplyRemoveFrame(int siblingIndex)
+ {
+ var childToRemove = Children[siblingIndex];
+ Children.RemoveAt(siblingIndex);
+ childToRemove.RemoveSelfAndDescendants();
+ }
+
+ private void RemoveSelfAndDescendants()
+ {
+ if (_targetElement != null)
+ {
+ // This adapter represents a physical element, so by removing it, we implicitly
+ // remove all descendants.
+ Renderer.ElementManager.RemoveElement(_targetElement);
+ }
+ else
+ {
+ // This adapter is just a container for other adapters
+ foreach (var child in Children)
+ {
+ child.RemoveSelfAndDescendants();
+ }
+ }
+ }
+
+ private void ApplySetAttribute(ref RenderTreeFrame attributeFrame)
+ {
+ if (_targetElement == null)
+ {
+ throw new InvalidOperationException($"Trying to apply attribute {attributeFrame.AttributeName} to an adapter that isn't for an element");
+ }
+
+ _targetElement.ApplyAttribute(
+ attributeFrame.AttributeEventHandlerId,
+ attributeFrame.AttributeName,
+ attributeFrame.AttributeValue,
+ attributeFrame.AttributeEventUpdatesAttributeName);
+ }
+
+ private void ApplyRemoveAttribute(string removedAttributeName)
+ {
+ if (_targetElement == null)
+ {
+ throw new InvalidOperationException($"Trying to remove attribute {removedAttributeName} to an adapter that isn't for an element");
+ }
+
+ _targetElement.ApplyAttribute(
+ attributeEventHandlerId: 0,
+ attributeName: removedAttributeName,
+ attributeValue: null,
+ attributeEventUpdatesAttributeName: null);
+ }
+
+ private int ApplyPrependFrame(RenderBatch batch, int componentId, int siblingIndex, RenderTreeFrame[] frames, int frameIndex)
+ {
+ ref var frame = ref frames[frameIndex];
+ switch (frame.FrameType)
+ {
+ case RenderTreeFrameType.Element:
+ {
+ InsertElement(siblingIndex, frames, frameIndex, componentId, batch);
+ return 1;
+ }
+ case RenderTreeFrameType.Component:
+ {
+ // Components are represented by NativeComponentAdapter
+ var childAdapter = Renderer.CreateAdapterForChildComponent(_targetElement ?? _closestPhysicalParent, frame.ComponentId);
+ childAdapter.Name = $"For: '{frame.Component.GetType().FullName}'";
+ childAdapter._targetComponent = frame.Component;
+ AddChildAdapter(siblingIndex, childAdapter);
+ return 1;
+ }
+ case RenderTreeFrameType.Region:
+ {
+ return InsertFrameRange(batch, componentId, siblingIndex, frames, frameIndex + 1, frameIndex + frame.RegionSubtreeLength);
+ }
+ case RenderTreeFrameType.Markup:
+ {
+ if (_targetElement is IHandleChildContentText handleChildContentText)
+ {
+ handleChildContentText.HandleText(siblingIndex, frame.MarkupContent);
+ }
+ else if (!string.IsNullOrWhiteSpace(frame.MarkupContent))
+ {
+ throw new NotImplementedException("Nonempty markup: " + frame.MarkupContent);
+ }
+#pragma warning disable CA2000 // Dispose objects before losing scope; adapters are disposed when they are removed from the adapter tree
+ var childAdapter = CreateAdapter(_targetElement ?? _closestPhysicalParent);
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ childAdapter.Name = $"Markup, sib#={siblingIndex}";
+ AddChildAdapter(siblingIndex, childAdapter);
+ return 1;
+ }
+ case RenderTreeFrameType.Text:
+ {
+ if (_targetElement is IHandleChildContentText handleChildContentText)
+ {
+ handleChildContentText.HandleText(siblingIndex, frame.TextContent);
+ }
+ else if (!string.IsNullOrWhiteSpace(frame.TextContent))
+ {
+ throw new NotImplementedException("Nonempty text: " + frame.TextContent);
+ }
+#pragma warning disable CA2000 // Dispose objects before losing scope; adapters are disposed when they are removed from the adapter tree
+ var childAdapter = CreateAdapter(_targetElement ?? _closestPhysicalParent);
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ childAdapter.Name = $"Text, sib#={siblingIndex}";
+ AddChildAdapter(siblingIndex, childAdapter);
+ return 1;
+ }
+ default:
+ throw new NotImplementedException($"Not supported frame type: {frame.FrameType}");
+ }
+ }
+
+ private NativeComponentAdapter CreateAdapter(IElementHandler physicalParent)
+ {
+ return new NativeComponentAdapter(Renderer, physicalParent);
+ }
+
+ private void InsertElement(int siblingIndex, RenderTreeFrame[] frames, int frameIndex, int componentId, RenderBatch batch)
+ {
+ // Elements represent native elements
+ ref var frame = ref frames[frameIndex];
+ //var elementName = frame.ElementName;
+ //var elementHandlerFactory = ElementHandlerRegistry.ElementHandlers[elementName];
+
+ var elementHandler = _targetComponent as IElementHandler;
+ //var elementHandler = elementHandlerFactory.CreateElementHandler(new ElementHandlerFactoryContext(Renderer, _closestPhysicalParent));
+
+ //if (_targetComponent is NativeControlComponentBase componentInstance)
+ //{
+ // componentInstance.SetElementReference(elementHandler);
+ //}
+
+ if (siblingIndex != 0)
+ {
+ // With the current design, we should be able to ignore sibling indices for elements,
+ // so bail out if that's not the case
+ throw new NotSupportedException($"Currently we assume all adapter elements render exactly zero or one elements. Found an element with sibling index {siblingIndex}");
+ }
+
+ // TODO: Consider in the future calling a new API to check if the elementHandler represents a native UI component:
+ // if (Renderer.ElementManager.IsNativeElement(elementHandler)) { add to UI tree }
+ // else { do something with non-native element, e.g. notify parent to handle it }
+
+ // For the location in the physical UI tree, find the last preceding-sibling adapter that has
+ // a physical descendant (if any). If there is one, we physically insert after that one. If not,
+ // we'll insert as the first child of the closest physical parent.
+ if (!Renderer.ElementManager.IsParented(elementHandler))
+ {
+ var elementIndex = GetIndexForElement();
+ Renderer.ElementManager.AddChildElement(_closestPhysicalParent, elementHandler, elementIndex);
+ }
+ _targetElement = elementHandler;
+
+ var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength;
+ for (var descendantIndex = frameIndex + 1; descendantIndex < endIndexExcl; descendantIndex++)
+ {
+ var candidateFrame = frames[descendantIndex];
+ if (candidateFrame.FrameType == RenderTreeFrameType.Attribute)
+ {
+ ApplySetAttribute(ref candidateFrame);
+ }
+ else
+ {
+ // As soon as we see a non-attribute child, all the subsequent child frames are
+ // not attributes, so bail out and insert the remnants recursively
+ InsertFrameRange(batch, componentId, childIndex: 0, frames, descendantIndex, endIndexExcl);
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Finds the sibling index to insert this adapter's element into. It walks up Parent adapters to find
+ /// an earlier sibling that has a native element, and uses that native element's physical index to determine
+ /// the location of the new element.
+ ///
+ /// * Adapter0
+ /// * Adapter1
+ /// * Adapter2
+ /// * Adapter3 (native)
+ /// * Adapter3.0 (searchOrder=2)
+ /// * Adapter3.0.0 (searchOrder=3)
+ /// * Adapter3.0.1 (native) (searchOrder=4) <-- This is the nearest earlier sibling that has a physical element)
+ /// * Adapter3.0.2
+ /// * Adapter3.1 (searchOrder=1)
+ /// * Adapter3.1.0 (searchOrder=0)
+ /// * Adapter3.1.1 (native) <-- Current adapter
+ /// * Adapter3.1.2
+ /// * Adapter3.2
+ /// * Adapter4
+ ///
+ ///
+ /// The index at which the native element should be inserted into within the parent. It returns -1 as a failure mode.
+ private int GetIndexForElement()
+ {
+ var childAdapter = this;
+ var parentAdapter = Parent;
+ while (parentAdapter != null)
+ {
+ // Walk previous siblings of this level and deep-scan them for native elements
+ var matchedEarlierSibling = GetEarlierSiblingMatch(parentAdapter, childAdapter);
+ if (matchedEarlierSibling != null)
+ {
+ if (!Renderer.ElementManager.IsParentOfChild(_closestPhysicalParent, matchedEarlierSibling._targetElement))
+ {
+ Debug.Fail($"Expected that the item found ({matchedEarlierSibling.DebugName}) with target element ({matchedEarlierSibling._targetElement.GetType().FullName}) should necessarily be an immediate child of the closest native parent ({_closestPhysicalParent.GetType().FullName}), but it wasn't...");
+ }
+
+ // If a native element was found somewhere within this sibling, the index for the new element
+ // will be 1 greater than its native index.
+ return Renderer.ElementManager.GetPhysicalSiblingIndex(matchedEarlierSibling._targetElement) + 1;
+ }
+
+ // If this level has a native element and all its relevant children have been scanned, then there's
+ // no previous sibling, so the new element to be added will be its earliest child (index=0). (There
+ // might be *later* siblings, but they are not relevant to this search.)
+ if (parentAdapter._targetElement != null)
+ {
+ Debug.Assert(parentAdapter._targetElement == _closestPhysicalParent, $"Expected that nearest parent ({parentAdapter.DebugName}) with native element ({parentAdapter._targetElement.GetType().FullName}) would have the closest physical parent ({_closestPhysicalParent.GetType().FullName}).");
+ return 0;
+ }
+
+ // If we haven't found a previous sibling with a native element or reached a native container, keep
+ // walking up the parent tree...
+ childAdapter = parentAdapter;
+ parentAdapter = parentAdapter.Parent;
+ }
+ Debug.Fail($"Expected to find a parent with a native element but found none.");
+ return -1;
+ }
+
+ private static NativeComponentAdapter GetEarlierSiblingMatch(NativeComponentAdapter parentAdapter, NativeComponentAdapter childAdapter)
+ {
+ var indexOfParentsChildAdapter = parentAdapter.Children.IndexOf(childAdapter);
+
+ for (var i = indexOfParentsChildAdapter - 1; i >= 0; i--)
+ {
+ var sibling = parentAdapter.Children[i];
+ if (sibling._targetElement is INonChildContainerElement)
+ {
+ continue;
+ }
+
+ // Deep scan this sibling adapter to find its latest and highest native element
+ var siblingWithNativeElement = sibling.GetLastDescendantWithPhysicalElement();
+ if (siblingWithNativeElement != null)
+ {
+ return siblingWithNativeElement;
+ }
+ }
+
+ // No preceding sibling has any native elements
+ return null;
+ }
+
+ private NativeComponentAdapter GetLastDescendantWithPhysicalElement()
+ {
+ if (_targetElement is INonChildContainerElement)
+ {
+ return null;
+ }
+ if (_targetElement != null)
+ {
+ // If this adapter has a target element, then this is the droid we're looking for. It can't be
+ // any children of this target element because they can't be children of this element's parent.
+ return this;
+ }
+
+ for (var i = Children.Count - 1; i >= 0; i--)
+ {
+ var child = Children[i];
+ var physicalDescendant = child.GetLastDescendantWithPhysicalElement();
+ if (physicalDescendant != null)
+ {
+ return physicalDescendant;
+ }
+ }
+
+ return null;
+ }
+
+ private int InsertFrameRange(RenderBatch batch, int componentId, int childIndex, RenderTreeFrame[] frames, int startIndex, int endIndexExcl)
+ {
+ var origChildIndex = childIndex;
+ for (var index = startIndex; index < endIndexExcl; index++)
+ {
+ ref var frame = ref batch.ReferenceFrames.Array[index];
+ var numChildrenInserted = ApplyPrependFrame(batch, componentId, childIndex, frames, index);
+ childIndex += numChildrenInserted;
+
+ // Skip over any descendants, since they are already dealt with recursively
+ index += CountDescendantFrames(frame);
+ }
+
+ return (childIndex - origChildIndex); // Total number of children inserted
+ }
+
+ private static int CountDescendantFrames(RenderTreeFrame frame)
+ {
+ return frame.FrameType switch
+ {
+ // The following frame types have a subtree length. Other frames may use that memory slot
+ // to mean something else, so we must not read it. We should consider having nominal subtypes
+ // of RenderTreeFramePointer that prevent access to non-applicable fields.
+ RenderTreeFrameType.Component => frame.ComponentSubtreeLength - 1,
+ RenderTreeFrameType.Element => frame.ElementSubtreeLength - 1,
+ RenderTreeFrameType.Region => frame.RegionSubtreeLength - 1,
+ _ => 0,
+ };
+ ;
+ }
+
+ private void AddChildAdapter(int siblingIndex, NativeComponentAdapter childAdapter)
+ {
+ childAdapter.Parent = this;
+
+ if (siblingIndex <= Children.Count)
+ {
+ Children.Insert(siblingIndex, childAdapter);
+ }
+ else
+ {
+ Debug.WriteLine($"WARNING: {nameof(AddChildAdapter)} called with {nameof(siblingIndex)}={siblingIndex}, but Children.Count={Children.Count}");
+ Children.Add(childAdapter);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_targetElement is IDisposable disposableTargetElement)
+ {
+ disposableTargetElement.Dispose();
+ }
+ }
+ }
+}
diff --git a/CPF.Razor/Core/NativeComponentRenderer.cs b/CPF.Razor/Core/NativeComponentRenderer.cs
new file mode 100644
index 0000000..1cb7beb
--- /dev/null
+++ b/CPF.Razor/Core/NativeComponentRenderer.cs
@@ -0,0 +1,134 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.RenderTree;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CPF.Razor
+{
+ public abstract class NativeComponentRenderer : Renderer
+ {
+ private readonly Dictionary _componentIdToAdapter = new Dictionary();
+ private ElementManager _elementManager;
+ private readonly Dictionary> _eventRegistrations = new Dictionary>();
+
+
+ public NativeComponentRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
+ : base(serviceProvider, loggerFactory)
+ {
+ }
+
+ protected abstract ElementManager CreateNativeControlManager();
+
+ internal ElementManager ElementManager
+ {
+ get
+ {
+ return _elementManager ?? (_elementManager = CreateNativeControlManager());
+ }
+ }
+
+ public override Dispatcher Dispatcher { get; }
+ = Dispatcher.CreateDefault();
+
+ ///
+ /// Creates a component of type and adds it as a child of .
+ ///
+ ///
+ ///
+ ///
+ public async Task AddComponent(IElementHandler parent) where TComponent : IComponent
+ {
+ await AddComponent(typeof(TComponent), parent).ConfigureAwait(false);
+ }
+
+ ///
+ /// Creates a component of type and adds it as a child of .
+ ///
+ ///
+ ///
+ ///
+ public async Task AddComponent(Type componentType, IElementHandler parent)
+ {
+ await Dispatcher.InvokeAsync(async () =>
+ {
+ var component = InstantiateComponent(componentType);
+ var componentId = AssignRootComponentId(component);
+
+ var rootAdapter = new NativeComponentAdapter(this, closestPhysicalParent: parent, knownTargetElement: parent)
+ {
+ Name = $"RootAdapter attached to {parent.GetType().FullName}",
+ };
+
+ _componentIdToAdapter[componentId] = rootAdapter;
+
+ await RenderRootComponentAsync(componentId).ConfigureAwait(false);
+ }).ConfigureAwait(false);
+ }
+
+ protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
+ {
+ foreach (var updatedComponent in renderBatch.UpdatedComponents.Array.Take(renderBatch.UpdatedComponents.Count))
+ {
+ var adapter = _componentIdToAdapter[updatedComponent.ComponentId];
+ adapter.ApplyEdits(updatedComponent.ComponentId, updatedComponent.Edits, renderBatch.ReferenceFrames, renderBatch);
+ }
+
+ var numDisposedComponents = renderBatch.DisposedComponentIDs.Count;
+ for (var i = 0; i < numDisposedComponents; i++)
+ {
+ var disposedComponentId = renderBatch.DisposedComponentIDs.Array[i];
+ if (_componentIdToAdapter.TryGetValue(disposedComponentId, out var adapter))
+ {
+ _componentIdToAdapter.Remove(disposedComponentId);
+ (adapter as IDisposable)?.Dispose();
+ }
+ }
+
+ var numDisposeEventHandlers = renderBatch.DisposedEventHandlerIDs.Count;
+ if (numDisposeEventHandlers != 0)
+ {
+ for (var i = 0; i < numDisposeEventHandlers; i++)
+ {
+ DisposeEvent(renderBatch.DisposedEventHandlerIDs.Array[i]);
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public void RegisterEvent(ulong eventHandlerId, Action unregisterCallback)
+ {
+ if (eventHandlerId == 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(eventHandlerId), "Event handler ID must not be 0.");
+ }
+ if (unregisterCallback == null)
+ {
+ throw new ArgumentNullException(nameof(unregisterCallback));
+ }
+ _eventRegistrations.Add(eventHandlerId, unregisterCallback);
+ }
+
+ private void DisposeEvent(ulong eventHandlerId)
+ {
+ if (!_eventRegistrations.TryGetValue(eventHandlerId, out var unregisterCallback))
+ {
+ throw new InvalidOperationException($"Attempting to dispose unknown event handler id '{eventHandlerId}'.");
+ }
+ unregisterCallback(eventHandlerId);
+ }
+
+ internal NativeComponentAdapter CreateAdapterForChildComponent(IElementHandler physicalParent, int componentId)
+ {
+ var result = new NativeComponentAdapter(this, physicalParent);
+ _componentIdToAdapter[componentId] = result;
+ return result;
+ }
+ }
+}
diff --git a/CPF.Razor/Core/NativeControlComponentBase.cs b/CPF.Razor/Core/NativeControlComponentBase.cs
new file mode 100644
index 0000000..79f0ac9
--- /dev/null
+++ b/CPF.Razor/Core/NativeControlComponentBase.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Rendering;
+using System;
+
+namespace CPF.Razor
+{
+ public abstract class NativeControlComponentBase : ComponentBase, ICpfElementHandler where T : UIElement, new()
+ {
+ public IElementHandler ElementHandler { get; private set; }
+
+ UIElement ICpfElementHandler.Element => Element;
+
+ T element;
+ public T Element
+ {
+ get
+ {
+ if (element == null)
+ {
+ element = CreateElement();
+ }
+ return element;
+ }
+ }
+
+ public object TargetElement => Element;
+
+ //public void SetElementReference(IElementHandler elementHandler)
+ //{
+ // ElementHandler = elementHandler ?? throw new ArgumentNullException(nameof(elementHandler));
+ //}
+
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ if (builder is null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.OpenElement(0, GetType().FullName);
+ RenderAttributes(new AttributesBuilder(builder));
+
+ var childContent = GetChildContent();
+ if (childContent != null)
+ {
+ builder.AddContent(2, childContent);
+ }
+
+ builder.CloseElement();
+ }
+
+ protected virtual void RenderAttributes(AttributesBuilder builder)
+ {
+ }
+
+ protected virtual RenderFragment GetChildContent() => null;
+
+ public abstract void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName);
+
+ protected virtual T CreateElement()
+ {
+ return new T();
+ }
+ }
+}
diff --git a/CPF.Razor/Core/ServiceCollectionAdditionalServicesExtensions.cs b/CPF.Razor/Core/ServiceCollectionAdditionalServicesExtensions.cs
new file mode 100644
index 0000000..63c0075
--- /dev/null
+++ b/CPF.Razor/Core/ServiceCollectionAdditionalServicesExtensions.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class ServiceCollectionAdditionalServicesExtensions
+ {
+ ///
+ /// Copies service descriptors from one service collection to another.
+ ///
+ /// The destination service collection to which the additional services will be added.
+ /// The list of additional services to add.
+ ///
+ public static IServiceCollection AddAdditionalServices(this IServiceCollection services, IServiceCollection additionalServices)
+ {
+ if (services is null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ if (additionalServices is null)
+ {
+ throw new ArgumentNullException(nameof(additionalServices));
+ }
+
+ foreach (var additionalService in additionalServices)
+ {
+ services.Add(additionalService);
+ }
+ return services;
+ }
+ }
+}
diff --git a/CPF.Razor/Core/TextSpanContainer.cs b/CPF.Razor/Core/TextSpanContainer.cs
new file mode 100644
index 0000000..bbb31ec
--- /dev/null
+++ b/CPF.Razor/Core/TextSpanContainer.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System;
+using System.Collections.Generic;
+
+namespace CPF.Razor
+{
+ ///
+ /// Helper class for types that accept inline text spans. This type collects text spans
+ /// and returns the string represented by the contained text spans.
+ ///
+ public class TextSpanContainer
+ {
+ private readonly List _textSpans = new List();
+
+ public TextSpanContainer(bool trimWhitespace = true)
+ {
+ TrimWhitespace = trimWhitespace;
+ }
+
+ public bool TrimWhitespace { get; }
+
+ ///
+ /// Updates the text spans with the new text at the new index and returns the new
+ /// string represented by the contained text spans.
+ ///
+ ///
+ ///
+ ///
+ public string GetUpdatedText(int index, string text)
+ {
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ if (index >= _textSpans.Count)
+ {
+ // Expand the list to allow for the new text's index to exist
+ _textSpans.AddRange(new string[index - _textSpans.Count + 1]);
+ }
+ _textSpans[index] = text;
+
+ var allText = string.Join(string.Empty, _textSpans);
+ return TrimWhitespace
+ ? allText?.Trim()
+ : allText;
+ }
+ }
+}
diff --git a/CPF.Razor/CpfDispatcher.cs b/CPF.Razor/CpfDispatcher.cs
new file mode 100644
index 0000000..f5a3e6c
--- /dev/null
+++ b/CPF.Razor/CpfDispatcher.cs
@@ -0,0 +1,54 @@
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CPF.Razor
+{
+ public class CpfDispatcher : Dispatcher
+ {
+ public override bool CheckAccess()
+ {
+ return CPF.Threading.Dispatcher.MainThread.CheckAccess();
+ }
+
+ public override Task InvokeAsync(Action workItem)
+ {
+ return Task.Run(() =>
+ {
+ CPF.Threading.Dispatcher.MainThread.Invoke(workItem);
+ });
+ }
+
+ public override Task InvokeAsync(Func workItem)
+ {
+ return Task.Run(() =>
+ {
+ var task = Task.CompletedTask;
+ CPF.Threading.Dispatcher.MainThread.Invoke(() => { task = workItem(); });
+ return task;
+ });
+ }
+
+ public override Task InvokeAsync(Func workItem)
+ {
+ return Task.Run(() =>
+ {
+ TResult result = default;
+ CPF.Threading.Dispatcher.MainThread.Invoke(() => { result = workItem(); });
+ return result;
+ });
+ }
+
+ public override Task InvokeAsync(Func> workItem)
+ {
+ return Task.Run(() =>
+ {
+ TResult result = default;
+ CPF.Threading.Dispatcher.MainThread.Invoke(async () => { result = await workItem(); });
+ return result;
+ });
+ }
+ }
+}
diff --git a/CPF.Razor/CpfElementManager.cs b/CPF.Razor/CpfElementManager.cs
new file mode 100644
index 0000000..9dab84c
--- /dev/null
+++ b/CPF.Razor/CpfElementManager.cs
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+//using Microsoft.MobileBlazorBindings.Core;
+using System;
+using System.Diagnostics;
+
+namespace CPF.Razor
+{
+ internal class CpfElementManager : ElementManager
+ {
+ protected override bool IsParented(ICpfElementHandler handler)
+ {
+ return handler.Element.Parent != null;
+ }
+
+ protected override void AddChildElement(
+ ICpfElementHandler parentHandler,
+ ICpfElementHandler childHandler,
+ int physicalSiblingIndex)
+ {
+ if (parentHandler.Element is CPF.Controls.Panel panel)
+ {
+ if (physicalSiblingIndex <= panel.Children.Count)
+ {
+ panel.Children.Insert(physicalSiblingIndex, childHandler.Element);
+ }
+ else
+ {
+ //Debug.WriteLine($"WARNING: {nameof(AddChildElement)} called with {nameof(physicalSiblingIndex)}={physicalSiblingIndex}, but parentControl.Controls.Count={parentHandler.Control.Controls.Count}");
+ panel.Children.Add(childHandler.Element);
+ }
+ }
+ else if (parentHandler.Element is CPF.Controls.Window win)
+ {
+ if (physicalSiblingIndex <= win.Children.Count)
+ {
+ win.Children.Insert(physicalSiblingIndex, childHandler.Element);
+ }
+ else
+ {
+ win.Children.Add(childHandler.Element);
+ }
+ }
+ else if (parentHandler.Element is CPF.Controls.ContentControl contentControl)
+ {
+ contentControl.Content = childHandler.Element;
+ }
+ else
+ {
+ Debug.Fail("未实现添加控件");
+ }
+ }
+
+ protected override int GetPhysicalSiblingIndex(
+ ICpfElementHandler handler)
+ {
+ return (handler.Element.Parent as CPF.Controls.Panel).Children.IndexOf(handler.Element);
+ }
+
+ protected override void RemoveElement(ICpfElementHandler handler)
+ {
+ if (handler.Element.Parent is CPF.Controls.Panel panel)
+ {
+ panel.Children.Remove(handler.Element);
+ }
+ else
+ {
+ Debug.Fail("未实现移除控件");
+ }
+ }
+
+ protected override bool IsParentOfChild(ICpfElementHandler parentHandler, ICpfElementHandler childHandler)
+ {
+ return childHandler.Element.Parent == parentHandler.Element;
+ }
+ }
+}
diff --git a/CPF.Razor/CpfExtensions.cs b/CPF.Razor/CpfExtensions.cs
new file mode 100644
index 0000000..c852625
--- /dev/null
+++ b/CPF.Razor/CpfExtensions.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.AspNetCore.Components;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+
+namespace CPF.Razor
+{
+ public static class CpfExtensions
+ {
+ ///
+ /// Creates a component of type and adds it as a child of .
+ ///
+ ///
+ ///
+ ///
+ public static void AddComponent(this IHost host, CPF.UIElement parent) where TComponent : IComponent
+ {
+ if (host is null)
+ {
+ throw new ArgumentNullException(nameof(host));
+ }
+
+ if (parent is null)
+ {
+ throw new ArgumentNullException(nameof(parent));
+ }
+
+ var services = host.Services;
+ var renderer = new CpfRenderer(services, services.GetRequiredService());
+
+ //// TODO: This call is an async call, but is called as "fire-and-forget," which is not ideal.
+ //// We need to figure out how to get Xamarin.Forms to run this startup code asynchronously, which
+ //// is how this method should be called.
+ renderer.AddComponent(new ElementHandler(renderer, parent)).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/CPF.Razor/CpfHost.cs b/CPF.Razor/CpfHost.cs
new file mode 100644
index 0000000..8c32046
--- /dev/null
+++ b/CPF.Razor/CpfHost.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.IO;
+
+namespace CPF.Razor
+{
+ public static class CpfHost
+ {
+ public static IHostBuilder CreateDefaultBuilder()
+ {
+ // Inspired by Microsoft.Extensions.Hosting.Host, which can be seen here:
+ // https://github.com/dotnet/extensions/blob/master/src/Hosting/Hosting/src/Host.cs
+ // But slightly modified to work on all of Android, iOS, and UWP.
+
+ var builder = new HostBuilder();
+
+ builder.UseContentRoot(Directory.GetCurrentDirectory());
+
+ builder.ConfigureLogging((hostingContext, logging) =>
+ {
+ logging.AddConsole(configure => configure.DisableColors = true);
+ logging.AddDebug();
+ logging.AddEventSourceLogger();
+ })
+ .UseDefaultServiceProvider((context, options) =>
+ {
+ var isDevelopment = context.HostingEnvironment.IsDevelopment();
+ options.ValidateScopes = isDevelopment;
+ options.ValidateOnBuild = isDevelopment;
+ });
+
+ return builder;
+ }
+ }
+}
diff --git a/CPF.Razor/CpfRenderer.cs b/CPF.Razor/CpfRenderer.cs
new file mode 100644
index 0000000..6970d4b
--- /dev/null
+++ b/CPF.Razor/CpfRenderer.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.Logging;
+using System;
+using Microsoft.AspNetCore.Components;
+using System.Diagnostics;
+//using Microsoft.MobileBlazorBindings.Core;
+
+namespace CPF.Razor
+{
+ public class CpfRenderer : NativeComponentRenderer
+ {
+ public CpfRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
+ : base(serviceProvider, loggerFactory)
+ {
+ }
+
+ protected override void HandleException(Exception exception)
+ {
+ //MessageBox.Show(exception?.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ Debug.WriteLine(exception?.Message);
+ }
+
+ protected override ElementManager CreateNativeControlManager()
+ {
+ return new CpfElementManager();
+ }
+
+ public override Dispatcher Dispatcher => new CpfDispatcher();
+ }
+}
diff --git a/CPF.Razor/ElementHandler.cs b/CPF.Razor/ElementHandler.cs
new file mode 100644
index 0000000..f6975b0
--- /dev/null
+++ b/CPF.Razor/ElementHandler.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CPF.Razor
+{
+ public class ElementHandler : ICpfElementHandler
+ {
+ public ElementHandler(NativeComponentRenderer renderer, CPF.UIElement elementControl)
+ {
+ Renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
+ Element = elementControl ?? throw new ArgumentNullException(nameof(elementControl));
+ }
+
+ //protected void RegisterEvent(string eventName, Action setId, Action clearId)
+ //{
+ // RegisteredEvents[eventName] = new EventRegistration(eventName, setId, clearId);
+ //}
+ //private Dictionary RegisteredEvents { get; } = new Dictionary();
+
+ public NativeComponentRenderer Renderer { get; }
+ public CPF.UIElement Element { get; }
+ public object TargetElement => Element;
+
+ public virtual void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
+ {
+ //switch (attributeName)
+ //{
+ // case nameof(XF.Element.AutomationId):
+ // ElementControl.AutomationId = (string)attributeValue;
+ // break;
+ // case nameof(XF.Element.ClassId):
+ // ElementControl.ClassId = (string)attributeValue;
+ // break;
+ // case nameof(XF.Element.StyleId):
+ // ElementControl.StyleId = (string)attributeValue;
+ // break;
+ // default:
+ // if (!TryRegisterEvent(attributeName, attributeEventHandlerId))
+ // {
+ // throw new NotImplementedException($"{GetType().FullName} doesn't recognize attribute '{attributeName}'");
+ // }
+ // break;
+ //}
+ var p = Element.GetPropertyMetadata(attributeName);
+ if (p != null)
+ {
+ Element.SetValue(attributeValue.ConvertTo(p.PropertyType), attributeName);
+ }
+ }
+
+ //private bool TryRegisterEvent(string eventName, ulong eventHandlerId)
+ //{
+ // if (RegisteredEvents.TryGetValue(eventName, out var eventRegistration))
+ // {
+ // Renderer.RegisterEvent(eventHandlerId, eventRegistration.ClearId);
+ // eventRegistration.SetId(eventHandlerId);
+
+ // return true;
+ // }
+ // return false;
+ //}
+ }
+}
diff --git a/CPF.Razor/ICpfElementHandler.cs b/CPF.Razor/ICpfElementHandler.cs
new file mode 100644
index 0000000..403728f
--- /dev/null
+++ b/CPF.Razor/ICpfElementHandler.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+//using Microsoft.MobileBlazorBindings.Core;
+
+namespace CPF.Razor
+{
+ public interface ICpfElementHandler : IElementHandler
+ {
+ UIElement Element { get; }
+ }
+}
diff --git a/ConsoleApp1.sln b/ConsoleApp1.sln
index 1901bb3..f0d8f66 100644
--- a/ConsoleApp1.sln
+++ b/ConsoleApp1.sln
@@ -59,6 +59,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CPF.Demo", "CPF_Demo\CPF.De
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "蓝图重制版", "蓝图重制版\蓝图重制版.csproj", "{003E155A-8C40-41AF-A796-ED17E729E013}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CPF.Razor", "CPF.Razor\CPF.Razor.csproj", "{87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CpfRazorSample", "CpfRazorSample\CpfRazorSample.csproj", "{25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Private\SharedVSIX\SharedVSIX.projitems*{db53e8d7-dfb6-48eb-a7b6-d1cf762acb9b}*SharedItemsImports = 4
@@ -224,6 +228,18 @@ Global
{003E155A-8C40-41AF-A796-ED17E729E013}.Release|Any CPU.Build.0 = Release|Any CPU
{003E155A-8C40-41AF-A796-ED17E729E013}.类库d|Any CPU.ActiveCfg = 类库d|Any CPU
{003E155A-8C40-41AF-A796-ED17E729E013}.类库d|Any CPU.Build.0 = 类库d|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.Release|Any CPU.Build.0 = Release|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.类库d|Any CPU.ActiveCfg = Debug|Any CPU
+ {87E1ED0A-BFBF-4F5E-9FDF-5EAFE48DD719}.类库d|Any CPU.Build.0 = Debug|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.类库d|Any CPU.ActiveCfg = Debug|Any CPU
+ {25A4EE47-F5BD-4F1E-B143-3E3B50C5AC2A}.类库d|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -241,4 +257,7 @@ Global
{F34CFFEE-546F-490E-A76A-2792840B284D} = {2B729C46-7592-425A-87E9-D769A94881F7}
{DF526631-D060-47F2-AFD4-62C6CEA2FE9A} = {2B729C46-7592-425A-87E9-D769A94881F7}
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {16FB883C-167C-4E1A-B311-6D74452A3CD6}
+ EndGlobalSection
EndGlobal
diff --git a/CpfRazorSample/CpfRazorSample.csproj b/CpfRazorSample/CpfRazorSample.csproj
new file mode 100644
index 0000000..f7ea705
--- /dev/null
+++ b/CpfRazorSample/CpfRazorSample.csproj
@@ -0,0 +1,35 @@
+
+
+
+ WinExe
+ netcoreapp3.0
+
+
+ 9.0
+ 3.0
+
+
+
+ true
+
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CpfRazorSample/Program.cs b/CpfRazorSample/Program.cs
new file mode 100644
index 0000000..5786464
--- /dev/null
+++ b/CpfRazorSample/Program.cs
@@ -0,0 +1,34 @@
+using CPF.Platform;
+using CPF.Skia;
+using CPF.Windows;
+using Microsoft.Extensions.Hosting;
+using System;
+using CPF.Razor;
+
+namespace CpfRazorSample
+{
+ class Program
+ {
+ [STAThread]
+ static void Main(string[] args)
+ {
+ Application.Initialize(
+ (OperatingSystemType.Windows, new WindowsPlatform(), new SkiaDrawingFactory())
+ , (OperatingSystemType.OSX, new CPF.Mac.MacPlatform(), new SkiaDrawingFactory())//如果需要支持Mac才需要
+ , (OperatingSystemType.Linux, new CPF.Linux.LinuxPlatform(), new SkiaDrawingFactory())//如果需要支持Linux才需要
+ );
+
+ var host = Host.CreateDefaultBuilder()
+ .ConfigureServices((hostContext, services) =>
+ {
+ // Register app-specific services
+ //services.AddSingleton();
+ })
+ .Build();
+
+ var window = new CPF.Controls.Window();
+ host.AddComponent(window);
+ Application.Run(window);
+ }
+ }
+}
diff --git a/CpfRazorSample/Stylesheet1.css b/CpfRazorSample/Stylesheet1.css
new file mode 100644
index 0000000..d895ab6
--- /dev/null
+++ b/CpfRazorSample/Stylesheet1.css
@@ -0,0 +1,502 @@
+/*@font-face {
+ font-family: '微软雅黑';
+ src: url('res://ConsoleApp1/msyh.ttc');
+}*/ /*加载字体*/
+
+/** {
+ FontFamily: 微软雅黑;
+}*/
+
+@media windows {
+ * {
+ FontFamily: '微软雅黑'; /*不同系统的字体不同,自己根据情况改或者使用内嵌字体*/
+ }
+}
+
+@media osx {
+ * {
+ FontFamily: '苹方-简';
+ }
+}
+
+@media linux {
+ * {
+ FontFamily: '文泉驿正黑';
+ }
+}
+/*设置窗体标题栏背景颜色圆角*/
+/*#caption {
+ IsAntiAlias: true;
+ Background: #2c2c2c;
+ CornerRadius:5,5,0,0;
+ Height:30;
+}
+#frame {
+ CornerRadius: 5;
+ IsAntiAlias: true;
+}*/
+Button {
+ BorderFill: #DCDFE6;
+ IsAntiAlias: True;
+ CornerRadius: 4,4,4,4;
+ Background: #FFFFFF;
+}
+
+ Button[IsMouseOver=true] {
+ BorderFill: rgb(198,226,255);
+ Background: rgb(236,245,255);
+ Foreground: rgb(64,158,255);
+ }
+
+ Button[IsPressed=true] {
+ BorderFill: rgb(58,142,230);
+ }
+
+ Button.primary {
+ BorderFill: rgb(64,158,255);
+ CornerRadius: 4,4,4,4;
+ Background: rgb(64,158,255);
+ Foreground: #FFFFFF;
+ }
+
+ Button.primary[IsMouseOver=true] {
+ BorderFill: rgb(102,177,255);
+ Background: rgb(102,177,255);
+ Foreground: #FFFFFF;
+ }
+
+ Button.primary[IsPressed=true] {
+ BorderFill: rgb(58,142,230);
+ Background: rgb(58,142,230);
+ }
+
+ Button.success {
+ BorderFill: rgb(103,194,58);
+ CornerRadius: 4,4,4,4;
+ Background: rgb(103,194,58);
+ Foreground: #FFFFFF;
+ }
+
+ Button.success[IsMouseOver=true] {
+ BorderFill: rgb(133,206,97);
+ Background: rgb(133,206,97);
+ Foreground: #FFFFFF;
+ }
+
+ Button.success[IsPressed=true] {
+ BorderFill: rgb(93,175,52);
+ Background: rgb(93,175,52);
+ }
+
+ Button.danger {
+ BorderFill: rgb(245,108,108);
+ Background: rgb(245,108,108);
+ CornerRadius: 4,4,4,4;
+ Foreground: #FFFFFF;
+ }
+
+ Button.danger[IsMouseOver=true] {
+ BorderFill: rgb(247,137,137);
+ Background: rgb(247,137,137);
+ Foreground: #FFFFFF;
+ }
+
+ Button.danger[IsPressed=true] {
+ BorderFill: rgb(221,97,97);
+ Background: rgb(221,97,97);
+ }
+
+#dialogClose {
+ BorderFill: null;
+}
+
+TextBox, .textBox, DatePicker {
+ Background: #fff;
+ IsAntiAlias: true;
+ BorderFill: #DCDFE6;
+ CornerRadius: 4,4,4,4;
+ BorderStroke: 1;
+}
+
+ .groupPanel TextBox, DatePicker TextBox, .textBox TextBox {
+ BorderStroke: 0;
+ }
+
+ .textBox[IsKeyboardFocusWithin=true] {
+ BorderFill: #1E9FFF;
+ }
+
+.singleLine { /*单行文本框*/
+ AcceptsReturn: false;
+ HScrollBarVisibility: Hidden;
+ VScrollBarVisibility: Hidden;
+}
+
+ .singleLine #contentPresenter {
+ Padding: 3;
+ }
+
+.multiline { /*多行文本框*/
+}
+
+
+.slotLeft {
+ CornerRadius: 0,4,4,0;
+ BorderFill: #DCDFE6;
+ Background: #F5F7FA;
+ BorderThickness: 1,0,0,0;
+ BorderType: BorderThickness;
+ MarginTop: 0;
+ MarginBottom: 0;
+ MarginRight: 0;
+}
+
+RadioButton #radioButtonBorder {
+ StrokeFill: rgb(220,223,230);
+}
+
+RadioButton[IsChecked=true] #radioButtonBorder {
+ StrokeFill: #1E9FFF;
+ Fill: #1E9FFF;
+}
+
+RadioButton #optionMark {
+ StrokeFill: #1E9FFF;
+ Fill: #fff;
+}
+
+CheckBox #indeterminateMark {
+ Fill: #1E9FFF;
+}
+
+CheckBox #checkBoxBorder {
+ Background: #fff;
+ BorderFill: rgb(220,223,230);
+}
+
+CheckBox[IsChecked=true] #checkBoxBorder {
+ Background: #1E9FFF;
+ BorderFill: #1E9FFF;
+}
+
+CheckBox Polyline {
+ StrokeFill: #fff;
+}
+
+.radioGroup {
+ BorderType: BorderThickness;
+ BorderFill: rgb(220,223,230);
+ BorderThickness: 1,1,0,1;
+}
+
+ .radioGroup RadioButton {
+ BorderType: BorderThickness;
+ BorderFill: rgb(220,223,230);
+ BorderThickness: 0,0,1,0;
+ }
+
+ .radioGroup RadioButton #markPanel {
+ Visibility: Collapsed;
+ }
+
+ .radioGroup RadioButton TextBlock {
+ Margin: 5;
+ }
+
+ .radioGroup RadioButton[IsChecked=true] {
+ Background: rgb(64,158,255);
+ Foreground: #fff;
+ }
+
+.error {
+ Foreground: #f00;
+ Visibility: Collapsed;
+}
+
+ .error[DesignMode=true] {
+ Visibility: Visible;
+ }
+
+.twoLine[AttachedExtenstions.IsError=true] .error {
+ Visibility: Visible;
+}
+
+.twoLine[AttachedExtenstions.IsError=true] .textBox {
+ BorderFill: #f00;
+}
+
+ScrollBar {
+ Background: null;
+}
+
+ ScrollBar Thumb {
+ IsAntiAlias: true;
+ CornerRadius: 5;
+ }
+
+ ScrollBar[Orientation=Horizontal] {
+ Height: 12;
+ }
+
+ ScrollBar[Orientation=Vertical] {
+ Width: 12;
+ }
+
+ ScrollBar #PART_LineUpButton, ScrollBar #PART_LineDownButton {
+ Visibility: Collapsed;
+ }
+
+ComboBox {
+ Background: #fff;
+ IsAntiAlias: true;
+ BorderFill: #DCDFE6;
+ CornerRadius: 4,4,4,4;
+ BorderStroke: 1;
+}
+
+ ComboBox[IsKeyboardFocusWithin=true] {
+ BorderFill: #1E9FFF;
+ }
+
+#DropDownPanel TextBlock {
+ MarginLeft: 5;
+ MarginTop: 2;
+ MarginBottom: 2;
+}
+
+#dropDownBorder {
+ ShadowBlur: 2;
+ ShadowColor: rgba(0, 0, 0, 0.4);
+ BorderStroke: 0;
+}
+
+#DropDownPanel[IsMouseOver=false] ScrollBar {
+ Visibility: Collapsed;
+}
+
+#DropDownPanel ScrollBar[Orientation=Horizontal] {
+ Height: 10;
+}
+
+#DropDownPanel ScrollBar[Orientation=Vertical] {
+ Width: 10;
+}
+
+Slider {
+ IsAntiAlias: true;
+}
+
+ Slider Thumb {
+ IsAntiAlias: true;
+ Width: 16;
+ Height: 16;
+ CornerRadius: 7;
+ BorderFill: rgb(64,158,255);
+ BorderStroke: 2;
+ Background: #fff;
+ ZIndex: 1;
+ }
+
+
+ Slider Thumb[IsMouseOver=true] {
+ animation-name: sliderMouseOver;
+ animation-duration: 0.1s;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ }
+
+ Slider #TrackBackground {
+ CornerRadius: 2;
+ Background: rgb(228,231,237);
+ BorderStroke: 0;
+ }
+
+ Slider #decreaseRepeatButton {
+ Background: rgb(64,158,255);
+ CornerRadius: 2;
+ }
+
+ Slider[Orientation=Horizontal] #decreaseRepeatButton {
+ Height: 4;
+ MarginLeft: 5;
+ }
+
+ Slider[Orientation=Vertical] #decreaseRepeatButton {
+ Width: 4;
+ MarginBottom: 5;
+ }
+
+ProgressBar {
+ CornerRadius: 5;
+ IsAntiAlias: true;
+ BorderFill: null;
+ Background: rgb(235,238,245);
+}
+
+ ProgressBar #Indicator, ProgressBar #Animation {
+ CornerRadius: 5;
+ }
+
+NumericUpDown {
+ Background: #fff;
+ IsAntiAlias: true;
+ BorderFill: #DCDFE6;
+ CornerRadius: 4,4,4,4;
+ BorderStroke: 1;
+}
+
+ NumericUpDown RepeatButton {
+ Width: 20;
+ Background: rgb(245,247,250);
+ }
+
+ NumericUpDown #decreaseBtn {
+ CornerRadius: 4,0,0,4;
+ }
+
+ NumericUpDown #increaseBtn {
+ CornerRadius: 0,4,4,0;
+ }
+
+ NumericUpDown #textBoxBorder {
+ BorderFill: #DCDFE6;
+ }
+
+ NumericUpDown TextBox {
+ BorderStroke: 0;
+ }
+
+.widget {
+ IsAntiAlias: true;
+ BorderFill: #DCDFE6;
+ CornerRadius: 4,4,4,4;
+ BorderStroke: 1;
+}
+
+.widgetHead {
+ Background: linear-gradient(0 0,0 100%,#F7F7F7 0,#F0F0F0 1);
+ BorderType: BorderThickness;
+ BorderThickness: 0,0,0,1;
+ BorderFill: #DCDFE6;
+}
+
+DataGrid {
+ Foreground: #7a7a7a;
+}
+
+DataGridCellTemplate, DataGridRow, DataGrid, .DataGridCell {
+ BorderFill: rgb(235,238,245);
+}
+
+DataGridRow {
+ Height: 36;
+}
+
+ DataGridRow[IsMouseOver=true] {
+ Background: rgb(245,247,250);
+ }
+
+ DataGridRow[IsSelected=true] {
+ Background: rgb(245,247,250);
+ }
+
+DataGridColumnTemplate {
+ Height: 38;
+ FontSize: 15;
+ FontStyle: Bold;
+ Background: #fff;
+ BorderFill: rgb(235,238,245);
+}
+
+TabControl #headBorder {
+ Background: #fff;
+}
+
+TabItem > Border {
+ BorderThickness: 0,0,0,2;
+}
+
+TabItem[IsSelected=true] > Border {
+ BorderFill: #1E9FFF;
+}
+
+TabItem[IsSelected=true] {
+ Foreground: #1E9FFF;
+}
+
+TabControl[TabStripPlacement=Left] TabItem, TabControl[TabStripPlacement=Right] TabItem {
+ Width: 100%;
+ BorderType: BorderThickness;
+ BorderThickness: 0,0,0,1;
+ BorderFill: #e8e8e8;
+}
+
+ TabControl[TabStripPlacement=Left] TabItem TextBlock {
+ MarginRight: 0;
+ }
+
+ TabControl[TabStripPlacement=Left] TabItem > Border {
+ Width: 100%;
+ }
+
+TabControl[TabStripPlacement=Left] #headerPanel, TabControl[TabStripPlacement=Right] #headerPanel {
+ Width: 100;
+ Background: rgb(245,247,250);
+}
+
+.closeBtn[IsMouseOver=true] {
+ Fill: #171717;
+}
+
+.placeholder {
+ IsHitTestVisible: false;
+ Foreground: "192,196,204";
+ Visibility: Collapsed;
+}
+
+.textBox[AttachedExtenstions.IsEmpty=true] .placeholder {
+ Visibility: Visible;
+}
+
+.loginBox TextBox, .loginBox .placeholder {
+ FontSize: 16;
+}
+
+.loginBox CheckBox {
+ Foreground: #757575;
+}
+
+.searchBox Button {
+ CornerRadius: 0,4,4,0,
+}
+
+#MenuPop #menuPanel > Border, #MenuPop ContextMenu > Border {
+ Background: #fff;
+ ShadowColor: rgba(0, 0, 0, 0.4);
+}
+
+#MenuPop MenuItem[IsMouseOver=true] {
+ Background: #DCDFE6;
+}
+
+ListBoxItem {
+ Width: 100%;
+}
+
+#dialogClose, #dialogClose[IsMouseOver=true] {
+ Background: null;
+ BorderFill: null;
+}
+
+ #dialogClose[IsPressed=true] {
+ Background: null;
+ BorderFill: null;
+ }
+
+ #dialogClose Line {
+ StrokeFill: 218,218,218;
+ }
+
+ #dialogClose[IsMouseOver=true] Line {
+ StrokeFill: #fff;
+ }
diff --git a/CpfRazorSample/Test.razor b/CpfRazorSample/Test.razor
new file mode 100644
index 0000000..9483d06
--- /dev/null
+++ b/CpfRazorSample/Test.razor
@@ -0,0 +1,7 @@
+
+
+
+
+
+@**@
+@**@
\ No newline at end of file
diff --git a/CpfRazorSample/Window1.cs b/CpfRazorSample/Window1.cs
new file mode 100644
index 0000000..c668042
--- /dev/null
+++ b/CpfRazorSample/Window1.cs
@@ -0,0 +1,52 @@
+using CPF;
+using CPF.Animation;
+using CPF.Charts;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Shapes;
+using CPF.Styling;
+using CPF.Svg;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace CpfRazorSample
+{
+ public class Window1 : Window
+ {
+ protected override void InitializeComponent()
+ {
+ LoadStyleFile("res://CpfRazorSample/Stylesheet1.css");//加载样式文件,文件需要设置为内嵌资源
+
+ Title = "标题";
+ Width = 500;
+ Height = 400;
+ Background = null;
+ Children.Add(new WindowFrame(this, new Panel
+ {
+ Width = "100%",
+ Height = "100%",
+ Children =
+ {
+ new Button{ Content="按钮" }
+ }
+ }));
+
+ if (!DesignMode)//设计模式下不执行,也可以用#if !DesignMode
+ {
+
+ }
+ }
+
+#if !DesignMode //用户代码写到这里,设计器下不执行,防止设计器出错
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ }
+ //用户代码
+
+#endif
+ }
+}
diff --git a/CpfRazorSample/_Imports.razor b/CpfRazorSample/_Imports.razor
new file mode 100644
index 0000000..afab265
--- /dev/null
+++ b/CpfRazorSample/_Imports.razor
@@ -0,0 +1,3 @@
+@using CPF.Razor.Controls
+@using CpfRazorSample
+@*@using CPF.Controls*@
\ No newline at end of file
From f86e0009844cdeff19232e01d730be72bd0b0094 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E7=BA=A2=E5=B8=BD?= <761716178@qq.com>
Date: Mon, 8 Jan 2024 17:37:26 +0800
Subject: [PATCH 4/6] =?UTF-8?q?Razor=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CPF.Razor/Controls/Element.cs | 104 +++++++++++++++----
CPF.Razor/Controls/Panel.cs | 35 +------
CPF.Razor/Core/NativeComponentAdapter.cs | 4 +
CPF.Razor/Core/NativeControlComponentBase.cs | 9 +-
CPF.Razor/ElementHandler.cs | 2 +
CPF.Razor/ICpfElementHandler.cs | 1 +
CpfRazorSample/Component1.razor | 4 +
CpfRazorSample/Test.razor | 18 +++-
8 files changed, 122 insertions(+), 55 deletions(-)
create mode 100644 CpfRazorSample/Component1.razor
diff --git a/CPF.Razor/Controls/Element.cs b/CPF.Razor/Controls/Element.cs
index fd6fa35..c5ee696 100644
--- a/CPF.Razor/Controls/Element.cs
+++ b/CPF.Razor/Controls/Element.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Components;
+using CPF.Input;
+using Microsoft.AspNetCore.Components;
//using Microsoft.MobileBlazorBindings.Core;
using System;
using System.Collections.Generic;
@@ -8,33 +9,100 @@ namespace CPF.Razor.Controls
{
public abstract class Element : NativeControlComponentBase where T : UIElement, new()
{
- [Parameter] public string MarginLeft { get; set; }
- [Parameter] public string MarginTop { get; set; }
- [Parameter] public string Width { get; set; }
- [Parameter] public string Height { get; set; }
-
- //public CPF.UIElement NativeControl => ((ICpfElementHandler)ElementHandler).Element;
-
protected override void RenderAttributes(AttributesBuilder builder)
{
base.RenderAttributes(builder);
- if (MarginLeft != null)
+ var type = GetType();
+ var ps = type.GetProperties();
+ foreach (var item in ps)
{
- builder.AddAttribute(nameof(MarginLeft), MarginLeft);
+ var attr = item.GetCustomAttributes(typeof(ParameterAttribute), true);
+ if (attr != null && attr.Length > 0 && item.PropertyType != typeof(RenderFragment))
+ {
+ var v = item.GetValue(this);
+ if (v != null)
+ {
+ if (item.PropertyType == typeof(EventCallback) || (item.PropertyType.IsGenericType && item.PropertyType.GetGenericTypeDefinition() == typeof(EventCallback<>)))
+ {//事件注册还必须加小写的on
+ builder.AddAttribute("on" + item.Name, v);
+ }
+ else
+ {
+ builder.AddAttribute(item.Name, v);
+ }
+ }
+ }
}
- if (MarginTop != null)
+
+ //if (MarginLeft != null)
+ //{
+ // builder.AddAttribute(nameof(MarginLeft), MarginLeft);
+ //}
+ //if (MarginTop != null)
+ //{
+ // builder.AddAttribute(nameof(MarginTop), MarginTop);
+ //}
+ //if (Height != null)
+ //{
+ // builder.AddAttribute(nameof(Height), Height);
+ //}
+ //if (Width != null)
+ //{
+ // builder.AddAttribute(nameof(Width), Width);
+ //}
+ }
+ public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
+ {
+ var p = Element.GetPropertyMetadata(attributeName);
+ if (p != null)
{
- builder.AddAttribute(nameof(MarginTop), MarginTop);
+ Element.SetValue(attributeValue.ConvertTo(p.PropertyType), attributeName);
}
- if (Height != null)
+ else
{
- builder.AddAttribute(nameof(Height), Height);
- }
- if (Width != null)
- {
- builder.AddAttribute(nameof(Width), Width);
+ if (events.Contains(attributeName))
+ {
+ handlerIds[attributeName] = attributeEventHandlerId;
+ Renderer.RegisterEvent(attributeEventHandlerId, id => { if (id == attributeEventHandlerId) { handlerIds.Remove(attributeName); } });
+ }
}
}
+
+ Dictionary handlerIds = new Dictionary();
+ HashSet events = new HashSet();
+
+ protected override T CreateElement()
+ {
+ var r = base.CreateElement();
+ var type = typeof(T);
+ var ps = type.GetEvents();
+ foreach (var item in ps)
+ {
+ var name = "on" + item.Name;
+ events.Add(name);
+ r.Commands.Add(item.Name, (s, e) =>
+ {
+ if (handlerIds.TryGetValue(name, out var id))
+ {
+ Renderer.Dispatcher.InvokeAsync(() => Renderer.DispatchEventAsync(id, null, e as EventArgs));
+ }
+ });
+ }
+
+ return r;
+ }
+
+
+ //只要属性和事件自动生成就行
+ [Parameter] public string Name { get; set; }
+ [Parameter] public FloatField? MarginLeft { get; set; }
+ [Parameter] public FloatField? MarginTop { get; set; }
+ [Parameter] public FloatField? MarginBottom { get; set; }
+ [Parameter] public FloatField? MarginRight { get; set; }
+ [Parameter] public CPF.FloatField? Width { get; set; }
+ [Parameter] public CPF.FloatField? Height { get; set; }
+ [Parameter] public EventCallback MouseDown { get; set; }
+
}
}
diff --git a/CPF.Razor/Controls/Panel.cs b/CPF.Razor/Controls/Panel.cs
index a49199f..e989a47 100644
--- a/CPF.Razor/Controls/Panel.cs
+++ b/CPF.Razor/Controls/Panel.cs
@@ -6,45 +6,16 @@ using System.Text;
namespace CPF.Razor.Controls
{
+ ///
+ /// 测试
+ ///
public partial class Panel : Element
{
- //static Panel()
- //{
- // ElementHandlerRegistry.RegisterElementHandler();
- //}
-
[Parameter] public string Background { get; set; }
#pragma warning disable CA1721 // Property names should not match get methods
[Parameter] public RenderFragment ChildContent { get; set; }
#pragma warning restore CA1721 // Property names should not match get methods
- protected override void RenderAttributes(AttributesBuilder builder)
- {
- base.RenderAttributes(builder);
-
- if (Background != null)
- {
- builder.AddAttribute(nameof(Background), Background);
- }
- }
protected override RenderFragment GetChildContent() => ChildContent;
-
- public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
- {
- //switch (attributeName)
- //{
- // //case nameof(AutoScroll):
- // // AutoScroll = AttributeHelper.GetBool(attributeValue);
- // // break;
- // default:
-
- // break;
- //}
- var p = Element.GetPropertyMetadata(attributeName);
- if (p != null)
- {
- Element.SetValue(attributeValue.ConvertTo(p.PropertyType), attributeName);
- }
- }
}
}
diff --git a/CPF.Razor/Core/NativeComponentAdapter.cs b/CPF.Razor/Core/NativeComponentAdapter.cs
index 1658948..2e26614 100644
--- a/CPF.Razor/Core/NativeComponentAdapter.cs
+++ b/CPF.Razor/Core/NativeComponentAdapter.cs
@@ -258,6 +258,10 @@ namespace CPF.Razor
//{
// componentInstance.SetElementReference(elementHandler);
//}
+ if (elementHandler is ICpfElementHandler handler)
+ {
+ handler.Renderer = Renderer;
+ }
if (siblingIndex != 0)
{
diff --git a/CPF.Razor/Core/NativeControlComponentBase.cs b/CPF.Razor/Core/NativeControlComponentBase.cs
index 79f0ac9..33e69fb 100644
--- a/CPF.Razor/Core/NativeControlComponentBase.cs
+++ b/CPF.Razor/Core/NativeControlComponentBase.cs
@@ -9,7 +9,7 @@ namespace CPF.Razor
{
public abstract class NativeControlComponentBase : ComponentBase, ICpfElementHandler where T : UIElement, new()
{
- public IElementHandler ElementHandler { get; private set; }
+ //public IElementHandler ElementHandler { get; private set; }
UIElement ICpfElementHandler.Element => Element;
@@ -28,6 +28,13 @@ namespace CPF.Razor
public object TargetElement => Element;
+ NativeComponentRenderer _Renderer;
+ public NativeComponentRenderer Renderer
+ {
+ get => _Renderer;
+ set => _Renderer = value;
+ }
+
//public void SetElementReference(IElementHandler elementHandler)
//{
// ElementHandler = elementHandler ?? throw new ArgumentNullException(nameof(elementHandler));
diff --git a/CPF.Razor/ElementHandler.cs b/CPF.Razor/ElementHandler.cs
index f6975b0..c2428bf 100644
--- a/CPF.Razor/ElementHandler.cs
+++ b/CPF.Razor/ElementHandler.cs
@@ -22,6 +22,8 @@ namespace CPF.Razor
public CPF.UIElement Element { get; }
public object TargetElement => Element;
+ NativeComponentRenderer ICpfElementHandler.Renderer { get; set; }
+
public virtual void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
{
//switch (attributeName)
diff --git a/CPF.Razor/ICpfElementHandler.cs b/CPF.Razor/ICpfElementHandler.cs
index 403728f..3bda0a4 100644
--- a/CPF.Razor/ICpfElementHandler.cs
+++ b/CPF.Razor/ICpfElementHandler.cs
@@ -8,5 +8,6 @@ namespace CPF.Razor
public interface ICpfElementHandler : IElementHandler
{
UIElement Element { get; }
+ NativeComponentRenderer Renderer { get; set; }
}
}
diff --git a/CpfRazorSample/Component1.razor b/CpfRazorSample/Component1.razor
new file mode 100644
index 0000000..5a93e1d
--- /dev/null
+++ b/CpfRazorSample/Component1.razor
@@ -0,0 +1,4 @@
+
+@code {
+
+}
diff --git a/CpfRazorSample/Test.razor b/CpfRazorSample/Test.razor
index 9483d06..e29b8c7 100644
--- a/CpfRazorSample/Test.razor
+++ b/CpfRazorSample/Test.razor
@@ -1,7 +1,17 @@
-
-
+
+
+ @if (visible)
+ {
+
+ }
-@**@
-@**@
\ No newline at end of file
+@code
+{
+ bool visible = false;
+ void OnMouseDown()
+ {
+ visible = !visible;
+ }
+}
\ No newline at end of file
From a7c1ac873cbeb57b2a7342e4421c6c5dd34abf22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E7=BA=A2=E5=B8=BD?= <761716178@qq.com>
Date: Mon, 8 Jan 2024 20:55:43 +0800
Subject: [PATCH 5/6] =?UTF-8?q?Razor=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CPF.Razor/Controls/Element.cs | 20 +--
CPF.Razor/Controls/Panel.cs | 5 +-
CPF.Razor/Controls/StackPanel.cs | 15 ++
.../Controls/generated/Border.generated.cs | 60 +++++++
.../Controls/generated/Button.generated.cs | 77 +++++++++
.../Controls/generated/Calendar.generated.cs | 75 +++++++++
.../Controls/generated/Chart.generated.cs | 139 ++++++++++++++++
.../Controls/generated/CheckBox.generated.cs | 83 ++++++++++
.../generated/CodeTextBox.generated.cs | 116 +++++++++++++
.../Controls/generated/ComboBox.generated.cs | 118 +++++++++++++
.../generated/ContentControl.generated.cs | 75 +++++++++
.../Controls/generated/Control.generated.cs | 70 ++++++++
.../Controls/generated/DataGrid.generated.cs | 113 +++++++++++++
.../generated/DatePicker.generated.cs | 79 +++++++++
.../Controls/generated/Decorator.generated.cs | 71 ++++++++
.../Controls/generated/DockPanel.generated.cs | 55 ++++++
.../generated/DocumentBlock.generated.cs | 28 ++++
.../Controls/generated/Ellipse.generated.cs | 28 ++++
.../Controls/generated/Expander.generated.cs | 77 +++++++++
.../Controls/generated/Grid.generated.cs | 59 +++++++
.../generated/GridSplitter.generated.cs | 80 +++++++++
.../Controls/generated/Label.generated.cs | 71 ++++++++
.../Controls/generated/Line.generated.cs | 30 ++++
.../Controls/generated/ListBox.generated.cs | 115 +++++++++++++
.../generated/NativeElement.generated.cs | 29 ++++
.../generated/NumericUpDown.generated.cs | 89 ++++++++++
.../generated/PageManger.generated.cs | 71 ++++++++
.../Controls/generated/Panel.generated.cs | 51 ++++++
.../Controls/generated/Path.generated.cs | 33 ++++
.../Controls/generated/Picture.generated.cs | 34 ++++
.../Controls/generated/PieChart.generated.cs | 78 +++++++++
.../Controls/generated/Polygon.generated.cs | 28 ++++
.../Controls/generated/Polyline.generated.cs | 28 ++++
.../generated/ProgressBar.generated.cs | 90 ++++++++++
.../generated/RadioButton.generated.cs | 87 ++++++++++
.../Controls/generated/Rectangle.generated.cs | 28 ++++
.../generated/RepeatButton.generated.cs | 79 +++++++++
.../generated/ResponsivePanel.generated.cs | 63 +++++++
CPF.Razor/Controls/generated/SVG.generated.cs | 34 ++++
.../Controls/generated/ScrollBar.generated.cs | 87 ++++++++++
.../generated/ScrollViewer.generated.cs | 79 +++++++++
.../Controls/generated/Separator.generated.cs | 70 ++++++++
.../Controls/generated/Slider.generated.cs | 121 ++++++++++++++
.../generated/StackPanel.generated.cs | 55 ++++++
.../Controls/generated/Switch.generated.cs | 91 ++++++++++
.../generated/TabControl.generated.cs | 78 +++++++++
.../Controls/generated/TextBlock.generated.cs | 45 +++++
.../Controls/generated/TextBox.generated.cs | 136 +++++++++++++++
.../Controls/generated/Thumb.generated.cs | 74 +++++++++
.../generated/TimePicker.generated.cs | 74 +++++++++
.../generated/ToggleButton.generated.cs | 83 ++++++++++
.../Controls/generated/Track.generated.cs | 76 +++++++++
.../Controls/generated/TreeView.generated.cs | 88 ++++++++++
.../Controls/generated/UIElement.generated.cs | 156 ++++++++++++++++++
.../Controls/generated/Viewbox.generated.cs | 79 +++++++++
.../Controls/generated/WrapPanel.generated.cs | 54 ++++++
CPF.Razor/CpfExtensions.cs | 2 +-
CpfRazorSample/Test.razor | 11 +-
58 files changed, 3921 insertions(+), 19 deletions(-)
create mode 100644 CPF.Razor/Controls/StackPanel.cs
create mode 100644 CPF.Razor/Controls/generated/Border.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Button.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Calendar.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Chart.generated.cs
create mode 100644 CPF.Razor/Controls/generated/CheckBox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/CodeTextBox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ComboBox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ContentControl.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Control.generated.cs
create mode 100644 CPF.Razor/Controls/generated/DataGrid.generated.cs
create mode 100644 CPF.Razor/Controls/generated/DatePicker.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Decorator.generated.cs
create mode 100644 CPF.Razor/Controls/generated/DockPanel.generated.cs
create mode 100644 CPF.Razor/Controls/generated/DocumentBlock.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Ellipse.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Expander.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Grid.generated.cs
create mode 100644 CPF.Razor/Controls/generated/GridSplitter.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Label.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Line.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ListBox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/NativeElement.generated.cs
create mode 100644 CPF.Razor/Controls/generated/NumericUpDown.generated.cs
create mode 100644 CPF.Razor/Controls/generated/PageManger.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Panel.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Path.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Picture.generated.cs
create mode 100644 CPF.Razor/Controls/generated/PieChart.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Polygon.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Polyline.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ProgressBar.generated.cs
create mode 100644 CPF.Razor/Controls/generated/RadioButton.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Rectangle.generated.cs
create mode 100644 CPF.Razor/Controls/generated/RepeatButton.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ResponsivePanel.generated.cs
create mode 100644 CPF.Razor/Controls/generated/SVG.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ScrollBar.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ScrollViewer.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Separator.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Slider.generated.cs
create mode 100644 CPF.Razor/Controls/generated/StackPanel.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Switch.generated.cs
create mode 100644 CPF.Razor/Controls/generated/TabControl.generated.cs
create mode 100644 CPF.Razor/Controls/generated/TextBlock.generated.cs
create mode 100644 CPF.Razor/Controls/generated/TextBox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Thumb.generated.cs
create mode 100644 CPF.Razor/Controls/generated/TimePicker.generated.cs
create mode 100644 CPF.Razor/Controls/generated/ToggleButton.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Track.generated.cs
create mode 100644 CPF.Razor/Controls/generated/TreeView.generated.cs
create mode 100644 CPF.Razor/Controls/generated/UIElement.generated.cs
create mode 100644 CPF.Razor/Controls/generated/Viewbox.generated.cs
create mode 100644 CPF.Razor/Controls/generated/WrapPanel.generated.cs
diff --git a/CPF.Razor/Controls/Element.cs b/CPF.Razor/Controls/Element.cs
index c5ee696..5682382 100644
--- a/CPF.Razor/Controls/Element.cs
+++ b/CPF.Razor/Controls/Element.cs
@@ -7,7 +7,7 @@ using System.Text;
namespace CPF.Razor.Controls
{
- public abstract class Element : NativeControlComponentBase where T : UIElement, new()
+ public abstract partial class Element : NativeControlComponentBase where T : UIElement, new()
{
protected override void RenderAttributes(AttributesBuilder builder)
{
@@ -94,15 +94,15 @@ namespace CPF.Razor.Controls
}
- //只要属性和事件自动生成就行
- [Parameter] public string Name { get; set; }
- [Parameter] public FloatField? MarginLeft { get; set; }
- [Parameter] public FloatField? MarginTop { get; set; }
- [Parameter] public FloatField? MarginBottom { get; set; }
- [Parameter] public FloatField? MarginRight { get; set; }
- [Parameter] public CPF.FloatField? Width { get; set; }
- [Parameter] public CPF.FloatField? Height { get; set; }
- [Parameter] public EventCallback MouseDown { get; set; }
+ ////只要属性和事件自动生成就行
+ //[Parameter] public string Name { get; set; }
+ //[Parameter] public FloatField? MarginLeft { get; set; }
+ //[Parameter] public FloatField? MarginTop { get; set; }
+ //[Parameter] public FloatField? MarginBottom { get; set; }
+ //[Parameter] public FloatField? MarginRight { get; set; }
+ //[Parameter] public CPF.FloatField? Width { get; set; }
+ //[Parameter] public CPF.FloatField? Height { get; set; }
+ //[Parameter] public EventCallback MouseDown { get; set; }
}
}
diff --git a/CPF.Razor/Controls/Panel.cs b/CPF.Razor/Controls/Panel.cs
index e989a47..62d6351 100644
--- a/CPF.Razor/Controls/Panel.cs
+++ b/CPF.Razor/Controls/Panel.cs
@@ -6,12 +6,9 @@ using System.Text;
namespace CPF.Razor.Controls
{
- ///
- /// 测试
- ///
public partial class Panel : Element
{
- [Parameter] public string Background { get; set; }
+ //[Parameter] public string Background { get; set; }
#pragma warning disable CA1721 // Property names should not match get methods
[Parameter] public RenderFragment ChildContent { get; set; }
diff --git a/CPF.Razor/Controls/StackPanel.cs b/CPF.Razor/Controls/StackPanel.cs
new file mode 100644
index 0000000..beec2e1
--- /dev/null
+++ b/CPF.Razor/Controls/StackPanel.cs
@@ -0,0 +1,15 @@
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CPF.Razor.Controls
+{
+ public partial class StackPanel
+ {
+#pragma warning disable CA1721 // Property names should not match get methods
+ [Parameter] public RenderFragment ChildContent { get; set; }
+#pragma warning restore CA1721 // Property names should not match get methods
+ protected override RenderFragment GetChildContent() => ChildContent;
+ }
+}
diff --git a/CPF.Razor/Controls/generated/Border.generated.cs b/CPF.Razor/Controls/generated/Border.generated.cs
new file mode 100644
index 0000000..fe9c213
--- /dev/null
+++ b/CPF.Razor/Controls/generated/Border.generated.cs
@@ -0,0 +1,60 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 在另一个元素四周绘制边框和背景
+ ///
+ public partial class Border : Element
+ {
+
+ [Parameter] public string Background { get; set; }
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ [Parameter] public UIElement Child { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ ///
+ /// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值
+ ///
+ [Parameter] public Thickness? Padding { get; set; }
+ ///
+ /// 模糊宽度
+ ///
+ [Parameter] public byte? ShadowBlur { get; set; }
+ ///
+ /// 阴影颜色
+ ///
+ [Parameter] public string ShadowColor { get; set; }
+ ///
+ /// 阴影水平偏移
+ ///
+ [Parameter] public sbyte? ShadowHorizontal { get; set; }
+ ///
+ /// 阴影垂直偏移
+ ///
+ [Parameter] public sbyte? ShadowVertical { get; set; }
+
+ }
+}
diff --git a/CPF.Razor/Controls/generated/Button.generated.cs b/CPF.Razor/Controls/generated/Button.generated.cs
new file mode 100644
index 0000000..49ab720
--- /dev/null
+++ b/CPF.Razor/Controls/generated/Button.generated.cs
@@ -0,0 +1,77 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 表示 Windows 按钮控件,该按钮对 Click 事件做出反应。
+ ///
+ public partial class Button : Element
+ {
+
+ ///
+ /// 背景填充
+ ///
+ [Parameter] public string Background { get; set; }
+ ///
+ /// 边框线条填充
+ ///
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型,BorderStroke和BorderThickness
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ [Parameter] public ClickMode? ClickMode { get; set; }
+ ///
+ /// 内容可以是字符串,UI元素等等
+ ///
+ [Parameter] public object Content { get; set; }
+ [Parameter] public string ContentStringFormat { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ ///
+ /// 字体名
+ ///
+ [Parameter] public string FontFamily { get; set; }
+ ///
+ /// 字体尺寸,点
+ ///
+ [Parameter] public float? FontSize { get; set; }
+ ///
+ /// 字体样式
+ ///
+ [Parameter] public FontStyles? FontStyle { get; set; }
+ ///
+ /// 控件文字的填充
+ ///
+ [Parameter] public string Foreground { get; set; }
+ ///
+ /// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式:all或者left,top,right,bottom
+ ///
+ [Parameter] public Thickness? Padding { get; set; }
+ ///
+ /// 表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough/none [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]
+ ///
+ [Parameter] public TextDecoration? TextDecoration { get; set; }
+ [Parameter] public EventCallback Click { get; set; }
+ [Parameter] public EventCallback Initialized { get; set; }
+
+ }
+}
diff --git a/CPF.Razor/Controls/generated/Calendar.generated.cs b/CPF.Razor/Controls/generated/Calendar.generated.cs
new file mode 100644
index 0000000..9df7b41
--- /dev/null
+++ b/CPF.Razor/Controls/generated/Calendar.generated.cs
@@ -0,0 +1,75 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+using System;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 代表一个控件,此控件允许用户使用可视的日历显示来选择日期
+ ///
+ public partial class Calendar : Element
+ {
+
+ ///
+ /// 背景填充
+ ///
+ [Parameter] public string Background { get; set; }
+ ///
+ /// 边框线条填充
+ ///
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型,BorderStroke和BorderThickness
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ [Parameter] public DateTime? DisplayDate { get; set; }
+ [Parameter] public CalendarMode? DisplayMode { get; set; }
+ [Parameter] public DayOfWeek? FirstDayOfWeek { get; set; }
+ ///
+ /// 字体名
+ ///
+ [Parameter] public string FontFamily { get; set; }
+ ///
+ /// 字体尺寸,点
+ ///
+ [Parameter] public float? FontSize { get; set; }
+ ///
+ /// 字体样式
+ ///
+ [Parameter] public FontStyles? FontStyle { get; set; }
+ ///
+ /// 控件文字的填充
+ ///
+ [Parameter] public string Foreground { get; set; }
+ ///
+ /// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式:all或者left,top,right,bottom
+ ///
+ [Parameter] public Thickness? Padding { get; set; }
+ [Parameter] public Nullable SelectedDate { get; set; }
+ ///
+ /// 表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough/none [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]
+ ///
+ [Parameter] public TextDecoration? TextDecoration { get; set; }
+ [Parameter] public EventCallback Initialized { get; set; }
+
+ }
+}
diff --git a/CPF.Razor/Controls/generated/Chart.generated.cs b/CPF.Razor/Controls/generated/Chart.generated.cs
new file mode 100644
index 0000000..700db58
--- /dev/null
+++ b/CPF.Razor/Controls/generated/Chart.generated.cs
@@ -0,0 +1,139 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Charts;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Collections.Generic;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 提供折线图,曲线图,柱状图
+ ///
+ public partial class Chart : Element
+ {
+
+ ///
+ /// 背景填充
+ ///
+ [Parameter] public string Background { get; set; }
+ ///
+ /// 边框线条填充
+ ///
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型,BorderStroke和BorderThickness
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ ///
+ /// 是否可以缩放滚动
+ ///
+ [Parameter] public bool? CanScroll { get; set; }
+ ///
+ /// 图表区域填充
+ ///
+ [Parameter] public string ChartFill { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ [Parameter] public IList Data { get; set; }
+ ///
+ /// 字体名
+ ///
+ [Parameter] public string FontFamily { get; set; }
+ ///
+ /// 字体尺寸,点
+ ///
+ [Parameter] public float? FontSize { get; set; }
+ ///
+ /// 字体样式
+ ///
+ [Parameter] public FontStyles? FontStyle { get; set; }
+ ///
+ /// 控件文字的填充
+ ///
+ [Parameter] public string Foreground { get; set; }
+ ///
+ /// 网格填充
+ ///
+ [Parameter] public string GridFill { get; set; }
+ ///
+ /// 网格显示模式
+ ///
+ [Parameter] public GridShowMode? GridShowMode { get; set; }
+ ///
+ /// 水平缩放值 大于等于1
+ ///
+ [Parameter] public float? HorizontalScaling { get; set; }
+ ///
+ /// 鼠标移入选中的线条填充
+ ///
+ [Parameter] public string HoverSelectLineFill { get; set; }
+ ///
+ /// 鼠标移入选中的坐标轴提示背景填充
+ ///
+ [Parameter] public string HoverSelectTipBackFill { get; set; }
+ ///
+ /// 鼠标移入选中的坐标轴提示文字填充
+ ///
+ [Parameter] public string HoverSelectTipFill { get; set; }
+ ///
+ /// Y轴最大值
+ ///
+ [Parameter] public Nullable MaxValue { get; set; }
+ ///
+ /// Y轴最小值
+ ///
+ [Parameter] public Nullable MinValue { get; set; }
+ ///
+ /// 鼠标移入图表的时候显示信息
+ ///
+ [Parameter] public bool? MouseHoverShowTip { get; set; }
+ ///
+ /// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式:all或者left,top,right,bottom
+ ///
+ [Parameter] public Thickness? Padding { get; set; }
+ ///
+ /// 显示滚动缩放值的线条填充
+ ///
+ [Parameter] public string ScrollLineFill { get; set; }
+ [Parameter] public float? ScrollValue { get; set; }
+ ///
+ /// 表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough/none [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]
+ ///
+ [Parameter] public TextDecoration? TextDecoration { get; set; }
+ ///
+ /// X轴文字
+ ///
+ [Parameter] public string XAxis { get; set; }
+ ///
+ /// X轴颜色
+ ///
+ [Parameter] public string XAxisFill { get; set; }
+ ///
+ /// Y轴颜色
+ ///
+ [Parameter] public string YAxisFill { get; set; }
+ ///
+ /// Y轴刻度分割数量,大于等于1
+ ///
+ [Parameter] public uint? YAxisScaleCount { get; set; }
+ [Parameter] public EventCallback Initialized { get; set; }
+
+ }
+}
diff --git a/CPF.Razor/Controls/generated/CheckBox.generated.cs b/CPF.Razor/Controls/generated/CheckBox.generated.cs
new file mode 100644
index 0000000..4db9ff4
--- /dev/null
+++ b/CPF.Razor/Controls/generated/CheckBox.generated.cs
@@ -0,0 +1,83 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+using System;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 表示用户可以选择和清除的控件。
+ ///
+ public partial class CheckBox : Element
+ {
+
+ ///
+ /// 背景填充
+ ///
+ [Parameter] public string Background { get; set; }
+ ///
+ /// 边框线条填充
+ ///
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型,BorderStroke和BorderThickness
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ [Parameter] public ClickMode? ClickMode { get; set; }
+ ///
+ /// 内容可以是字符串,UI元素等等
+ ///
+ [Parameter] public object Content { get; set; }
+ [Parameter] public string ContentStringFormat { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ ///
+ /// 字体名
+ ///
+ [Parameter] public string FontFamily { get; set; }
+ ///
+ /// 字体尺寸,点
+ ///
+ [Parameter] public float? FontSize { get; set; }
+ ///
+ /// 字体样式
+ ///
+ [Parameter] public FontStyles? FontStyle { get; set; }
+ ///
+ /// 控件文字的填充
+ ///
+ [Parameter] public string Foreground { get; set; }
+ [Parameter] public Nullable IsChecked { get; set; }
+ [Parameter] public bool? IsThreeState { get; set; }
+ ///
+ /// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式:all或者left,top,right,bottom
+ ///
+ [Parameter] public Thickness? Padding { get; set; }
+ ///
+ /// 表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough/none [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]
+ ///
+ [Parameter] public TextDecoration? TextDecoration { get; set; }
+ [Parameter] public EventCallback Checked { get; set; }
+ [Parameter] public EventCallback Indeterminate { get; set; }
+ [Parameter] public EventCallback Unchecked { get; set; }
+ [Parameter] public EventCallback Click { get; set; }
+ [Parameter] public EventCallback Initialized { get; set; }
+
+ }
+}
diff --git a/CPF.Razor/Controls/generated/CodeTextBox.generated.cs b/CPF.Razor/Controls/generated/CodeTextBox.generated.cs
new file mode 100644
index 0000000..2b15c49
--- /dev/null
+++ b/CPF.Razor/Controls/generated/CodeTextBox.generated.cs
@@ -0,0 +1,116 @@
+// CPF自动生成.
+
+using CPF;
+using CPF.Controls;
+using CPF.Drawing;
+using CPF.Input;
+using CPF.Razor;
+using CPF.Shapes;
+using Microsoft.AspNetCore.Components;
+
+namespace CPF.Razor.Controls
+{
+ ///
+ /// 支持可视化设计的控件基类
+ ///
+ public partial class CodeTextBox : Element
+ {
+
+ ///
+ /// 如果按 Tab 键会在当前光标位置插入一个制表符,则为 true;如果按 Tab 键会将焦点移动到标记为制表位的下一个控件且不插入制表符,则为 false
+ ///
+ [Parameter] public bool? AcceptsTab { get; set; }
+ ///
+ /// 背景填充
+ ///
+ [Parameter] public string Background { get; set; }
+ ///
+ /// 边框线条填充
+ ///
+ [Parameter] public string BorderFill { get; set; }
+ ///
+ /// 获取或设置线条类型
+ ///
+ [Parameter] public Stroke? BorderStroke { get; set; }
+ ///
+ /// 四周边框粗细
+ ///
+ [Parameter] public Thickness? BorderThickness { get; set; }
+ ///
+ /// 边框类型,BorderStroke和BorderThickness
+ ///
+ [Parameter] public BorderType? BorderType { get; set; }
+ ///
+ /// 获取或设置用于绘制文本框的插入符号的画笔
+ ///
+ [Parameter] public string CaretFill { get; set; }
+ ///
+ /// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
+ ///
+ [Parameter] public CornerRadius? CornerRadius { get; set; }
+ ///
+ /// 字体名
+ ///
+ [Parameter] public string FontFamily { get; set; }
+ ///
+ /// 字体尺寸,点
+ ///
+ [Parameter] public float? FontSize { get; set; }
+ ///
+ /// 字体样式
+ ///
+ [Parameter] public FontStyles? FontStyle { get; set; }
+ ///
+ /// 控件文字的填充
+ ///