我正在写一种MsCharts设计器。 -设计图,ChartAreas,系列,... -通过标准的System.Windows.Forms.DataVisualization.Charting.Chart.Chart.ChartSerializer
保存对象我希望用户能够向轴添加多个带状线。 我正在尝试在Axis的StripLines集合内标识一个StripLine。
StripLine的Name属性是只读的(get,没有设置)。 我看不到实际设置Name属性的方法。 我不明白这有什么用?
我本来要使用StripLine的Tag属性,但可惜Tag属性没有序列化。 注意:
如果我编辑序列化图表并将Tag =“ AStripLine”添加到元素,然后通过Chart.ChartSerializer加载,则Tag =值实际上就在其中。
如果我通过Chart保存/序列化图表。ChartSerializer标记未保存。
任何帮助/想法都将不胜感激。
答案 0 :(得分:1)
Tag
属性的类型为object
,并用内部属性修饰,该属性指示序列化程序不要序列化Tag
属性。因此,行为是预期的。
但是,由于序列化程序依赖于TypeDescriptor
,因此您可以以不同的方式为TypeDescriptor
类描述StripLine
属性创建新的Tag
,例如:
因此它会使用以下格式正确地序列化和反序列化它,例如:
<StripLine Text="text1" Tag="1" />
还在运行时在属性网格中显示它:
您需要创建以下类:
StripLineTypeDescriptionProvider
:帮助为StripLine
注册新的类型描述符StripLineTypeDescriptor
:描述类型的属性,并允许您更改Tag
属性的行为。在此类中,我们重写GetProperties
并将Tag
属性替换为修改后的属性描述符,该属性描述符告诉序列化程序对Tag
进行序列化,并告诉属性网格对其进行显示并使其可编辑。 / li>
MyPropertyDescriptor
:帮助我们指定Tag
属性的新类型。您可以决定将其设置为字符串,int或什至是复杂类型。类型可以在字符串之间来回转换就足够了。然后就足以在构造器中注册StripLine
的类型描述符或以以下形式加载事件:
var provider = new StripLineTypeDescriptionProvider();
TypeDescriptor.AddProvider(provider, typeof(StripLine));
实施
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
public class StripLineTypeDescriptionProvider : TypeDescriptionProvider
{
public StripLineTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new StripLineTypeDescriptor(baseDescriptor);
}
}
public class StripLineTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public StripLineTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>().ToList();
var tag = properties.Where(x => x.Name == "Tag").FirstOrDefault();
var tagAttributes = tag.Attributes.Cast<Attribute>()
.Where(x => x.GetType() != typeof(BrowsableAttribute)).ToList();
var serializationAttribute = tagAttributes.Single(
x => x.GetType().FullName == "System.Windows.Forms.DataVisualization.Charting.Utilities.SerializationVisibilityAttribute");
var visibility = serializationAttribute.GetType().GetField("_visibility",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
visibility.SetValue(serializationAttribute, Enum.Parse(visibility.FieldType, "Attribute"));
tagAttributes.Add(new BrowsableAttribute(true));
var newTag = new MyPropertyDescriptor(tag, tagAttributes.ToArray());
properties.Remove(tag);
properties.Add(newTag);
return new PropertyDescriptorCollection(properties.ToArray());
}
}
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor o;
public MyPropertyDescriptor(PropertyDescriptor originalProperty,
Attribute[] attributes) : base(originalProperty)
{
o = originalProperty;
AttributeArray = attributes;
}
public override bool CanResetValue(object component)
{ return o.CanResetValue(component); }
public override object GetValue(object component) => o.GetValue(component);
public override void ResetValue(object component) { o.ResetValue(component); }
public override void SetValue(object component, object value) { o.SetValue(component, value); }
public override bool ShouldSerializeValue(object component) => true;
public override AttributeCollection Attributes => new AttributeCollection(AttributeArray);
public override Type ComponentType => o.ComponentType;
public override bool IsReadOnly => false;
public override Type PropertyType => typeof(string);
}
参考
以下是类的源代码,可帮助您了解图表序列化的工作原理:
答案 1 :(得分:0)
这确实是一个奇怪的发现!
起初我以为Names
可能会以一种有用的方式自动生成,例如StripLine1
,StripLine2
等。
但是他们都以StripLine
作为Name
。
因此,对于您识别它们将毫无用处。
但是有Tag
属性可以解决。这很容易设置为唯一字符串。.
StripLine sl = new StripLine()
{ Text = "LW" , StripWidth = 2, ForeColor = Color.Teal, Tag = "Low-Water"};
要使其对于轴AxisY
唯一,可以使用以下方法:
StripLine sl = new StripLine()
{ Text = "LW" , StripWidth = 2, ForeColor = Color.Teal,
Tag = "Low-Water" + chart1.ChartAreas[0].AxisY.StripLines.Count };
由于Tag
的类型为object
,因此您可以创建一个类来保存更多信息,例如短名称和说明。
更新:我刚刚注意到您了解Tags
以及如何不对其进行序列化。但是,您可以使用以下解决方法:
StripLines
并将Text
更改为:oldText +分隔符+标记字符串。作为分隔符,您可以使用制表符(\ t)或文本中不需要的其他字符(或字符串)。(垂直制表符,我的初衷是不允许使用xml实体。)>
这是准备Text
并重建Tags
的功能:
void StripLineTagger(Chart chart, bool beforeSer)
{
char sep = '\t';
var axes = new List<Axis> { chart.ChartAreas[0].AxisX, chart.ChartAreas[0].AxisX2,
chart.ChartAreas[0].AxisY, chart.ChartAreas[0].AxisY2};
foreach (var ax in axes)
foreach (var sl in ax.StripLines)
{
if (beforeSer) sl.Text = sl.Text + sep + sl.Tag.ToString();
else
{
var p = sl.Text.Split(sep);
sl.Text = p[0];
sl.Tag = p[1];
}
}
}
这是未经测试的,缺少所有检查。.
更新2:
您可以添加自己的子类来替换常规的StripLines
:
class MyStripLine : StripLine
{
new public string Name { get; set; } // looks fine butwon't get serialized
public string ID{ get; set; } // gets serialized
//..
public MyStripLine()
{
}
}
可以将它们添加到StripLines集合中并按预期工作。不幸的是Name
属性看起来不错,但是没有写出来。.使用另一属性(ID
)很简单,我无法进行反序列化工作。