2014-12-23 2 views
2

Я пишу плагин для торгового программного обеспечения (C#, winforms, .NET 3.5), и я хотел бы нарисовать перекрестие курсора над панелью (допустим, ChartPanel), который содержит данные, которые могут быть дороже краски. Что я сделал до сих пор:Существующие графики в растровые изображения

  1. Я добавил CursorControl к панелям
    • это CursorControl расположена над главной панелью рисования так, чтобы она покрывала это вся область
    • это Enabled = false так, что все входные события передаются родителю ChartPanel
    • это Paint метод реализован таким образом, что он рисует линии сверху вниз и слева направо в текущей позиции мыши
  2. Когда MouseMove событие вызывается, у меня есть две возможности:
    • ) призыв ChartPanel.Invalidate(), но, как я сказал, основные данные могут быть дорогими, чтобы рисовать и это приведет к тому, чтобы все перерисовывать каждый раз я двигаю мышь , что является неправильным (но это единственный способ, которым я могу сделать эту работу сейчас)
    • B) Вызвать CursorControl.Invalidate() и до того, как курсор будет нарисован, я бы сделал снимок данных, нарисованных в настоящее время, и сохранил их как фон для курсора, который будет просто восстановлен каждый раз, когда курсор нужно перекрасить ... проблема с этим ... Я не знаю, как это сделать.

2.б. Это будет означать, чтобы:

  • Turn существующий Graphics объект в Bitmap (она (Graphics) дается мне через метод Paint, и я должен рисовать на ней, так что я просто не могу создать новый объект Graphics. .. может быть, я получаю это неправильно, но это, как я понимаю)
  • до перекрестия расписано, восстановление графика содержимого из Bitmap и перекрашивать перекрестие

Я не могу контролировать процесс картины дорогих данных. Я могу просто получить доступ к моим CursorControl и его методам, которые вызывается через API.

Итак, есть ли способ сохранить существующее графическое содержимое в Bitmap и восстановить его позже? Или есть лучший способ решить эту проблему?


ПОСТАНОВИЛ: После многих часов проб и ошибок я придумал рабочее решение. Есть много проблем с программным обеспечением я использую, которые не могут быть обсуждены в целом, но основные принципы ясны:

  • существующих графики с уже нарисованной вещью не могут быть преобразованы непосредственно Bitmap, вместо этого я должен был использовать panel.DrawToBitmap способ впервые упомянутый в @ Gusman ответ.Я знал об этом, я хотел этого избежать, но в конце концов я должен был согласиться, потому что это единственный способ:
  • Также я хотел избежать двойного рисования каждого кадра, поэтому первая краска для перекрестия всегда рисуется непосредственно на ChartPanel. После перемещения мыши без изменения изображения диаграммы я делаю снимок через DrawToBitmap и продолжаю, как описано в выбранном ответе.
  • управления должно быть непрозрачным (не включен прозрачным фон), чтобы освежать это не вызывает краски на нем родительское управление (что могло бы привести весь график перекрашивать)

Я до сих пор испытываю случайные мерцания каждым несколько секунд или около того, но я думаю, что могу как-то это понять. Хотя я выбрал ответ Гусмана, я хотел бы поблагодарить всех участников, поскольку я использовал много других трюков, упомянутых в других ответах, таких как Panel.BackgroundImage, использование метода Plot() вместо Paint() для блокировки изображения и т. Д.

+0

В отличие от сайтов форума, мы не используем «Спасибо» или «Любая помощь приветствуется», или подписи на [так ]. См. «[Должны ли« Привет »,« спасибо », теги и приветствия удалены из сообщений?] (Http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be -removed-from-posts). –

+0

Что касается наличия второй панели, расположенной поверх другой, которая имеет прозрачный фон и используется исключительно для рисования курсора (с линиями). Кроме того, убедитесь, что все двойное буферизовать и оптимизировать сложную покраску, где это возможно (например, обеспечить, чтобы все вычисления выполнялись внутри функции «загрузка», а не в событии рисования) – musefan

+0

«Я не могу контролировать процесс рисования дорогих данных» - может вы, по крайней мере, контролируете и/или реагируете на любые изменения данных, которые могли бы потребовать обновления диаграммы? Если это так, тогда вы сможете (в зависимости от того, как реализован элемент управления диаграммой) буферизировать рендеринг диаграммы путем подклассификации , переопределяя 'OnPaint()' и перехватывая рендеринг. Если нет, то b ig challenge здесь обнаруживает, когда визуализированная диаграмма изменилась, так что вы знаете, когда обновлять внеэкранную копию элемента управления (что делает внеэкранную копию сама по себе не слишком сложной). –

ответ

2

Почему вы не клонируете всю графику в ChartPanel над CursorControl?

Весь код здесь должен быть помещен в ваш CursorControl.

Во-первых, создать свойство, которое будет содержать ссылку на график и крюк, чтобы это краска событие, что-то вроде этого:

ChartPanel panel; 

public ChartPanel Panel 
{ 
    get{ return panel; } 

    set{ 

     if(panel != null) 
      panel.Paint -= CloneAspect; 

     panel = value; 

     panel.Paint += CloneAspect; 
    } 

} 

Теперь определим функцию CloneAspect, который будет оказывать внешний вид элемента управления в растр когда это краска opperation было сделано в панели Chart:

Bitmap aspect; 

