From 0d3b9908febcb0596912a6a42a8319aed77a25bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BE=E7=AB=B9?= Date: Thu, 1 Feb 2024 15:06:37 +0000 Subject: [PATCH] update README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 艾竹 --- README.md | 480 ------------------------------------------------------ 1 file changed, 480 deletions(-) diff --git a/README.md b/README.md index 99a2e89..f81f4f5 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,6 @@ ## 2023年12月21日更新内容(自动布局从NodeNetwork迁移) ![输入图片说明](Images/autolayout.gif) -## 2023年7月2日更新内容(做一个Block编程画板) -先上一张效果动图,本次更新主要仿照Scratch,目前仅完成拖拽部分,逻辑部分后续完善。 -![输入图片说明](Images/block.gif) - -### 本次扩展主要内容: -### 1.Block模块,入口在文件新建下。 -![输入图片说明](Images/block2.png) -### 2.简易Block的使用: -![输入图片说明](Images/block1.png) -### 3.仿Scratch的Block的使用(完成图鉴,准备编写逻辑): -![输入图片说明](Images/block3.png) - ## 2023年5月17日更新内容(做一个画笔画板) ![输入图片说明](Images/66.gif) @@ -46,474 +34,6 @@ ![输入图片说明](Images/drawingdemo2.png) -## 2023年5月1号更新内容(做一个可编程画板): - -![输入图片说明](Images/55.gif) - -### 1.简单使用,自定义一个text模块的代码如下: -``` -Code = @"using System; -namespace AIStudio.Wpf.CSharpScript -{ - public class Writer - { - public string StringValue{ get; set;} = ""Welcome to AIStudio.Wpf.Diagram""; - - public string Execute() - { - return StringValue; - } - } -}"; -``` -是不是很简单。 - -### 2.本次扩展的主要内容 - -【1】.可编程模块,使用C#语言。 - -【2】.控制台打印控件,可以打印程序中的Console.WriteLine数据 - -【3】.为了便于大家使用,写了一个Box工厂分配Box的数据流向效果图。 - -### 3.可编程模块的实现原理 -使用Microsoft.CodeAnalysis.CSharp.Scripting对代码进行编译,生成Assembly,然后对Assembly反射获得对象,对象内部固定有一个Execute方法,每次扫描的时候执行即可。 -1.编译使用的Using,必须添加引用集,为了省事,把整个程序的Reference都放入进行编译,获得引用的核心代码如下: - -``` -var references = AppDomain.CurrentDomain.GetAssemblies().Where(p => !p.IsDynamic && !string.IsNullOrEmpty(p.Location)).Select(x => MetadataReference.CreateFromFile(x.Location)).ToList(); -//Costura.Fody压缩后,无Location,读取资源文件中的reference -foreach (var assemblyEmbedded in AppDomain.CurrentDomain.GetAssemblies().Where(p => !p.IsDynamic && string.IsNullOrEmpty(p.Location))) -{ - using (var stream = Assembly.GetEntryAssembly().GetManifestResourceStream($"costura.{assemblyEmbedded.GetName().Name.ToLowerInvariant()}.dll.compressed")) - { - if (stream != null) - { - using (var compressStream = new DeflateStream(stream, CompressionMode.Decompress)) - { - var memStream = new MemoryStream(); - CopyTo(compressStream, memStream); - memStream.Position = 0; - references.Add(MetadataReference.CreateFromStream(memStream)); - } - - } - } -} -``` -2.动态编译的代码的核心代码如下: - -``` -public static Assembly GenerateAssemblyFromCode(string code, out string message) -{ - Assembly assembly = null; - message = ""; - // 丛代码中转换表达式树 - SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code); - // 随机程序集名称 - string assemblyName = Path.GetRandomFileName(); - // 引用 - - // 创建编译对象 - CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, References, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - - using (var ms = new MemoryStream()) - { - // 将编译好的IL代码放入内存流 - EmitResult result = compilation.Emit(ms); - - // 编译失败,提示 - if (!result.Success) - { - IEnumerable failures = result.Diagnostics.Where(diagnostic => - diagnostic.IsWarningAsError || - diagnostic.Severity == DiagnosticSeverity.Error).ToList(); - foreach (Diagnostic diagnostic in failures) - { - message += $"{diagnostic.Id}: {diagnostic.GetMessage()}"; - Console.WriteLine(message); - } - } - else - { - // 编译成功,从内存中加载编译好的程序集 - ms.Seek(0, SeekOrigin.Begin); - assembly = Assembly.Load(ms.ToArray()); - } - } - return assembly; -} -``` -3.获得编译后的程序集,以及执行。 -``` -// 反射获取程序集中 的类 -Type type = assembly.GetTypes().FirstOrDefault(p => p.FullName.StartsWith("AIStudio.Wpf")); //assembly.GetType("AIStudio.Wpf.CSharpScript.Write"); - -// 创建该类的实例 -object obj = Activator.CreateInstance(type); - -// 通过反射方式调用类中的方法。 -var result = type.InvokeMember("Execute", - BindingFlags.Default | BindingFlags.InvokeMethod, - null, - obj, - new object[] { }); -``` -### 4.代码编辑模块的实现 -选择AvalonEdit控件,另外为了使用VS2019_Dark的黑色皮肤,引用官方Demo中的HL和TextEditlib实现自定义换肤。 - -![输入图片说明](Images/editor.png) - -官方Demo的换肤写的超级复杂,看不懂,但是我们只要理解换肤的核心部分就是动态资源字典,因此我简化下,改进后的核心换肤代码如下: - -``` -public class TextEditorThemeHelper -{ - static Dictionary ThemeDictionary = new Dictionary(); - - public static List Themes = new List() { "Dark", "Light", "TrueBlue", "VS2019_Dark" }; - public static string CurrentTheme { get; set; } - - static TextEditorThemeHelper() - { - var resource = new ResourceDictionary { Source = new Uri("/TextEditLib;component/Themes/LightBrushs.xaml", UriKind.RelativeOrAbsolute) }; - ThemeDictionary.Add("Light", resource); - - resource = new ResourceDictionary { Source = new Uri("/TextEditLib;component/Themes/DarkBrushs.xaml", UriKind.RelativeOrAbsolute) }; - ThemeDictionary.Add("Dark", resource); - - Application.Current.Resources.MergedDictionaries.Add(resource); - } - - /// - /// 设置主题 - /// - /// - public static void SetCurrentTheme(string theme) - { - OnAppThemeChanged(theme);//切换到VS2019_Dark - CurrentTheme = theme; - } - - /// - /// Invoke this method to apply a change of theme to the content of the document - /// (eg: Adjust the highlighting colors when changing from "Dark" to "Light" - /// WITH current text document loaded.) - /// - internal static void OnAppThemeChanged(string theme) - { - ThemedHighlightingManager.Instance.SetCurrentTheme(theme); - - if (ThemeDictionary.ContainsKey(theme)) - { - foreach (var key in ThemeDictionary[theme].Keys) - { - ApplyToDynamicResource(key, ThemeDictionary[theme][key]); - } - } - // Does this highlighting definition have an associated highlighting theme? - else if (ThemedHighlightingManager.Instance.CurrentTheme.HlTheme != null) - { - // A highlighting theme with GlobalStyles? - // Apply these styles to the resource keys of the editor - foreach (var item in ThemedHighlightingManager.Instance.CurrentTheme.HlTheme.GlobalStyles) - { - switch (item.TypeName) - { - case "DefaultStyle": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorBackground, item.backgroundcolor); - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorForeground, item.foregroundcolor); - break; - - case "CurrentLineBackground": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorCurrentLineBackgroundBrushKey, item.backgroundcolor); - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorCurrentLineBorderBrushKey, item.bordercolor); - break; - - case "LineNumbersForeground": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorLineNumbersForeground, item.foregroundcolor); - break; - - case "Selection": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorSelectionBrush, item.backgroundcolor); - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorSelectionBorder, item.bordercolor); - break; - - case "Hyperlink": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorLinkTextBackgroundBrush, item.backgroundcolor); - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorLinkTextForegroundBrush, item.foregroundcolor); - break; - - case "NonPrintableCharacter": - ApplyToDynamicResource(TextEditLib.Themes.ResourceKeys.EditorNonPrintableCharacterBrush, item.foregroundcolor); - break; - - default: - throw new System.ArgumentOutOfRangeException("GlobalStyle named '{0}' is not supported.", item.TypeName); - } - } - } - - } - - /// - /// Re-define an existing and backup the originial color - /// as it was before the application of the custom coloring. - /// - /// - /// - private static void ApplyToDynamicResource(ComponentResourceKey key, Color? newColor) - { - if (Application.Current.Resources[key] == null || newColor == null) - return; - - // Re-coloring works with SolidColorBrushs linked as DynamicResource - if (Application.Current.Resources[key] is SolidColorBrush) - { - //backupDynResources.Add(resourceName); - - var newColorBrush = new SolidColorBrush((Color)newColor); - newColorBrush.Freeze(); - - Application.Current.Resources[key] = newColorBrush; - } - } - - private static void ApplyToDynamicResource(object key, object newValue) - { - if (Application.Current.Resources[key] == null || newValue == null) - return; - - Application.Current.Resources[key] = newValue; - } -} -``` -使用方法: -TextEditorThemeHelper.SetCurrentTheme("VS2019_Dark"); - -或者 -TextEditorThemeHelper.SetCurrentTheme("TrueBlue"); - -或者 -TextEditorThemeHelper.SetCurrentTheme("Dark"); - -或者 -TextEditorThemeHelper.SetCurrentTheme("Light"); - -是不是超级简单。 - -### 5.代码编辑模块的编译与测试。 - -![输入图片说明](Images/build.png) - -![输入图片说明](Images/test.png) - -### 6.WPF打印控制台数据 - -``` -控制台打印方法支持切换运行输出方法Console.SetOut,核心代码如下: -public class ConsoleWriter : TextWriter -{ - private readonly Action _Write; - private readonly Action _WriteLine; - private readonly Action _WriteCallerInfo; - - public ConsoleWriter() - { - - } - - /// - /// Console 输出重定向 - /// - /// 日志方法委托(针对于 Write) - /// 日志方法委托(针对于 WriteLine) - public ConsoleWriter(Action write, Action writeLine, Action writeCallerInfo) - { - _Write = write; - _WriteLine = writeLine?? write; - _WriteCallerInfo = writeCallerInfo; - } - - /// - /// Console 输出重定向 - /// - /// 日志方法委托(针对于 Write) - /// 日志方法委托(针对于 WriteLine) - public ConsoleWriter(Action write, Action writeLine) - { - _Write = write; - _WriteLine = writeLine; - } - - /// - /// Console 输出重定向 - /// - /// 日志方法委托 - public ConsoleWriter(Action write) - { - _Write = write; - _WriteLine = write; - } - - /// - /// Console 输出重定向(带调用方信息) - /// - /// 日志方法委托(后三个参数为 CallerFilePath、CallerMemberName、CallerLineNumber) - public ConsoleWriter(Action write) - { - _WriteCallerInfo = write; - } - - /// - /// 使用 UTF-16 避免不必要的编码转换 - /// - public override Encoding Encoding => Encoding.Unicode; - - /// - /// 最低限度需要重写的方法 - /// - /// 消息 - public override void Write(string value) - { - if (_WriteCallerInfo != null) - { - WriteWithCallerInfo(value); - return; - } - - _Write(value); - } - - /// - /// 为提高效率直接处理一行的输出 - /// - /// 消息 - public override void WriteLine(string value) - { - if (_WriteCallerInfo != null) - { - WriteWithCallerInfo(value); - return; - } - - _WriteLine(value); - } - - /// - /// 带调用方信息进行写消息 - /// - /// 消息 - private void WriteWithCallerInfo(string value) - { - //3、System.Console.WriteLine -> 2、System.IO.TextWriter + SyncTextWriter.WriteLine -> 1、DotNet.Utilities.ConsoleHelper.ConsoleWriter.WriteLine -> 0、DotNet.Utilities.ConsoleHelper.ConsoleWriter.WriteWithCallerInfo - var callInfo = ClassHelper.GetMethodInfo(4); - _WriteCallerInfo(value, callInfo?.FileName, callInfo?.MethodName, callInfo?.LineNumber ?? 0); - } - - public override void Close() - { - var standardOutput = new StreamWriter(Console.OpenStandardOutput()); - standardOutput.AutoFlush = true; - Console.SetOut(standardOutput); - base.Close(); - } -} -``` -使用: - -ConsoleWriter ConsoleWriter = new ConsoleWriter(_write, _writeLine); - -Console.SetOut(ConsoleWriter); - -### 7.动态编译模块的输入输出自动生成。 -1.输入输出模块:public string Value{ get; set;} -2.输入模块:public string Value{private get; set;} -3.输出模块:public string Value{get;private set;} -4.与外部交互模块:private string Value{ get; set;} ,必须同名同属性。 -核心代码如下: - -``` -public static Dictionary> GetPropertyInfo(Type type) -{ - Dictionary> puts = new Dictionary>() - { - {"Input", new List() }, - {"Output", new List() }, - {"Input_Output", new List() }, - {"Inner", new List() } - }; - - try - { - foreach (System.Reflection.PropertyInfo info in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - if (info.CanRead && info.CanWrite) - { - if (info.SetMethod.IsPublic && info.GetMethod.IsPublic) - { - puts["Input_Output"].Add(info); - } - else if (info.SetMethod.IsPublic) - { - puts["Input"].Add(info); - } - else if (info.GetMethod.IsPublic) - { - puts["Output"].Add(info); - } - } - else if (info.CanRead) - { - if (info.GetMethod.IsPublic) - { - puts["Output"].Add(info); - } - } - } - - foreach (System.Reflection.PropertyInfo info in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)) - { - if (info.CanRead) - { - puts["Inner"].Add(info); - } - } - } - catch (Exception ex) - { - - } - - return puts; -} -``` - -### 8.最后介绍一下Demo的实现。 -1#.Int整数模块,界面定义一个TextBox绑定Int模块的输入管脚。 -2#.Box产生模块,如果内部数组为空,那么按照输入管脚的数量初始化一个容量为输入整数数量的数组(随机颜色与形状),然后把数据放到输出管脚,当数据被取走后,下一个数据再次放到输出管脚。 -3#.Bool模块,为false的时候按照颜色进行分配,为true的时候按照形状进行分配。 -4#.Box分配模块,当输入管脚为空的时候,2#模块的输出可以移动到4#的输入管脚,移动时间为1s,移动完成后,清除2#模块的输出。同时把数据按照颜色或者形状分配到输出,同时把输入管脚清除。 -按照颜色分配时: -(1.如果颜色为红色,那么输出到1号 -(2.如果颜色为橙色,那么输出到2号 -(3.如果颜色为黄色,那么输出到3号 -(4.如果颜色为绿色,那么输出到4号 -(5.如果颜色为青色,那么输出到5号 -(6.如果颜色为蓝色,那么输出到6号 -(7.如果颜色为紫色,那么输出到7号 -按照形状分配时: -(1.如果形状为圆形,那么输出到1号 -(2.如果形状为三角形,那么输出到2号 -(3.如果形状为方形,那么输出到3号 -(4.如果形状为菱形,那么输出到4号 -(5.如果形状为梯形,那么输出到5号 -(6.如果形状为五角星,那么输出到6号 -(7.如果形状为六边形,那么输出到7号 -6#.有两个红色|圆形收集器(7#,8#),按两个容器中的数量比较反馈,均匀分配到这两个收集器中。 -9#,10#,11#,12#,13#,14#按照管脚取走数据即可。 - - ## 2023年4月5号更新内容(本次更新主要仿照百度脑图): ### 1.思维导图、目录组织图、鱼骨头图、逻辑结构图、组织结构图,入口在文件新建下。