【问题标题】:How to save collection data in Design Time with Custom UITypeEditor C# WindowsFormApplication?如何使用自定义 UITypeEditor C# WindowsFormApplication 在设计时保存集合数据?
【发布时间】:2016-12-01 22:45:01
【问题描述】:

我编写了自己的 UITypeEditor,并在 @Sefe 和 THIS 链接的帮助下实现了我的目标。

我的基本设置:

在此设置中,BaseForm 扩展了 System.Windows.Forms.Form。在这里,我将我的属性 (List<Control> ActionButtons) 放入了具有自定义 UITypeEditor 的属性,例如模态样式。

恢复的工作流程(这里的所有内容都在设计时):

    1 - 打开我的表单。
2 - 单击 MyForm 属性面板上的 ActionButtons(由 BaseForm 继承)省略号 [...]。
3 - 打开一个自定义表单,其中包含我想要选择的充气对象。 (此表单由我的 CustomUITypeEditor 调用)
4 - 选择我想要的对象,然后关闭表单。所以现在,MyForm 中的数据正常,并序列化到 Designer.cs 文件中。我可以在省略号中再次单击重新打开该 EditorUI,然后查看我之前选择的对象。
5 - 现在当我关闭 MyForm 并重新打开它时,所有数据都丢失了!!!但数据仍然序列化到 Designer.cs 中。 6 - 如果我使用 string 而不是 List<Control> 执行所有这些步骤,则一切正常。

下面是我的代码:

public class CollectionTypeEditor : UITypeEditor {

private IWindowsFormsEditorService _editorService = null;
private ICollection<Control> mControls = null;
private List<Control> mPickedControls = null;

// Editor like Modal style
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
  return UITypeEditorEditStyle.Modal;
}

// Opens modal and get returned data
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
  if (provider == null)
    return value;

  _editorService = (IWindowsFormsEditorService) provider
    .GetService(typeof(IWindowsFormsEditorService));

  if (_editorService == null)
    return value;

  mControls = new List<Control>();

  // retrieve old data
  mPickedControls = value as List<Control>;
  if (mPickedControls == null)
    mPickedControls = new List<Control>();

  // getting existent controls that will be inflated in modal
  Control mContext = (Control) context.Instance;
  GetControls(mContext);

  // open form and get response
  CollectionDesign<Control> frmCollections = new CollectionDesign<Control>(mControls, ref mPickedControls);
  var response = _editorService.ShowDialog(frmCollections);

  // returning data from editor
  return response == DialogResult.OK ? mPickedControls : value;
}

这里一切正常。我在BaseForm 中的变量代码。 Ps.:这个变量将显示在MyForm 上,我点击省略号 [...]

[Editor(typeof(CollectionTypeEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(ActionButtonConverter))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Control> ActionButtons { get; set; }

添加了序列化属性,因为无法保存文件。当表单关闭并重新打开时,所有数据都将丢失。

奇怪的是我写了其他 UITypeEditor 就像 同样的方式,只是将数据类型更改为string,我可以关闭或重新打开我的表单,一切正常,数据被保存.

我已经添加了一个 TypeConverter,但我认为这不是这里的情况。我的代码有什么问题?

我在重新打开表单时收到此错误:

Severity Code Description Project File Line Suppression State Message Method 'System.CodeDom.CodePropertyReferenceExpression.Add' not found.        

更新

现在我的控件列表在关闭或重新打开时存储在 myForm.designer 文件中,但控件未附加到属性网格上。即:如果我在关闭并重新打开 myForm 时单击省略号添加按钮“addbt”,则代码会自动生成。

myForm.designer 上自动生成的代码如下:

  // 
  // addbt
  // 
  this.addbt.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
  this.addbt.Cursor = System.Windows.Forms.Cursors.Hand;
  this.addbt.Font = new System.Drawing.Font("Tahoma", 9F);
  this.addbt.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(222)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
  this.addbt.Location = new System.Drawing.Point(13, 6);
  this.addbt.Name = "addbt";
  this.addbt.Size = new System.Drawing.Size(103, 33);
  this.addbt.TabIndex = 0;
  this.addbt.Text = "Incluir";
  this.addbt.UseVisualStyleBackColor = true;

  // 
  // myForm
  // 
  this.ActionButtons.Add(this.addbt);

如果我再次单击省略号,数据将附加到 myForm 的 PropertyGrid 上的属性。但是当我重新打开 myForm 时,之前存储的这些值不会传递给 myForm 的 PropertyGrid 上的属性(数据仍存储在自动生成的代码设计器中)。因此,当单击省略号 [...] 时,value 中的 EditValue 方法不会存储数据。我觉得它更接近了:)

下面的 TypeConverter 可能有问题:

public class ActionButtonConverter : TypeConverter {

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
  if (sourceType == (typeof(string)))
    return true;

  return base.CanConvertFrom(context, sourceType);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
  if(value.GetType() == typeof(string)) {
    List<Control> ctrs = value as List<Control>;

    if (ctrs != null)
      return ctrs;
  }

  return base.ConvertFrom(context, culture, value);
}


public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
  if (destinationType == typeof(string))
    return true;
  return base.CanConvertTo(context, destinationType);
}

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
  if (value == null)
    return "null";

  if (destinationType == typeof(string))
    return "(custom collection)";

  return base.ConvertTo(context, culture, value, destinationType);
}