void CloneAspect(object sender, PaintEventArgs e) 
{ 

    if(aspect == null || aspect.Width != panel.Width || aspect.Height != panel.Height) 
    { 

     if(aspect != null) 
      aspect.Dispose(); 

     aspect = new Bitmap(panel.Width, panel.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); 

    } 

    panel.DrawToBitmap(aspect, new Rectangle(0,0, panel.Width, panel.Height); 

} 

Тогда в переопределен метод OnPaint сделать это:

public override void OnPaint(PaintEventArgs e) 
{ 
    e.Graphics.DrawImage(aspect); 

    //Now draw the cursor 
    (...) 
} 

И, наконец, где вы создаете диаграмму и customcursor вы делаете:

CursorControl.Panel = ChartPanel; 

И вуаля, вы можете перерисовать столько раз, сколько нужно, без перерасчета содержания графика.

Cheers.

+0

Нет причин для вызова 'CreateGraphics()' в 'OnPaint()' или обработчике события 'Paint'. Весь смысл события «Paint» заключается в том, что экземпляр «Graphics», который вам требуется для рисования, передается вам. –

+0

Кроме того, использование события «Paint» в качестве триггера для визуализации управления диаграммой в растровое изображение действительно не помогает; это означает, что рендеринг будет буферизирован для любого обновления на экране, что может произойти по разным причинам, а не только по изменению данных (например, часть окна была открыта перекрывающимся окном, окно было восстановлено или изменено, и т. д.) –

+0

Это то, что я пробовал сразу после того, как задал этот вопрос, но здесь я столкнулся с двумя проблемами: 1. Panel.DrawToBitmap() снова запускает событие Paint ChartPanel, поэтому дорогая картина теперь выполняется дважды. 2. Paint() на родительской панели перерисовывает даже CursorControl, поэтому мы заканчиваем рекурсию. Это можно было бы обработать, но если дублировать вызов можно как-то избежать, то я бы хотел его избежать. – LukasH

3

Это можно сделать несколькими способами, всегда сохраняя графику как Bitmap. Самый прямой и эффективный способ - позволить Panel выполнить всю работу за вас.

Вот эта идея: Большинство winforms Controls имеют двухслойный дисплей.

В случае Panel двумя слоями являются его BackgroundImage и его поверхность Control. То же самое верно для многих других элементов управления, таких как Label, CheckBox, RadioButton или Button.

(одно интересное исключение PictureBox, который в дополнении имеет (передний план) Image.)

Таким образом, мы можем переместить дорогие вещи в BackgroundImage и нарисуйте крестик на surcafe. В нашем случае, Panel, все приятные дополнительные услуги находятся на месте, и вы можете выбрать все значения для объекта BackgroundImageLayout, включая Tile, Stretch, Center или Zoom. Мы выбираем None.

Теперь мы добавим один флаг к вашему проекту:

bool panelLocked = false; 

и функцию, чтобы установить его по мере необходимости:

void lockPanel(bool lockIt) 
{ 
    if (lockIt) 
    {  
     Bitmap bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Width); 
     panel1.DrawToBitmap(bmp, panel1.ClientRectangle); 
     panel1.BackgroundImage = bmp; 
    } 
    else 
    { 
     if (panel1.BackgroundImage != null) 
      panel1.BackgroundImage.Dispose(); 
     panel1.BackgroundImage = null; 
    } 
    panelLocked = lockIt; 
} 

Здесь вы можете увидеть магию на работе: Прежде чем мы на самом деле заприте Panel, сделав дорогой материал, мы сообщим ему создать снимок его графики и поместить его в BackgroundImage.

Теперь нам нужно использовать флаг контролировать Paint событие:

private void panel1_Paint(object sender, PaintEventArgs e) 
{ 
    Size size = panel1.ClientSize; 

    if (panelLocked) 
    { 
     // draw a full size cross-hair cursor over the whole Panel 
     // change this to suit your own needs! 
     e.Graphics.DrawLine(Pens.Red, 0, mouseCursor.Y, size.Width - 1, mouseCursor.Y); 
     e.Graphics.DrawLine(Pens.Red, mouseCursor.X, 0, mouseCursor.X, size.Height); 
    } 

    // expensive drawing, you insert your own stuff here.. 
    else 
    { 
     List<Pen> pens = new List<Pen>(); 
     for (int i = 0; i < 111; i++) 
      pens.Add(new Pen(Color.FromArgb(R.Next(111), 
        R.Next(111), R.Next(111), R.Next(111)), R.Next(5)/2f)); 

     for (int i = 0; i < 11111; i++) 
      e.Graphics.DrawEllipse(pens[R.Next(pens.Count)], R.Next(211), 
            R.Next(211), 1 + R.Next(11), 1 + R.Next(11)); 
    } 

} 

Наконец мы Скрипт MouseMove из Panel:

private void panel1_MouseMove(object sender, MouseEventArgs e) 
{ 
    mouseCursor = e.Location; 
    if (panelLocked) panel1.Invalidate(); 
} 

используя вторую переменную уровня класса:

Point mouseCursor = Point.Empty; 

Вы называете lockPanel(true) или lockPanel(false) по мере необходимости ..

Если вы реализуете это непосредственно, вы заметите некоторое мерцание. Это уходит, если вы используете двойную буферизацию Panel:

class DrawPanel : Panel 
{ 
    public DrawPanel() { this.DoubleBuffered = true; } 
} 

Это перемещает перекрестие над Panels в совершенно гладко. Вы можете включить & от курсора мыши при MouseLeave и MouseEnter ..

enter image description here

+0

Такой замечательный ответ, и у него не было никаких выплат? Хотел бы я дать больше, чем +1! –

+0

Спасибо. Иногда это может случиться, но поздняя оценка еще приятнее ;-) – TaW

Смежные вопросы