gpt4 book ai didi

c# - 托管 Windows 窗体设计器 - 在运行时序列化设计器并生成 C# 代码

转载 作者:行者123 更新时间:2023-12-01 23:47:02 35 4
gpt4 key购买 nike

我正在创建一个设计器界面并将控件加载到运行时。我在将控件反序列化/加载到运行时时遇到问题。

我尝试过的所有方法似乎都存在某种类型的问题。

发出面临的例子:

  • 控件仍然受设计时的约束
  • 并非所有属性都会与所有属性(即嵌套属性)进行反序列化。
  • 似乎确实遵循了控件关联,即面板中的按钮将不再出现在面板中,即使该属性在加载后仍然是父级。

我在 git 上创建了一个示例项目:Surface Designer Test

主要代码片段:

从设计时开始序列化

private void LoadRuntime(int type)
{
var controls = surface.ComponentContainer.Components;
SerializationStore data = (SerializationStore)surface.
_designerSerializationService.Serialize(controls);
MemoryStream ms = new MemoryStream();
data.Save(ms);
SaveData.Data = ms.ToArray();
SaveData.LoadType = type;
new RuntimeForm().Show();
}

public object Serialize(System.Collections.ICollection objects)
{
ComponentSerializationService componentSerializationService =
_serviceProvider.GetService(typeof(ComponentSerializationService)) as
ComponentSerializationService;
SerializationStore returnObject = null;
using (SerializationStore serializationStore =
componentSerializationService.CreateStore())
{
foreach (object obj in objects)
{
if (obj is Control control)
{
componentSerializationService.SerializeAbsolute(serializationStore, obj);
}
returnObject = serializationStore;
}
}
return returnObject;
}

运行时反序列化

这是反射的尝试:

MemoryStream ms = new MemoryStream(SaveData.Data);
Designer d = new Designer();
var controls = d._designerSerializationService.Deserialize(ms);

ms.Close();
if (SaveData.LoadType == 1)
{
foreach (Control cont in controls)
{
var ts = Assembly.Load(cont.GetType().Assembly.FullName);
var o = ts.GetType(cont.GetType().FullName);
Control controlform = (Control)Activator.CreateInstance(o);
PropertyInfo[] controlProperties = cont.GetType().GetProperties();
foreach (PropertyInfo propInfo in controlProperties)
{
if (propInfo.CanWrite)
{
if (propInfo.Name != "Site" && propInfo.Name != WindowTarget")
{
try
{
var obj = propInfo.GetValue(cont, null);
propInfo.SetValue(controlform, obj, null);
}
catch { }
}
else { }
}
}
Controls.Add(controlform);
}
}

这是直接加载控件的尝试(仍然绑定(bind)到设计时):

MemoryStream ms = new MemoryStream(SaveData.Data);
Designer d = new Designer();
var controls = d._designerSerializationService.Deserialize(ms);
foreach (Control cont in controls)
Controls.Add(cont);

我觉得我错过了 System.ComponentModel.Design 中的一个概念框架。

我也不认为有必要为每个控件编写一个自定义序列化器,因为 Visual Studio 肯定已经有了这个序列化器,能够在 PropertyGrid 中更改它们的所有属性时序列化它们> 并在运行程序时加载它们。

我很想将设计器序列化到 .cs 文件中,但是如何实现呢?如何序列化控件/表单并将属性更改为像 VS 设计器一样的文件,我尝试并只找到了 xml 和二进制序列化器。我理想的解决方案是使用 CodeDom 构建一个 designer.cs

在设计时和运行时之间完成这种序列化的正确方法是什么?

最佳答案

假设您有 DesignSurfaceForm 显示为设计器的根组件,并使用 CreateComponent 在运行时创建一些组件方法IDesignerHost ,这是我解决问题的方法:

您还可以稍微扩展示例并使用 ISelectionService使用 PropertyGrid 获取有关所选组件的通知并在运行时更改属性:

enter image description here

示例 - 在运行时从 DesignSurface 生成 C# 代码

在此示例中,我将展示如何在运行时托管 Windows 窗体设计器并设计包含一些控件和组件的窗体,并在运行时生成 C# 代码并运行生成的代码。

Please note: It's not a production code and it's just an example as a proof of concept.

创建 DesignSurface 并托管设计器

您可以像这样创 build 计图面:

DesignSurface designSurface;
private void Form1_Load(object sender, EventArgs e)
{
designSurface = new DesignSurface(typeof(Form));
var host = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));
var root = (Form)host.RootComponent;
TypeDescriptor.GetProperties(root)["Name"].SetValue(root, "Form1");
root.Text = "Form1";

var button1 = (Button)host.CreateComponent(typeof(Button), "button1");
button1.Text = "button1";
button1.Location = new Point(8, 8);
root.Controls.Add(button1);

var timer1 = (Timer)host.CreateComponent(typeof(Timer), "timer1");
timer1.Interval = 2000;
var view = (Control)designSurface.View;
view.Dock = DockStyle.Fill;
view.BackColor = Color.White;
this.Controls.Add(view);
}

使用 TypeCodeDomSerializer 和 CSharpCodeProvider 生成 C# 代码

这就是我从设计界面生成代码的方式:

string GenerateCSFromDesigner(DesignSurface designSurface)
{
CodeTypeDeclaration type;
var host = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));
var root = host.RootComponent;
var manager = new DesignerSerializationManager(host);
using (manager.CreateSession())
{
var serializer = (TypeCodeDomSerializer)manager.GetSerializer(root.GetType(),
typeof(TypeCodeDomSerializer));
type = serializer.Serialize(manager, root, host.Container.Components);
type.IsPartial = true;
type.Members.OfType<CodeConstructor>()
.FirstOrDefault().Attributes = MemberAttributes.Public;
}
var builder = new StringBuilder();
CodeGeneratorOptions option = new CodeGeneratorOptions();
option.BracingStyle = "C";
option.BlankLinesBetweenMembers = false;
using (var writer = new StringWriter(builder, CultureInfo.InvariantCulture))
{
using (var codeDomProvider = new CSharpCodeProvider())
{
codeDomProvider.GenerateCodeFromType(type, writer, option);
}
return builder.ToString();
}
}

例如:

var code = GenerateCSFromDesigner(designSurface);

通过 CSharpCodeProvider 运行代码

然后运行它:

void Run(string code, string formName)
{
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] {
"mscorlib.dll",
"System.Windows.Forms.dll",
"System.dll",
"System.Drawing.dll",
"System.Core.dll",
"Microsoft.CSharp.dll"});
parameters.GenerateExecutable = true;
code = $@"
{code}
public class Program
{{
[System.STAThread]
static void Main()
{{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
System.Windows.Forms.Application.Run(new {formName}());
}}
}}";
var results = csc.CompileAssemblyFromSource(parameters, code);
if (!results.Errors.HasErrors)
{
System.Diagnostics.Process.Start(results.CompiledAssembly.CodeBase);
}
else
{
var errors = string.Join(Environment.NewLine,
results.Errors.Cast<CompilerError>().Select(x => x.ErrorText));
MessageBox.Show(errors);
}
}

例如:

Run(GenerateCSFromDesigner(designSurface), "Form1");

关于c# - 托管 Windows 窗体设计器 - 在运行时序列化设计器并生成 C# 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59529839/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com