我认为我在反序列化中有任何错误。当 myForm 重新打开时,ITypeDescriptorContext 或值 (TypeConverter) 没有任何关于序列化数据到 Designer.cs 文件中的信息。

谢谢大家,抱歉语言不好:P

【问题讨论】:

  • 我不太明白发生了什么。当您重新打开表单时,您在属性网格中看到了什么?当您单击省略号按钮时,您的表单设计器是填充的还是空的?
  • @Sefe 当我打开表单时,属性网格中的变量为空,但NameForm.Designer.cs 文件已正确保存数据,就像我在 UPDATE 部分中显示的那样。那是序列化问题还是 TypeConverter 问题?
  • 当你打开你的编辑器时,它会显示所有添加的控件吗?如果是,则可能是类型编辑器问题。在这种情况下,您应该确保您的列表正确转换为字符串。如果没有,您可能需要查看您的 serialilzer 的反序列化方法。
  • @Sefe 如果您说的编辑器与 myForm 相同,那么是的,所有控件都会添加。如果不是,则该编辑器是 typeof(CustomTypeEditor) 然后不是(未添加控件)。你能告诉我如何序列化/反序列化这些数据或编写一个基本示例,如包含两个项目的列表吗?我应该放什么课?我将在更新部分发布我的 TypeEditor。

标签: c# winforms design-time uitypeeditor


【解决方案1】:

WinForms 设计时支持是一个棘手的主题。为了更好地理解:发生的事情是正在设计的表单被转换为代码。这个过程称为设计时序列化。序列化由CodeDomSerializer 执行,可以通过DesignerSerializerAttribute 应用于属性。您可以在表单的自动生成文件中查看生成的代码。当您进行 WinForms 设计时开发时,您有时必须检查序列化程序输出(在 FormName.Designer.cs 文件中)。我敢肯定,在您的情况下,不会有任何输出将控件添加到您的容器控件(否则关闭和重新打开表单不会有问题)。

默认序列化程序可以处理大多数序列化情况,主要是在TypeConverter 的帮助下。所以您通常不必创建自己的序列化程序,但有时您必须这样做。你的情况就是这样。

您添加到帖子中的错误消息是 CodeDom 错误消息,因此它肯定来自序列化程序,因为这是创建源代码的地方。我认为您在这种特殊情况下的问题是您没有将新项目添加到您的列表中,而是您的表单上已经存在的其他控件。通常,默认序列化程序将为集合中的每个元素创建一个新项目。但是,这不是您需要在此处执行的操作,因为您希望将现有 项添加到集合中(这就是它与string 一起使用的原因,因为它始终可以创建为文字)。

您的解决方案是创建自己的CodeDomSerializer,通过设计时架构查找添加的控件(您可能需要IReferenceService)并添加 CodeDom 图以将现有项目添加到您的集合中.

例如,默认序列化程序创建的代码如下所示:

this.myControl.ActionButtons.Add(new Button());

您的代码必须如下所示:

this.myControl.ActionButtons.Add(this.myActionButton);

这意味着首先使用 IReferenceService 获取按钮的名称(您只有集合中的对象),然后创建一个 CodeDom 图表,将此按钮添加到您的属性中。为此,您必须覆盖 CodeDomSerializer.SerializeProperty 并拦截您的 ActionButtons 属性的序列化(确保调用所有其他属性的基类),您可以在其中进行序列化。

【讨论】:

  • 你能再看一下更新部分吗?感谢您的回复。 :)
【解决方案2】:

几天后,我找到了解决此问题的方法。我解决了它创建我自己的继承 CollectionBase 的 Button 集合:

public class ButtonCollection : CollectionBase {

public CustomButton this[int i] {
  get { return InnerList[i] as CustomButton; }
  set { InnerList[i] = value; }
}

public ButtonCollection() {

}

public CustomButton Add(CustomButton bt) {
  InnerList.Add(bt);
  return bt;
}

public void AddRange(CustomButton[] bts) {
  InnerList.AddRange(bts);
}

public void Remove(CustomButton bt) {
  InnerList.Remove(bt);
}

public bool Contains(CustomButton bt) {
  return InnerList.Contains(bt);
}

public CustomButton[] GetValues() {
  CustomButton[] bts = new CustomButton[InnerList.Count];
  InnerList.CopyTo(bts);
  return bts;
}

我还对 TypeConverter 进行了更改:

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo info, object value, Type destType) {

  if ((destType == typeof(string)) && (value is CustomButton)) {
    CustomButton bt = (CustomButton) value;
    return bt.Name;
  }

  // this helped me a lot
  // here the object needs to know how to create itself
  // Type[0] can be overridden by Type[] { (your constructor parameterTypes) }
  // null can be overridden by objects that will be passed how parameter
  // third parameter is a value indicating if the initialization of the object is or not complete
  else if (destType == typeof(InstanceDescriptor)) {
    return new InstanceDescriptor(
      typeof(CustomButton).GetConstructor(new Type[0]),
      null,
      false
    );
  }

  return base.ConvertTo(context, info, value, destType);
}

TypeConverter 装饰器传递给CustomButton 类:

[TypeConverter(typeof(CustomButtonConverter))]
public class CustomButton { 
...

我在自定义 collectionsEditor 的完整示例之后完成了这一切,可以在 here 找到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多