Как определить StripLine в коллекции StripLines (ось) (сериализация)? - PullRequest
2 голосов
/ 09 июля 2019

Я пишу что-то вроде дизайнера MsCharts. - Схема проектирования, ChartAreas, Series, ... - Объект сохраняется с помощью стандартного System.Windows.Forms.DataVisualization.Charting.Chart.ChartSerializer

Я хочу, чтобы пользователь мог добавить несколько полос в Оси. Я пытаюсь определить StripLine в Коллекции StripLines Оси.

Свойство Name StripLine доступно только для чтения (получить, не устанавливать). Я не вижу способа установить свойство Name. Я не понимаю, как это полезно?

Я собирался использовать свойство Tag в StripLine, но, увы, свойство Tag не сериализовано. Примечание:

Если я отредактирую сериализованную диаграмму и добавлю тег = "AStripLine" к элементу, а затем загрузлю его через Chart.ChartSerializer Значение тега = фактически есть.

Если я сохраняю / сериализую диаграмму через Chart.ChartSerializer Tag не сохраняется.

Любая помощь / идеи будут с благодарностью.

Ответы [ 2 ]

1 голос
/ 10 июля 2019
Свойство

Tag имеет тип object, и оно украшено внутренним атрибутом, указывающим сериализатору не сериализовать свойство Tag. Так что поведение ожидается.

Но поскольку сериализатор использует TypeDescriptor, вы можете создать новое свойство TypeDescriptor для StripLine, описывающее Tag, другим способом, например:

  • Сделать его доступным для просмотра в сетке свойств
  • Сделать его редактируемым в сетке свойств
  • Сделать его сериализуемым для сериализатора диаграммы

Таким образом, он корректно сериализует и десериализует его в этом формате, например:

<StripLine Text="text1" Tag="1" />

Также показывает это в сетке свойств во время выполнения:

enter image description here

Вам необходимо создать следующие классы:

  • StripLineTypeDescriptionProvider: помогает зарегистрировать дескриптор нового типа для StripLine
  • StripLineTypeDescriptor: описывает свойства типа и позволяет изменять Tag поведение свойства. В этом классе мы переопределяем GetProperties и заменяем свойство Tag измененным дескриптором свойства, который указывает сериализатору сериализовать Tag, а также указывает сетке свойств показать его и сделать его редактируемым.
  • MyPropertyDescriptor: Помогает нам указать новый тип свойства Tag. Вы можете решить установить его как string, 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);
}

Ссылки

Вот исходный код для классов, который поможет вам узнать, как работает сериализация диаграмм:

0 голосов
/ 09 июля 2019

Это действительно странная находка!

Сначала я подумал, что, возможно, 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 + separator + Tag tag.
  • После десериализациивы делаете наоборот.

В качестве разделителя вы можете использовать табуляцию (\ 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) просто, я не могу заставить работать десериализацию.

...