Нарисуйте маркер (тег) с помощью GDI

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

введите здесь описание изображения

и я начал кодировать, через пару минут у меня было это:

введите здесь описание изображения

Как видите, большой маркер выглядит неплохо, но самый маленький мог бы смотреться и лучше.

Мой текущий код, отвечающий за отрисовку маркера, выглядит так:

    enum Position
    {
        Left,
        Right
    }

    private void DrawMarker(Graphics gfx, Rectangle Bounds, int CornerRadius, Pen DrawPen, Position direction)
    {
        GraphicsPath gfxPath = new GraphicsPath();
        gfxPath.AddArc(new Rectangle(Bounds.X + Bounds.Width - CornerRadius, Bounds.Y + 0, CornerRadius, CornerRadius), 270, 45); //tr

        if(direction==Position.Right)
            gfxPath.AddArc(new Rectangle(Bounds.X + Bounds.Width - CornerRadius + 40, Bounds.Y + Bounds.Height / 2, CornerRadius, CornerRadius), 315, 90); //right

        gfxPath.AddArc(new Rectangle(Bounds.X + Bounds.Width - CornerRadius, Bounds.Y + Bounds.Height - CornerRadius, CornerRadius, CornerRadius), 45, 45); //br
        gfxPath.AddArc(new Rectangle(Bounds.X + 0, Bounds.Y + Bounds.Height - CornerRadius, CornerRadius, CornerRadius), 90, 90); //bl
        gfxPath.AddArc(new Rectangle(Bounds.X + 0, Bounds.Y + 0, CornerRadius, CornerRadius), 180, 90); //tl
        gfxPath.CloseAllFigures();
        //gfx.FillPath(new SolidBrush(FillColor), gfxPath);
        gfx.DrawPath(DrawPen, gfxPath);
    }

Мне нужен способ улучшить внешний вид этих маркеров.
Я думаю, что проблема в углах дуг в верхнем правом и нижнем правом углах. Я установил их на 45, но это выглядит плохо, если отметки маленькие.

Углы с левой стороны всегда будут равны 90, а углы справа и на отмеченной стороне необходимо рассчитать.

Как мне изменить свою функцию для правильного расчета дуг?


person Misiu    schedule 14.02.2014    source источник
comment
Я настоятельно рекомендую WPF для этого.   -  person Federico Berasategui    schedule 14.02.2014
comment
Очевидно, ваша математика неверна. Простой способ избежать математических вычислений — начать с одного маркера и просто масштабировать его.   -  person Hans Passant    schedule 14.02.2014
comment
@HighCore Я хотел бы использовать для этого WPF, но, к сожалению, проект построен в WinForms, и этот элемент управления должен работать в WinForms.   -  person Misiu    schedule 14.02.2014
comment
@misiu вы можете размещать контент WPF в существующем приложении winforms через файл ElementHost.   -  person Federico Berasategui    schedule 14.02.2014
comment
@ Ганс, что ты имеешь в виду, говоря о масштабировании? Мне не нужно вычислять положение каждого угла? Мне нужен способ нарисовать маркер, который изменит свою высоту в зависимости от текста.   -  person Misiu    schedule 14.02.2014
comment
@HighCore Я знаю, что могу добавить элементы управления WPF в WinForms, проблема в том, что я не являюсь пользователем WPF, и это может быть сложно для первой сборки с использованием WPF.   -  person Misiu    schedule 14.02.2014
comment
@HighCore WPF? Пфф! :П   -  person SE13013    schedule 17.07.2015


Ответы (1)


Похоже, что «нос» прямоугольника тега не компенсирует размер радиуса, из-за чего «нос» опускается вниз:

r.Y + r.Height / 2 - radius / 2

Я также укоротил ширину с 40 до 20.

private void DrawMarker(Graphics gfx, Rectangle r, int radius, Pen drawPen, Position direction) {
  using (GraphicsPath gfxPath = new GraphicsPath()) {
    gfxPath.AddArc(new Rectangle(r.X + r.Width - radius, r.Y + 0, radius, radius), 270, 45); //tr
    if (direction == Position.Right) {
      gfxPath.AddArc(new Rectangle(r.X + r.Width - radius + 20, r.Y + r.Height / 2 - radius / 2, radius, radius), 315, 90); //right
    }
    gfxPath.AddArc(new Rectangle(r.X + r.Width - radius, r.Y + r.Height - radius, radius, radius), 45, 45); //br
    gfxPath.AddArc(new Rectangle(r.X + 0, r.Y + r.Height - radius, radius, radius), 90, 90); //bl
    gfxPath.AddArc(new Rectangle(r.X + 0, r.Y + 0, radius, radius), 180, 90); //tl
    gfxPath.CloseAllFigures();
    gfx.DrawPath(drawPen, gfxPath);
  }
}

protected override void OnPaint(PaintEventArgs e) {
  e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  using (Pen pen = new Pen(Color.DarkGray, 2)) {
    DrawMarker(e.Graphics, new Rectangle(16, 16, 100, 32), 8, pen, Position.Right);
  }
  base.OnPaint(e);
}

Результат:

введите здесь описание изображения

person LarsTech    schedule 14.02.2014
comment
Спасибо за исправление носа :) Сейчас проверю. Моя проблема не в положении носа, а в углах с правой стороны (стороны, где находится нос), потому что сейчас у меня жестко запрограммировано 45 градусов, но я думаю, что это должно быть рассчитано на основе высоты маркера. Если маркер высокий, то угол больше, если низкий, угол больше. То же самое для угла дуги носа. У меня проблема с вычислением этого. - person Misiu; 14.02.2014
comment
@Misiu Я не думаю, что это проблема угла. Когда я смотрю на исходное изображение, это выглядит как проблема с расстоянием. Для расстояния до носа попробуйте r.X + r.Width - radius + r.Height / 2. Я добавляю половину высоты прямоугольника, чтобы нос больше торчал, когда он больше. - person LarsTech; 14.02.2014