MS Chart Прямоугольная ширина аннотации в процентах, а не в пикселях

Почему ширина прямоугольной аннотации MS-диаграммы указывается в процентах, а не в пикселях, как в msdn? https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.annotation.width(v=vs.110).aspx

Это то, что говорит msdn. Получает или задает ширину аннотации в пикселях.

Я хотел бы установить ширину в пикселях, как это указано. Я что-то здесь упускаю?

3 ответа

Решение

Поздравляем, вы нашли ошибку в документации! Легко определить, просто перейдя по ссылке на Annotation.Height docs..

Размеры Annotation, как и многие другие в Chart контроль, действительно приведены в процентах. Это имеет то преимущество, что они довольно умно растут и сжимаются вместе с Chart контролировать, как и многие другие элементы.

Так что если вы удвоите ширину графика, вы в основном удвоите пространство для показанного DataPoints и если ваш Annotation переместился на 1/3 ширины, прежде чем он будет делать это после изменения размера диаграммы.

Что приятно.

Но не то, что вы хотели..

Таким образом, чтобы установить размер в фиксированный размер в пикселях, вам нужно сделать это:

  • Рассчитайте размер, который вы хотите в процентах и ​​установите его
  • Повторяйте каждый раз, когда вы изменяете размер диаграммы или ее макета

Настоящая проблема заключается в расчете: если вы посмотрите на Chart.ClientSize Вы получаете размер в пикселях, и чтобы получить n пикселей, вам нужно сделать что-то вроде этого:

float WidthInPercent = 100f * n / Chart.ClientSize.width;

Это, однако, не учитывает различные элементы вашего Chart вероятно имеет: Size из Annotation на самом деле не рассчитывается как прямые проценты от размера диаграммы.

Вместо этого он рассчитывается в процентах от InnerPlotPosition из ChartArea CA,

InnerPlotPosition по умолчанию установлено значение Auto так что доступ к его значениям, например, CA.InnerPlotPosition.Width вернусь 0f; но вы можете получить (текущие!!) значения, выполнив это:

RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();

То же самое касается Size/Position из ChartArea сам:

RectangleF CAP = CA.Position.ToRectangleF();

Теперь вы можете объединить эти проценты с (текущим!) Chart.ClientSize чтобы узнать, какой процент вам нужно для достижения определенного размера пикселя..

Обратите внимание, что эти значения будут меняться при изменении размера, потому что внешние дополнения, такие как Legend а также Axis а также Labels и т. д. не будут изменять размеры, поэтому их относительные размеры будут увеличиваться или уменьшаться по сравнению с содержащими их элементами.

Так что вам нужно пересчитать по каждому Resize событие, или, что еще лучше: напишите функцию, которая сделает это за вас, которую вы можете вызывать, когда это необходимо...

Результатом является Annotation который (в значительной степени из-за округления) сохранит свой размер, независимо от того, как вы изменили размер Chart..

Вот несколько полезных функций:

Этот возвращает текущий ClientRectangle из ChartArea в пикселях

RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF CAR = CA.Position.ToRectangleF();
    float pw = chart.ClientSize.Width / 100f;
    float ph = chart.ClientSize.Height / 100f;
    return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}

Этот похож и возвращает текущий ClientRectangle из ChartArea's InnerplotPosition в пикселях:

RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
    RectangleF CArp = ChartAreaClientRectangle(chart, CA);

    float pw = CArp.Width / 100f;
    float ph = CArp.Height / 100f;

    return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y, 
                            pw * IPP.Width, ph * IPP.Height);
}

Наконец, тот, который преобразует размер в пикселях в единицу в процентах, снова действителен только в настоящее время, т. Е. До следующих изменений размера или компоновки..:

SizeF Pixels2Percent( ChartArea CA, int w, int h)
{
    RectangleF IPPR = InnerPlotPositionClientRectangle(chart1, CA);

    float pw = 100f * w / IPPR.Width ;
    float ph = 100f * h / IPPR.Height ;

    return new SizeF(pw, ph);
}

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

Как вы можете видеть, размер остается прежним.

Также обратите внимание на цветные прямоугольники, которые я рисую в Paint событие, чтобы продемонстрировать новые функции!

Здесь Paint событие:

private void chart1_Paint(object sender, PaintEventArgs e)
{
    ChartArea CA = chart1.ChartAreas[0];
    e.Graphics.DrawRectangle(Pens.Violet, 
               Rectangle.Round(ChartAreaClientRectangle(chart1, CA)));
    e.Graphics.DrawRectangle(Pens.LimeGreen, 
               Rectangle.Round(InnerPlotPositionClientRectangle(chart1, CA)));
}

Здесь Resize событие:

private void chart1_Resize(object sender, EventArgs e)
{
    sizeAnn(ra, new Size(24, 36));
}

А вот функция проклейки:

void sizeAnn(RectangleAnnotation ra, Size sz)
{
    ChartArea CA = chart1.ChartAreas[0];
    SizeF pixelPercent = Pixels2Percent(CA, sz.Width, sz.Height);
    ra.Width = pixelPercent.Width;
    ra.Height = pixelPercent.Height;
}

Да, мы не можем установить его в% или px, так как значение свойства установлено в double. Во-вторых, pixle = H, умноженное на W. но вы только устанавливаете ширину графика, тогда как вы можете установить ее в пикселях? Это принципиально невозможно. Я надеюсь, вы поймете, что я имею в виду.

Я знаю, что ОП хотел установить ширину в пикселях, но в случае, если некоторые люди (например, я) не хотят устанавливать ширину ни в пикселях, ни в%, а в соответствии с фактическими значениями, просто установите:

annotation.IsSizeAlwaysRelative = False
annotation.Width = whatEverValue
Другие вопросы по тегам