基于WPF制作一個(gè)可編程畫板
先上一張效果動(dòng)圖
同樣老規(guī)矩,先上源碼地址:https://gitee.com/akwkevin/aistudio.-wpf.-diagram
簡(jiǎn)單使用,自定義一個(gè)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; ????????} ????} }";
是不是很簡(jiǎn)單。
本次擴(kuò)展的主要內(nèi)容
1.可編程模塊,使用C#語言。
2.控制臺(tái)打印控件,可以打印程序中的Console.WriteLine數(shù)據(jù)
3.為了便于大家使用,寫了一個(gè)Box工廠分配Box的數(shù)據(jù)流向效果圖。
可編程模塊的實(shí)現(xiàn)原理
使用Microsoft.CodeAnalysis.CSharp.Scripting對(duì)代碼進(jìn)行編譯,生成Assembly,然后對(duì)Assembly反射獲得對(duì)象,對(duì)象內(nèi)部固定有一個(gè)Execute方法,每次掃描的時(shí)候執(zhí)行即可。
1.編譯使用的Using,必須添加引用集,為了省事,把整個(gè)程序的Reference都放入進(jìn)行編譯,獲得引用的核心代碼如下:
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.動(dòng)態(tài)編譯的代碼的核心代碼如下:
public?static?Assembly?GenerateAssemblyFromCode(string?code,?out?string?message) { ????Assembly?assembly?=?null; ????message?=?""; ????//?叢代碼中轉(zhuǎn)換表達(dá)式樹 ????SyntaxTree?syntaxTree?=?CSharpSyntaxTree.ParseText(code); ????//?隨機(jī)程序集名稱 ????string?assemblyName?=?Path.GetRandomFileName(); ????//?引用 ????//?創(chuàng)建編譯對(duì)象 ????CSharpCompilation?compilation?=?CSharpCompilation.Create(assemblyName,?new[]?{?syntaxTree?},?References,?new?CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); ????using?(var?ms?=?new?MemoryStream()) ????{ ????????//?將編譯好的IL代碼放入內(nèi)存流 ????????EmitResult?result?=?compilation.Emit(ms); ????????//?編譯失敗,提示 ????????if?(!result.Success) ????????{ ????????????IEnumerable<Diagnostic>?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 ????????{ ????????????//?編譯成功,從內(nèi)存中加載編譯好的程序集 ????????????ms.Seek(0,?SeekOrigin.Begin); ????????????assembly?=?Assembly.Load(ms.ToArray()); ????????} ????} ????return?assembly; }
3.獲得編譯后的程序集,以及執(zhí)行。
//?反射獲取程序集中?的類 Type?type?=?assembly.GetTypes().FirstOrDefault(p?=>?p.FullName.StartsWith("AIStudio.Wpf"));???//assembly.GetType("AIStudio.Wpf.CSharpScript.Write"); //?創(chuàng)建該類的實(shí)例 object?obj?=?Activator.CreateInstance(type); //?通過反射方式調(diào)用類中的方法。 var?result?=?type.InvokeMember("Execute", ????BindingFlags.Default?|?BindingFlags.InvokeMethod, ????null, ????obj, ????new?object[]?{?});
代碼編輯模塊的實(shí)現(xiàn)
選擇AvalonEdit控件,另外為了使用VS2019_Dark的黑色皮膚,引用官方Demo中的HL和TextEditlib實(shí)現(xiàn)自定義換膚。
官方Demo的換膚寫的超級(jí)復(fù)雜,看不懂,但是我們只要理解換膚的核心部分就是動(dòng)態(tài)資源字典,因此我簡(jiǎn)化下,改進(jìn)后的核心換膚代碼如下:
public?class?TextEditorThemeHelper { ????static?Dictionary<string,?ResourceDictionary>?ThemeDictionary?=?new?Dictionary<string,?ResourceDictionary>(); ????public?static?List<string>?Themes?=?new?List<string>()?{?"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); ????} ????///?<summary> ????///?設(shè)置主題 ????///?</summary> ????///?<param?name="theme"></param> ????public?static?void?SetCurrentTheme(string?theme) ????{ ????????OnAppThemeChanged(theme);//切換到VS2019_Dark ????????CurrentTheme?=?theme; ????} ????///?<summary> ????///?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.) ????///?</summary> ????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); ????????????????} ????????????} ????????} ????} ????///?<summary> ????///?Re-define?an?existing?<seealso?cref="SolidColorBrush"/>?and?backup?the?originial?color ????///?as?it?was?before?the?application?of?the?custom?coloring. ????///?</summary> ????///?<param?name="key"></param> ????///?<param?name="newColor"></param> ????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");
是不是超級(jí)簡(jiǎn)單。
代碼編輯模塊的編譯與測(cè)試
WPF打印控制臺(tái)數(shù)據(jù)
///控制臺(tái)打印方法支持切換運(yùn)行輸出方法Console.SetOut,核心代碼如下: public?class?ConsoleWriter?:?TextWriter { ????private?readonly?Action<string>?_Write; ????private?readonly?Action<string>?_WriteLine; ????private?readonly?Action<string,?string,?string,?int>?_WriteCallerInfo; ????public?ConsoleWriter() ????{ ????} ????///?<summary> ????///?Console?輸出重定向 ????///?</summary> ????///?<param?name="write">日志方法委托(針對(duì)于?Write)</param> ????///?<param?name="writeLine">日志方法委托(針對(duì)于?WriteLine)</param> ????public?ConsoleWriter(Action<string>?write,?Action<string>?writeLine,?Action<string,?string,?string,?int>?writeCallerInfo) ????{ ????????_Write?=?write; ????????_WriteLine?=?writeLine???write; ????????_WriteCallerInfo?=?writeCallerInfo; ????} ????///?<summary> ????///?Console?輸出重定向 ????///?</summary> ????///?<param?name="write">日志方法委托(針對(duì)于?Write)</param> ????///?<param?name="writeLine">日志方法委托(針對(duì)于?WriteLine)</param> ????public?ConsoleWriter(Action<string>?write,?Action<string>?writeLine) ????{ ????????_Write?=?write; ????????_WriteLine?=?writeLine; ????} ????///?<summary> ????///?Console?輸出重定向 ????///?</summary> ????///?<param?name="write">日志方法委托</param> ????public?ConsoleWriter(Action<string>?write) ????{ ????????_Write?=?write; ????????_WriteLine?=?write; ????} ????///?<summary> ????///?Console?輸出重定向(帶調(diào)用方信息) ????///?</summary> ????///?<param?name="write">日志方法委托(后三個(gè)參數(shù)為?CallerFilePath、CallerMemberName、CallerLineNumber)</param> ????public?ConsoleWriter(Action<string,?string,?string,?int>?write) ????{ ????????_WriteCallerInfo?=?write; ????} ????///?<summary> ????///?使用?UTF-16?避免不必要的編碼轉(zhuǎn)換 ????///?</summary> ????public?override?Encoding?Encoding?=>?Encoding.Unicode; ????///?<summary> ????///?最低限度需要重寫的方法 ????///?</summary> ????///?<param?name="value">消息</param> ????public?override?void?Write(string?value) ????{ ????????if?(_WriteCallerInfo?!=?null) ????????{ ????????????WriteWithCallerInfo(value); ????????????return; ????????} ????????_Write(value); ????} ????///?<summary> ????///?為提高效率直接處理一行的輸出 ????///?</summary> ????///?<param?name="value">消息</param> ????public?override?void?WriteLine(string?value) ????{ ????????if?(_WriteCallerInfo?!=?null) ????????{ ????????????WriteWithCallerInfo(value); ????????????return; ????????} ????????_WriteLine(value); ????} ????///?<summary> ????///?帶調(diào)用方信息進(jìn)行寫消息 ????///?</summary> ????///?<param?name="value">消息</param> ????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);
動(dòng)態(tài)編譯模塊的輸入輸出自動(dòng)生成
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<string,?List<PropertyInfo>>?GetPropertyInfo(Type?type) { ????Dictionary<string,?List<PropertyInfo>>?puts?=?new?Dictionary<string,?List<PropertyInfo>>() ????{ ????????{"Input",?new?List<PropertyInfo>()?}, ????????{"Output",?new?List<PropertyInfo>()?}, ????????{"Input_Output",?new?List<PropertyInfo>()?}, ????????{"Inner",?new?List<PropertyInfo>()?} ????}; ????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; }
最后介紹一下Demo的實(shí)現(xiàn)
1#.Int整數(shù)模塊,界面定義一個(gè)TextBox綁定Int模塊的輸入管腳。 2#.Box產(chǎn)生模塊,如果內(nèi)部數(shù)組為空,那么按照輸入管腳的數(shù)量初始化一個(gè)容量為輸入整數(shù)數(shù)量的數(shù)組(隨機(jī)顏色與形狀),然后把數(shù)據(jù)放到輸出管腳,當(dāng)數(shù)據(jù)被取走后,下一個(gè)數(shù)據(jù)再次放到輸出管腳。 3#.Bool模塊,為false的時(shí)候按照顏色進(jìn)行分配,為true的時(shí)候按照形狀進(jìn)行分配。4#.Box分配模塊,當(dāng)輸入管腳為空的時(shí)候,2#模塊的輸出可以移動(dòng)到4#的輸入管腳,移動(dòng)時(shí)間為1s,移動(dòng)完成后,清除2#模塊的輸出。同時(shí)把數(shù)據(jù)按照顏色或者形狀分配到輸出,同時(shí)把輸入管腳清除。 按照顏色分配時(shí): (1.如果顏色為紅色,那么輸出到1號(hào) (2.如果顏色為橙色,那么輸出到2號(hào) (3.如果顏色為黃色,那么輸出到3號(hào) (4.如果顏色為綠色,那么輸出到4號(hào) (5.如果顏色為青色,那么輸出到5號(hào) (6.如果顏色為藍(lán)色,那么輸出到6號(hào) (7.如果顏色為紫色,那么輸出到7號(hào) 按照形狀分配時(shí): (1.如果形狀為圓形,那么輸出到1號(hào) (2.如果形狀為三角形,那么輸出到2號(hào) (3.如果形狀為方形,那么輸出到3號(hào) (4.如果形狀為菱形,那么輸出到4號(hào) (5.如果形狀為梯形,那么輸出到5號(hào) (6.如果形狀為五角星,那么輸出到6號(hào) (7.如果形狀為六邊形,那么輸出到7號(hào) 6#.有兩個(gè)紅色|圓形收集器(7#,8#),按兩個(gè)容器中的數(shù)量比較反饋,均勻分配到這兩個(gè)收集器中。 9#,10#,11#,12#,13#,14#按照管腳取走數(shù)據(jù)即可。
以上就是基于WPF制作一個(gè)可編程畫板的詳細(xì)內(nèi)容,更多關(guān)于WPF可編程畫板的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)List.Sort()使用小計(jì)
在C#開發(fā)中,List是常見的一種集合類型,其提供了一個(gè) Sort() 方法來實(shí)現(xiàn)對(duì)集合的排序,本文主要介紹了C#實(shí)現(xiàn)List.Sort()使用小計(jì),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12C#/VB.NET實(shí)現(xiàn)將Html轉(zhuǎn)為Word的示例詳解
本文分享以C#程序代碼為例,實(shí)現(xiàn)將Html文件轉(zhuǎn)換Word文檔的方法(附VB.NET代碼)。在實(shí)際轉(zhuǎn)換場(chǎng)景中可參考本文的方法,感興趣的可以了解一下2022-07-07C#實(shí)現(xiàn)json格式轉(zhuǎn)換成對(duì)象并更換key的方法
這篇文章主要介紹了C#實(shí)現(xiàn)json格式轉(zhuǎn)換成對(duì)象并更換key的方法,涉及C#操作json格式數(shù)據(jù)的相關(guān)技巧,需要的朋友可以參考下2015-06-06c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù)(四種)
本文主要介紹了c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07C# 9.0新特性——擴(kuò)展方法GetEnumerator支持foreach循環(huán)
這篇文章主要介紹了C# 9.0新特性——擴(kuò)展方法GetEnumerator支持foreach循環(huán)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c# 9.0,感興趣的朋友可以了解下2020-11-11C#基于WebSocket實(shí)現(xiàn)聊天室功能
這篇文章主要為大家詳細(xì)介紹了C#基于WebSocket實(shí)現(xiàn)聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02