【发布时间】:2020-10-20 13:55:14
【问题描述】:
所以基本上我有一个自定义 UserControl 包含一个私有数组 Label 对象,我希望能够从外部专门访问它们的 Text 属性。
因此,我添加了一个属性,其类型为 LabelTextCollection 是 IEnumerable 的实现,并将我的 Label 数组作为其内部列表。此外,我添加了UITypeEditor 的实现,以允许从 Windows 窗体设计器进行编辑。
为了试一试,我在表单中添加了我的控件并编辑了属性的值。在我关闭并重新打开设计器并且标签取回它们的默认值之前,所有这些都可以正常工作。
环顾四周后,我似乎必须添加CodeDomSerializer 的实现,以允许我的类型在设计时成功序列化到{Form}.Designer.cs 文件中。我尝试先序列化一个注释行来测试它,但没有生成代码。
我的最终目标是有一条类似的线
this.{controlName}.Titles.FromArray(new string[] { "Whatever" } )
在使用我的编辑器修改属性后在设计时添加。 我有什么误解和/或做错了什么?
自定义类型
[DesignerSerializer(typeof(LabelTextCollectionSerializer), typeof(CodeDomSerializer))]
public class LabelTextCollection : IEnumerable<string>, IEnumerable
{
private Label[] labels;
public LabelTextCollection(Label[] labels)
{
this.labels = labels;
}
public void SetLabels(Label[] labels)
{
this.labels = labels;
}
public IEnumerator<string> GetEnumerator()
{
return new LabelTextEnum(labels);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new LabelTextEnum(labels);
}
public string this[int index]
{
get { return labels[index].Text; }
set { labels[index].Text = value; }
}
public override string ToString()
{
if (labels.Length == 0) return string.Empty;
else
{
StringBuilder sb = new StringBuilder("{ ");
foreach (string label in this)
{
sb.Append(label);
if (label == this.Last()) sb.Append(" }");
else sb.Append(", ");
}
return sb.ToString();
}
}
public string[] ToArray()
{
string[] arr = new string[labels.Length];
for (int i = 0; i < labels.Length; i++) arr[i] = labels[i].Text;
return arr;
}
public void FromArray(string[] arr)
{
for(int i = 0; i < arr.Length; i++)
{
if (i >= labels.Length) break;
else labels[i].Text = arr[i];
}
}
public class LabelTextEnum : IEnumerator<string>, IEnumerator
{
private readonly Label[] labels;
private int position = -1;
public LabelTextEnum(Label[] labels)
{
this.labels = labels;
}
public object Current
{
get
{
try
{
return labels[position].Text;
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
string IEnumerator<string>.Current { get { return (string)Current; } }
public void Dispose()
{
return;
}
public bool MoveNext()
{
return ++position < labels.Length;
}
public void Reset()
{
position = -1;
}
}
}
类型编辑器
public class LabelTextCollectionEditor : UITypeEditor
{
IWindowsFormsEditorService _service;
IComponentChangeService _changeService;
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
_service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
_changeService = (IComponentChangeService)provider.GetService(typeof(IComponentChangeService));
if (_service != null && _changeService != null && value is LabelTextCollection)
{
LabelTextCollection property = (LabelTextCollection)value;
LabelTextCollectionForm form = new LabelTextCollectionForm() { Items = property.ToArray() };
if (_service.ShowDialog(form) == DialogResult.OK)
{
property.FromArray(form.Items);
value = property;
_changeService.OnComponentChanged(value, null, null, null);
}
}
}
return value;
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
}
序列化器
public class LabelTextCollectionSerializer : CodeDomSerializer
{
public override object Serialize(IDesignerSerializationManager manager, object value)
{
var baseSerializer = (CodeDomSerializer)manager.GetSerializer( typeof(LabelTextCollection).BaseType, typeof(CodeDomSerializer));
object codeObject = baseSerializer.Serialize(manager, value);
if (codeObject is CodeStatementCollection && value is LabelTextCollection)
{
var col = value as LabelTextCollection;
var statements = (CodeStatementCollection)codeObject;
statements.Add(new CodeCommentStatement("LabelTextCollection : " + col.ToString()));
}
return codeObject;
}
}
自定义类型的属性
[Category("Appearance")]
[Editor(typeof(LabelTextCollectionEditor), typeof(UITypeEditor))]
public LabelTextCollection Titles { get; }
编辑:
我在我的Titles 属性中添加了一个set 并设置了我的项目以进行设计时调试,然后我意识到在线上抛出了一个异常
object codeObject = baseSerializer.Serialize(manager, value);
说明Label 类型未标记为[Serializable]。
我假设基本序列化程序正在尝试编写对我的LabelTextCollection 构造函数的调用并将labels 字段序列化为它的参数。
我尝试用
替换该行object codeObject = new CodeObject();
它摆脱了异常,但没有在designer.cs 文件中写入任何内容。
我(再一次)假设什么都没有发生,因为我刚刚创建的 CodeObject 与文件之间没有任何关系(除非该关系在 Serialize 方法返回后建立?)。
您可能会说,我对 CodeDom 的东西还很陌生,所以我应该如何正确地创建这个对象?
编辑 2:
我太笨了...我忘记了codeObject is CodeStatementCollection 测试...
所以注释行写得很好,现在我需要做的就是用 CodeDom 写正确的行,它应该可以正常工作。
如果有人想帮忙,我目前已添加到designer.cs 文件中:
this.FromArray( new string[] { "TEST" } );
所以我错过了控件和属性的名称来达到我的最终目标。
我会回答我自己的帖子,以概括我在完成后为修复它所做的工作。
【问题讨论】:
-
public LabelTextCollection Titles { get; }是错字吗?您声明 LabelTextCollectionEditor 有效,但您不应在 propertygrid 中编辑只读属性。LabelTextCollectionSerializer看起来很好,假设它被调用,它应该添加注释行。 -
如果您还没有这样做,我建议您将项目设置为调试设计器代码。有关说明,请参阅:Set up the project for design-time debugging。
-
您好,感谢您的帮助。我确实将该属性设置为只读,因为从外部设置它可能会将它与我初始化它的标签断开,我的错。我假设因为标签的文本改变了属性的值也改变了,但我没有想到编辑器仍然可以在不设置属性的情况下访问对标签的引用。
-
查看帖子编辑以了解进度更新。
-
我上次做这类工作已经十年了,但是“Serializable”错误似乎很奇怪。请澄清以下内容:1)是
Label类型System.Windows.Forms.Label或您自己的类型; 2) 这是一个 .Net Core 项目吗? 3)请在问题中添加完整的异常消息和堆栈跟踪; 4)baseSerializer返回什么类型,即baseSerializer .GetType().FullName。我错过了LabelTextCollection没有无参数构造函数;如果没有,您在调用baseSerializer.Serialize时通常会收到 null。
标签: c# winforms serialization windows-forms-designer uitypeeditor