none
C# Отобразить заливку за текстом RRS feed

  • Вопрос

  • Здравствуйте!

    На форме есть текст нарисованный с помощью метода DrawString. Нужно чтобы фон этого элемента закрашивался в определенный цвет. Но, если это делать после того того, как текст отрисован, то метод FillRectangle этот текст закрашивает. Т.е получается, надо чтобы фон был на заднем плане, а текст-на переднем. Не знаю можно ли сделать так. Всем спасибо за любую помощь.

    26 октября 2012 г. 19:01

Ответы

  • Я рисую в нужном событии?  Если рисовать в OnPaint, то где его вызвать? И можно ли его тут применить, если я элементы рисую сам, а не использую наследуемые от класса CheckedListBox? Про свойство знаю, спасибо.

    Не понимаю зачем вы рисуете в событии клика?

    Рисоватьнужно только в событии _Paint() или OnPaint();

    в событии Click() нужно только установить цвет и координаты заливки и сохранить их в соотв.контроле.

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

    public class MyListControl : UserControl (можно любой другой)
    {
        public List<Color> ColorBox = new List<Color>();
        public List<Point> LocationBox = new List<Point>();
        public List<Size> SizeBox = new List<Size>();
        public List<string> TextValue = new List<string>();
        public int HeightLine = 14;
        public int AddTextValue(string text)
        {
            int n_y= TextValue.Count;
            TextValue.Add(text);
            ColorBox.Add(Colors.White);
            LocationBox.Add(new Point(0,n_y*14));
            SizeBox.Add(new Size(Size.Width,14));
            return TextValue.Count; // возвращаем индекс добавленного текста.
        }
        protected override void OnPaint(PaintEventArgs e)
        {
           base.OnPaint(e);
           Graphics grp = e.Graphics;
           for (int i=0; i<TextValue;i++)
           {
              Rectangle rect = new Rectangle(LocationBox[i], SizeBox[i]);
              RectangleF rectF = new RectangleF(0,i*14,Size.Width,Size.Height);
           grp.FillRectangle(new SolidBrush(ColorBox[i]), rect);
           grp.DrawString(Text, new Font("Times New Roman",10), Colors.Black, rectF);
           }
    } 
    void MyListBox_MouseClick(object sender, MouseEventArgs e)
    {
       int n = e.Y/14;
       if (n>TextValue.Count) {AddTextValue("Y курсора:"+e.Y.ToString());return;} // клик ниже нашего массива строк, добавим 1 строку
       if (e.Y-n*14>7) ColorBox[n] = Colors.Lime; else ColorBox[n] = Red;
       Refresh(); // или Invalidate() как предлагали выше.
    }  

    Вот получился практически работоспособный ListView с расскрасской каждой строки в отдельный цвет.

    и код не такой уж большой и сложный..конечно все упрощено, но под вас его доработать не сложно.


    • Помечено в качестве ответа nik_w 30 октября 2012 г. 12:58
    • Изменено anknown123455 31 октября 2012 г. 10:15
    30 октября 2012 г. 7:25

Все ответы

  • Сначала нарисуйте прямоугольник, а затем по нему напишите текст.
    27 октября 2012 г. 3:28
  • Спасибо, но дело в том, что фон должен закрашиваться по событию клика мыши на этот элемент. Т.е текст там уже будет. Получается нужно будет каждый раз текст прорисовывать заново, после того как будет закрашен фон.
    27 октября 2012 г. 5:21
  • ну так всё равно отрисовка текста и фона идёт в обработчике события Paint.
    там и будет каждый раз всё перерисовываться.
    Иначе вы получите стираемый текст с фоном, при наложении на него чего-нибудь (например окна другого приложения).

    Так и делайте как Вам предложили,
    но в онпейнте.
    Рисуете прямоугольник, потом текст.
    А по кнопке устанавливаете поле какое-нибудь,
    по значению которого уже рисуется (в онпейнте) Ваш прямоугольник.

    Если Вам и нужно, чтобы всё стиралось (т.е. не нужно чтобы текст с прямоугольником не пропадали),
    то рисутете каждый раз всё в том же порядке, и всё.
    • Изменено INFEL8 27 октября 2012 г. 5:55
    27 октября 2012 г. 5:54
  • Я , наверное пошел не совсем по тому пути. Я делаю аналог контрола CheckedListBox, элемент списка этого контрола представляет собой CheckBox и, правее, прямоугольник с текстом и фоном. Менять фон предполагалось по событию MouseClick контрола. Т.е я получаю координаты указатели мыши и определяю какой участок надо закрасить. Или так делать не стоит?
    27 октября 2012 г. 6:06
  • да можно наверное,
    просто закрашивать я почти всегда рекомендую в событии перерисовки контрола а не по клику

    по клику просто координаты те определять и всё

    27 октября 2012 г. 7:40
  • И вызывать это событие нужно будет методом OnPaint в обработчике события MouseClick?
    27 октября 2012 г. 7:56
  • нет, оно само происходит, на него нужно только подписаться для того контрола.
    там в "e" придёт графикс, и на нём рисовать, его диспозить только не надо, как было если через креатеГрафикс.
    а на клике получаете ваши координаты или чего там.

    в онпейнте уже с ними работаете.
    27 октября 2012 г. 8:09
  • Я так делаю в обработчике Paint класса моего контрола. Больше никаких событий, кроме MouseClick, нету. Т.е событие Paint контрола происходит несколько раз при запуске приложения, потом оно уже не происходит, поэтому я и хотел рисовать по клику. Не совсем понимаю, где должен быть метод OnPaint, который Вы упоминаете.
    27 октября 2012 г. 8:18
  • событие пейнт происходит всегда когда надо перерисовать.
    потому там и лучше рисовать.

    вы попробуйте по вашему контролу провести другим окном или свернуть/развернуть окно,
    и Ваш прямоугольник с текстом пропадёт


    • Изменено INFEL8 27 октября 2012 г. 8:49
    27 октября 2012 г. 8:49
  • Да, если свернуть/развернуть окошко, то фон пропадает. Получается, что закрашивать надо в момент клика(чтобы отобразить выделение) и в такие моменты когда окошко сворачивается/разворачивается, чтобы не потерять это выделение.
    27 октября 2012 г. 9:01
  • нет, в момент клика ни чего красить не надо.

    по клику надо установить параметры по которым рисовать и вызвать для контрола Invalidate()

    в остальные моменты само перерисуется если рисовать в онпейнте


    • Изменено INFEL8 27 октября 2012 г. 9:59
    27 октября 2012 г. 9:53
  • Как я понимаю, речь идёт о WinForms.

    Самый простой способ: унаследоваться от стандартного контрола, и изменить его поведение. Именно это имел в виду INFEL8, упоминая OnPaint.

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

    Приведу простой пример:

    public class CheckListBox : CheckedListBox
    {
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            Color backColor = e.Index % 2 == 0 ? Color.Pink : Color.Plum;
    
            var args = new DrawItemEventArgs(
                e.Graphics,
                e.Font,
                e.Bounds,
                e.Index,
                e.State,
                e.ForeColor,
                backColor);
    
            base.OnDrawItem(args);
        }
    }

    Здесь наш класс CheckListBox унаследован от родного дотнетного, и в нём переопределён метод OnDrawItem, который отвечает за отрисовку отдельных элементов. В данном примере чётные будут закрашиваться одним цветом, нечётные - другим. Получился полосатик :).

    Обработку клика мышкой логично делать в перегруженном методе OnMouseClick:

    protected override void OnMouseClick(MouseEventArgs e)
    {
        // здесь наш код
    }

    Конечно, не всегда таким способом можно добиться нужного результата, и тогда придётся писать полностью свой контрол с нуля.


    • Изменено Petalvik 27 октября 2012 г. 10:42
    27 октября 2012 г. 10:36
  • Мой класс наследует от UserControl, я хотел сделать так, чтобы в моем контроле можно было изменять цвет фона и цвет шрифта определенного элемента, и, самое главное, чтобы можно было самому управлять переносом элементов списка в другой столбец. Ведь, если у стандартного контрола CheckedListBox установить свойство MultiColumn, то столбцы будут, но перенос будет осуществляться автоматически. Поэтому и решил сделать свой, а не менять поведение уже существующего, хотя, похоже будет велосипед.

    Petalvik, INFEL8 спасибо за помощь, попробую пока сам что-нибудь сделать.

    27 октября 2012 г. 10:59
  • Ну я вообще имел ввиду просто на событие Paint подписаться,
    но это для всего контрола наверное,
    разбираться долго, включать студию и тд..

    может и дравитемом можно
    27 октября 2012 г. 12:07
  • Советую топикстартеру один раз сделать как предложили в самом начале.

    Это самый верный путь.

    красить контролы в месте отличном от события перерисовки не верное направление.

    вопрос: почему Invalidate(), я всегда и везде вызвыал Refresh()..

    В чем прелесть Invalidate() ?

    29 октября 2012 г. 3:58
  • Особо не знаю, но мне всегда казалось, что Инвалидате() производительнее обычно.
    не нужно все дочерние элементы перерисовывать, а только сам контрол или указанный регион.
    • Изменено INFEL8 29 октября 2012 г. 7:54
    29 октября 2012 г. 7:53
  • Особо не знаю, но мне всегда казалось, что Инвалидате() производительнее обычно.
    не нужно все дочерние элементы перерисовывать, а только сам контрол или указанный регион.

    Извините за оффтоп, Вы на 1С не программируете?

    "Control.Invalidate (): basically instruct the OS to redraw the contol. This does not happen immediatly.
    Control.Refresh (). forces an immediate redraw of the control (and its children) " Отсюда

    29 октября 2012 г. 8:19
  • Нет, не программирую.
    раскладку переключать не хочу просто,
    и пишу как пишется, а не как читается.
    29 октября 2012 г. 8:44
  • Добрый день! Получилось кое-что сделать, а именно выделение элемента по клику и установку/снятие галки у CheckBox. Последнее, правда, почему-то перестало работать. Вот код:

    class MyListBox : UserControl
        {
            Graphics g;
            public List<object> Items;
            CheckBox[] check_box;
            int max_height;
            int selected_index;
            int previous_selected_index;
            int previous_Y;
    
            public MyListBox()
            {
                Items = new List<object>();
    
                this.Paint += MyListBox_Paint;
                this.BackColor = Color.White;
                this.MouseClick += MyListBox_MouseClick;
                previous_selected_index = -1;
            }
    
            void MyListBox_MouseClick(object sender, MouseEventArgs e)
            {
                if (this.Items.Count < 1) return;
                if (e.Y > max_height) return;
    
                g = this.CreateGraphics();
                int Y = GetPoint(e.Y, out selected_index);
    
                if (selected_index != previous_selected_index)
                {
                    g.FillRectangle(new SolidBrush(SystemColors.Highlight), new Rectangle(16, Y, 100, 14));
                    g.DrawString(Items[selected_index].ToString(), Font, new SolidBrush(Color.White),
                         new PointF(this.Padding.Left + 17, this.Padding.Top - (this.Font.Size / 4) + Y + 2));
    
                    if (previous_selected_index != -1)
                    {
                        g.FillRectangle(new SolidBrush(Color.White), new Rectangle(16, previous_Y, 100, 14));
                        g.DrawString(Items[previous_selected_index].ToString(), Font, new SolidBrush(SystemColors.WindowText),
                             new PointF(this.Padding.Left + 17, this.Padding.Top - (this.Font.Size / 4) + previous_Y + 2));
                    }
    
                    if (check_box[selected_index].CheckState == CheckState.Checked)
                    {
                        check_box[selected_index].Checked = false;
                    }
                    else
                    {
                        check_box[selected_index].Checked = true;
                    }
                    previous_selected_index = selected_index;
                    previous_Y = Y;
                }
                else
                {
                    if (check_box[selected_index].CheckState == CheckState.Checked)
                    {
                        check_box[selected_index].Checked = false;
                    }
                    else
                    {
                        check_box[selected_index].Checked = true;
                    }
                }
            }
            /// <summary>
            /// Рисую элементы коллекции Items, а также рамку вокруг элемента.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void MyListBox_Paint(object sender, PaintEventArgs e)
            {
                check_box = new CheckBox[Items.Count];
                int y = 4;
                g = e.Graphics;
                Pen pen = new Pen(Color.Gray);
                pen.Width = 1.0F;
                e.Graphics.DrawRectangle(pen, new Rectangle(this.Location.X - 10, this.Location.Y - 10, this.Size.Width - 1, this.Size.Height - 1));
    
                for (int i = 0; i < Items.Count; i++, y += 15)
                {
                    check_box[i] = new CheckBox();
                    check_box[i].Location = new Point(this.Location.X - 8, y);
                    check_box[i].Size = new Size(13, 13);
    
                    e.Graphics.DrawString(Items[i].ToString(), Font, new SolidBrush(SystemColors.WindowText),
                      new PointF(this.Padding.Left + 17, this.Padding.Top - (this.Font.Size / 4) + y + 2));
                    this.Controls.Add(check_box[i]);
                }
    
                max_height = y;
            }
    
            /// <summary>
            /// По полученной координате Y положения мыши, определяю координату Y области, где требуется залить фон.
            /// Высота заливки-14.
            /// </summary>
            /// <param name="y"></param>
            /// <param name="selected_index"></param>
            /// <returns></returns>
            int GetPoint(int y, out int selected_index)
            {
                selected_index = 0;
                int _y;
                int k = 4;
    
                for (int i = 18, j = 0; i < max_height; i += 18, j++)
                {
                    if ((Math.Abs(i - y)) < (Math.Abs((i - 18) - y)))
                    {
                        k = i;
                        selected_index++;
                    }
                }
    
                if (k < y)
                {
                    _y = k - 14;
                    _y = k;
                }
                else
                {
                    _y = k;
                    _y = k - 14;
                }
    
                if (_y == 4) { selected_index = 0; }
                return _y;
            }
        }

    29 октября 2012 г. 9:22
  • Я тут такую штуку вспомнил,
    у контролов типа этого, есть свойство DrawMode, и ему ставить можно OwnerDrawFixed,
    думаю это можно у Вас как-то использовать.
    Это так, дополнение.
    29 октября 2012 г. 10:05
  • Я рисую в нужном событии?  Если рисовать в OnPaint, то где его вызвать? И можно ли его тут применить, если я элементы рисую сам, а не использую наследуемые от класса CheckedListBox? Про свойство знаю, спасибо.
    29 октября 2012 г. 10:39
  • если по событию рисовать, то приблизительно правильно. (я бы убрал рисование из клика).

    а если OnPaint, то это нужно делать как Petalvik сказал до того как про рисование элемента код выложен.

    • Изменено INFEL8 29 октября 2012 г. 12:56
    29 октября 2012 г. 12:55
  • Я рисую в нужном событии?  Если рисовать в OnPaint, то где его вызвать? И можно ли его тут применить, если я элементы рисую сам, а не использую наследуемые от класса CheckedListBox? Про свойство знаю, спасибо.

    Не понимаю зачем вы рисуете в событии клика?

    Рисоватьнужно только в событии _Paint() или OnPaint();

    в событии Click() нужно только установить цвет и координаты заливки и сохранить их в соотв.контроле.

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

    public class MyListControl : UserControl (можно любой другой)
    {
        public List<Color> ColorBox = new List<Color>();
        public List<Point> LocationBox = new List<Point>();
        public List<Size> SizeBox = new List<Size>();
        public List<string> TextValue = new List<string>();
        public int HeightLine = 14;
        public int AddTextValue(string text)
        {
            int n_y= TextValue.Count;
            TextValue.Add(text);
            ColorBox.Add(Colors.White);
            LocationBox.Add(new Point(0,n_y*14));
            SizeBox.Add(new Size(Size.Width,14));
            return TextValue.Count; // возвращаем индекс добавленного текста.
        }
        protected override void OnPaint(PaintEventArgs e)
        {
           base.OnPaint(e);
           Graphics grp = e.Graphics;
           for (int i=0; i<TextValue;i++)
           {
              Rectangle rect = new Rectangle(LocationBox[i], SizeBox[i]);
              RectangleF rectF = new RectangleF(0,i*14,Size.Width,Size.Height);
           grp.FillRectangle(new SolidBrush(ColorBox[i]), rect);
           grp.DrawString(Text, new Font("Times New Roman",10), Colors.Black, rectF);
           }
    } 
    void MyListBox_MouseClick(object sender, MouseEventArgs e)
    {
       int n = e.Y/14;
       if (n>TextValue.Count) {AddTextValue("Y курсора:"+e.Y.ToString());return;} // клик ниже нашего массива строк, добавим 1 строку
       if (e.Y-n*14>7) ColorBox[n] = Colors.Lime; else ColorBox[n] = Red;
       Refresh(); // или Invalidate() как предлагали выше.
    }  

    Вот получился практически работоспособный ListView с расскрасской каждой строки в отдельный цвет.

    и код не такой уж большой и сложный..конечно все упрощено, но под вас его доработать не сложно.


    • Помечено в качестве ответа nik_w 30 октября 2012 г. 12:58
    • Изменено anknown123455 31 октября 2012 г. 10:15
    30 октября 2012 г. 7:25
  • Спасибо, буду пробовать.
    30 октября 2012 г. 12:58