您好,登錄后才能下訂單哦!
這篇文章主要介紹“基于WPF怎么制作一個可編程畫板”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“基于WPF怎么制作一個可編程畫板”文章能幫助大家解決問題。
簡單使用,自定義一個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; } } }";
是不是很簡單。
1.可編程模塊,使用C#語言。
2.控制臺打印控件,可以打印程序中的Console.WriteLine數據
3.為了便于大家使用,寫了一個Box工廠分配Box的數據流向效果圖。
使用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<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 { // 編譯成功,從內存中加載編譯好的程序集 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[] { });
選擇AvalonEdit控件,另外為了使用VS2019_Dark的黑色皮膚,引用官方Demo中的HL和TextEditlib實現自定義換膚。
官方Demo的換膚寫的超級復雜,看不懂,但是我們只要理解換膚的核心部分就是動態資源字典,因此我簡化下,改進后的核心換膚代碼如下:
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> /// 設置主題 /// </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");
是不是超級簡單。
///控制臺打印方法支持切換運行輸出方法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">日志方法委托(針對于 Write)</param> /// <param name="writeLine">日志方法委托(針對于 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">日志方法委托(針對于 Write)</param> /// <param name="writeLine">日志方法委托(針對于 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 輸出重定向(帶調用方信息) /// </summary> /// <param name="write">日志方法委托(后三個參數為 CallerFilePath、CallerMemberName、CallerLineNumber)</param> public ConsoleWriter(Action<string, string, string, int> write) { _WriteCallerInfo = write; } /// <summary> /// 使用 UTF-16 避免不必要的編碼轉換 /// </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> /// 帶調用方信息進行寫消息 /// </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);
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的實現
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#按照管腳取走數據即可。
關于“基于WPF怎么制作一個可編程畫板”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。