Скролл в своем контроле
От: AXP  
Дата: 19.08.08 07:22
Оценка:
Всем привет.

Пишу контролл на C# под CF.
Основан на простом Control
Выводит цветной текст по определенному формату.
Рисую все сам.
Данные храню в массиве типа string[] (построчно).
Данные рисуются снизу-вверх (самые новые записи — внизу) в общем, как в командной строке.
Не додумаюсь, как сделать скролл.
Проблема в том, что рисую я только тот текст, что помещается на экран в данный момент + WordWrap.
Вот и как мне посчитать, с какой строки мне начинать рисовать, если есть скролл?

Сори за каламбур, всю ночь писал компонент.

Код компонента ниже


using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Components
{
    public class ANSITextBox : Control
    {
        public ANSITextBox() 
        {
            ;
        }
        Color cBlack = Color.Black;
        Color cRed = Color.Red;
        Color cGreen = Color.Green;
        Color cYellow = Color.Yellow;
        Color cBlue = Color.Blue;
        Color cMagenta = Color.Magenta; //purple
        Color cCyan = Color.Cyan;
        Color cWhite = Color.White;
        Color cGray = Color.Gray;

        public System.Drawing.Color ColorBlack
        {
            get
            {
                return cBlack;
            }
            set
            {
                cBlack = value;
            }
        }
        public System.Drawing.Color ColorRed
        {
            get
            {
                return cRed;
            }
            set
            {
                cRed = value;
            }
        }
        public System.Drawing.Color ColorGreen
        {
            get
            {
                return cGreen;
            }
            set
            {
                cGreen = value;
            }
        }
        public System.Drawing.Color ColorYellow
        {
            get
            {
                return cYellow;
            }
            set
            {
                cYellow = value;
            }
        }
        public System.Drawing.Color ColorBlue
        {
            get
            {
                return cBlue;
            }
            set
            {
                cBlue = value;
            }
        }
        public System.Drawing.Color ColorMagenta
        {
            get
            {
                return cMagenta;
            }
            set
            {
                cMagenta = value;
            }
        }
        public System.Drawing.Color ColorCyan
        {
            get
            {
                return cCyan;
            }
            set
            {
                cCyan = value;
            }
        }
        public System.Drawing.Color ColorWhite
        {
            get
            {
                return cWhite;
            }
            set
            {
                cWhite = value;
            }
        }
        public System.Drawing.Color ColorGrey
        {
            get
            {
                return cGray;
            }
            set
            {
                cGray = value;
            }
        }

        struct CText
        {
            public Color color;
            public string text;
        }

        int pos_y = 0;
        float scroll = 0;
        string iText;
        string[] iLines = new string[0];
        int iMaxLines = 100;

        bool iAutoScroll = false;
        ScrollBars iScrollBars = ScrollBars.None;
        bool iWordWrap = true;
        bool iAnsiColors = true;
        bool iAnsiFormat = true;
        public string[] Lines
        {
            get
            {
                return iLines;
            }
            set
            {
                iLines = value;
            }
        }
        public bool AutoScroll
        {
            get
            {
                return iAutoScroll;
            }
            set
            {
                iAutoScroll = value;
            }
        }
        public ScrollBars ScrollBars
        {
            get
            {
                return iScrollBars;
            }
            set
            {
                iScrollBars = value;
            }
        }
        public bool WordWrap
        {
            get
            {
                return iWordWrap;
            }
            set
            {
                iWordWrap = value;
            }
        }
        public bool AnsiColors
        {
            get
            {
                return iAnsiColors;
            }
            set
            {
                iAnsiColors = value;
            }
        }
        public bool AnsiFormat
        {
            get
            {
                return iAnsiFormat;
            }
            set
            {
                iAnsiFormat = value;
            }
        }
        public int MaxLines
        {
            get
            {
                return iMaxLines;
            }
            set
            {
                iMaxLines = value;
            }
        }

        private Array IncreaseArray(Array array, int newLength)
        {
            Array newArray = Array.CreateInstance(array.GetType().GetElementType(), newLength);
            array.CopyTo(newArray, 0);
            return newArray;
        }

        private Color GetColorById(int color)
        {
            switch (color)
            {
                case 30:
                    //return cBlack;
                    return cGray;
                case 31:
                    return cRed;
                case 32:
                    return cGreen;
                case 33:
                    return cYellow;
                case 34:
                    return cBlue;
                case 35:
                    return cMagenta;
                case 36:
                    return cCyan;
                case 37:
                    return cWhite;
                case 39:
                    return ForeColor;
                case 40:
                    return cBlack;
                case 41:
                    return cRed;
                case 42:
                    return cGreen;
                case 43:
                    return cYellow;
                case 44:
                    return cBlue;
                case 45:
                    return cMagenta;
                case 46:
                    return cCyan;
                case 47:
                    return cWhite;
                case 49:
                    return BackColor;
            }
            return ForeColor;
        }

        private bool isNumeric(string stringToCheck)
        {
            System.Text.RegularExpressions.Regex _isNumber =
                                    new System.Text.RegularExpressions.Regex
                                    (@"(^[-+]?\d+(,?\d*)*\.?\d*([Ee][-+]\d*)?$)|(^[-+]?\d?(,?\d*)*\.\d+([Ee][-+]\d*)?$)");
            return _isNumber.Match(stringToCheck).Success;
        }

        private string TrimSpecialChars(string str)
        {
            int pos;
            while ( (pos = str.IndexOf('\0')) > 0)
                str = str.Remove(pos, 1);
            return str;
        }

        private void AddLine(string str)
        {
            if (iLines.Length <= iMaxLines)
            {
                iLines = (string[])IncreaseArray(iLines, iLines.Length + 1);
            }
            else
            {
                for (int i = 0; i < iLines.Length-1; i++)
                    iLines[i] = iLines[i + 1];
            }
            iLines[iLines.Length-1] = str;
        }

        public void AddText(string str)
        {
            str = TrimSpecialChars(str);
            iText += str;
            char c_n = '\n';
            int pos;
            while ((pos = str.IndexOf(c_n)) > 0)
            {
                AddLine(str.Substring(0, pos + 1));
                str = str.Substring(pos + 1);
            }
            if (str != "") AddLine(str);
            Draw();
        }
        private ArrayList FormLine(int id)
        {
            string str = iLines[id];
            ArrayList list = new ArrayList();
            CText ct = new CText();
                ct.color = this.ForeColor;
            char c = (char)27;
            int pos, last = -1, pos_m = -1, pos1;
            string ctmp = "";
            list = new ArrayList();

            while ((pos = str.IndexOf(c)) > -1)
            {
                if (last == pos) break;
                last = -1;
                if (str.Length > pos + 1 && str[pos + 1] == '[')
                {
                    ct.text = str.Substring(0, pos);
                    list.Add(ct);
                    pos_m = str.Substring(pos).IndexOf('m');
                    if (pos_m < 0)
                    {
                        last = pos;
                        continue;
                    }
                    ctmp = str.Substring(pos + 2, pos_m - 2);
                    if (!isNumeric(ctmp.Replace(';','0')))
                    {
                        last = pos;
                        continue;
                    }
                    while ((pos1 = ctmp.IndexOf(';')) > -1)
                    {
                        ct.color = GetColorById(int.Parse(ctmp.Substring(0, pos1)));
                        ctmp = ctmp.Substring(pos1 + 1);
                    }
                    if (ctmp != "")
                        ct.color = GetColorById(int.Parse(ctmp));
                    str = str.Substring(pos + pos_m + 1);
                }
                else
                    last = pos;
            }
            if (str != "")
            {
                ct.text = str;
                list.Add(ct);
            }
            return list;
        }

        private void Draw()
        {
            pos_y = 0;
            SolidBrush b = new SolidBrush(this.BackColor);
            Graphics g = this.CreateGraphics();
            g.FillRectangle(b, 0, 0, this.Width, this.Height);
            float on_y = g.MeasureString("W", this.Font).Height;
            ArrayList line;
            for (int i = iLines.Length - 1; i >= 0; i--)
            {
                if (on_y * pos_y >= this.Height) break;
                line = FormLine(i);
                DrawStr(line, this.Font);
            }
        }
        private void DrawStr(ArrayList str, Font f)
        {
            Graphics g = this.CreateGraphics();

            ArrayList list = new ArrayList();
            ArrayList tlist = new ArrayList();
            CText ct = new CText(), ct1 = new CText();
            string tmp = "", ttmp = "";
            int n = 0;
            while (str.Count > 0)
            {
                ct = (CText)str[0];
                ttmp = ct.text;
                if (g.MeasureString(tmp + ttmp, f).Width <= this.Width)
                {
                    tlist.Add(ct);
                    tmp += ttmp;
                    str.RemoveAt(0);
                }
                else
                {
                    for (n = 1; g.MeasureString(tmp + ttmp, f).Width > this.Width; n++)
                    {
                        ttmp = ct.text.Substring(0, ct.text.Length - n);
                    }
                    ct1.text = ct.text.Substring(0, ct.text.Length - n + 1);
                    ct1.color = ct.color;
                    tlist.Add(ct1);
                    list.Add(tlist);
                    ct.text = ct.text.Substring(ct.text.Length - n + 1);
                    str[0] = ct;
                    tlist = new ArrayList();
                    tmp = "";
                }
            }
            if (tlist.Count > 0) list.Add(tlist);

            float x = 0, y;
            SolidBrush b;
            for (n = list.Count - 1; n >= 0; n--)
            {
                tlist = (ArrayList)list[n];
                SizeF size;
                for (int i = 0; i < tlist.Count; i++)
                {
                    ct = (CText)tlist[i];
                    size = g.MeasureString(ct.text, f);
                    y = this.Height - pos_y * size.Height - size.Height;
                    b = new SolidBrush(ct.color);
                    g.DrawString(ct.text, f, b, x, y);
                    x += size.Width;
                }
                x = 0;
                pos_y++;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Draw();
        }
    } 
}
Re: Скролл в своем контроле
От: Аноним  
Дата: 19.08.08 09:44
Оценка:
Здравствуйте, AXP, Вы писали:

AXP>Всем привет.


AXP>Пишу контролл на C# под CF.

AXP>Основан на простом Control
AXP>Выводит цветной текст по определенному формату.
AXP>Рисую все сам.
AXP>Данные храню в массиве типа string[] (построчно).
AXP>Данные рисуются снизу-вверх (самые новые записи — внизу) в общем, как в командной строке.
AXP>Не додумаюсь, как сделать скролл.
AXP>Проблема в том, что рисую я только тот текст, что помещается на экран в данный момент + WordWrap.
AXP>Вот и как мне посчитать, с какой строки мне начинать рисовать, если есть скролл?

AXP>Сори за каламбур, всю ночь писал компонент.


AXP>}

AXP>[/c#]

если в двух словах
... можно бросить компонетн VScrollBar (вроде так называется), и при увеличении вашего массива увеличивать свойство Maximum, а начинать рисовать с элемента под номером которое возвращает свойство Value...
Re[2]: Вы немного не поняли вопрос...
От: AXP  
Дата: 19.08.08 13:00
Оценка:
А>если в двух словах
А>... можно бросить компонетн VScrollBar (вроде так называется), и при увеличении вашего массива увеличивать свойство Maximum, а начинать рисовать с элемента под номером которое возвращает свойство Value...

Предположим, имеем текст:
----------
строка1
строка2
строка3
строка4
----------

тогда все будем именно так, это легко. А если текст ьудет:
----------
Длиннющая строка 1 которая перенесется на следующую строку
длиннющая строка 2, аналогична первой
строка3
коротенькая 4
----------

То при прорисовке строки 1 и 2 будут занимать место не одной строки а двух (а то и более, в зависимости от длины строки).

Соотв, при скролле надо учитывать и эти "дополнительные" строки. Вот и вопрос — как их лучше учесть?
Я котнечно могу прогнать весь массив и посчитать, но это слишком ресурсоемко тем более для кпк...
Re[3]: Вы немного не поняли вопрос...
От: Аноним  
Дата: 19.08.08 13:47
Оценка:
Здравствуйте, AXP, Вы писали:
у Graphics есть такая функция MeasureString, она покажет сколько места нужно для твоей строки.


А>>если в двух словах

А>>... можно бросить компонетн VScrollBar (вроде так называется), и при увеличении вашего массива увеличивать свойство Maximum, а начинать рисовать с элемента под номером которое возвращает свойство Value...

AXP>Предположим, имеем текст:

AXP>----------
AXP>строка1
AXP>строка2
AXP>строка3
AXP>строка4
AXP>----------

AXP>тогда все будем именно так, это легко. А если текст ьудет:

AXP>----------
AXP>Длиннющая строка 1 которая перенесется на следующую строку
AXP>длиннющая строка 2, аналогична первой
AXP>строка3
AXP>коротенькая 4
AXP>----------

AXP>То при прорисовке строки 1 и 2 будут занимать место не одной строки а двух (а то и более, в зависимости от длины строки).


AXP>Соотв, при скролле надо учитывать и эти "дополнительные" строки. Вот и вопрос — как их лучше учесть?

AXP>Я котнечно могу прогнать весь массив и посчитать, но это слишком ресурсоемко тем более для кпк...
Re[4]: Вы немного не поняли вопрос...
От: Shurik_ Россия  
Дата: 19.08.08 13:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, AXP, Вы писали:

А>у Graphics есть такая функция MeasureString, она покажет сколько места нужно для твоей строки.

Только с жирным и курсивом DrawString и MeasureString криво работает,
лучше использовать TextRenderer.DrawText и TextRenderer.MeasureText
Re[3]: Вы немного не поняли вопрос...
От: Shurik_ Россия  
Дата: 19.08.08 14:07
Оценка:
AXP>То при прорисовке строки 1 и 2 будут занимать место не одной строки а двух (а то и более, в зависимости от длины строки).

AXP>Соотв, при скролле надо учитывать и эти "дополнительные" строки. Вот и вопрос — как их лучше учесть?

AXP>Я котнечно могу прогнать весь массив и посчитать, но это слишком ресурсоемко тем более для кпк...

При добавлении новой строки можно просчитывать сколько она строк будет занимать,
для TextRenderer.MeasureText Graphics не нужен, так что это будет быстро, а
в Paint соответственно просто отрисовывать, будет не ресурсоемко..
Re[4]: Спасибо большое за помощь
От: AXP  
Дата: 19.08.08 19:10
Оценка:
Спасибо большое за помощь! Так и сделаю.
Re[5]: Спасибо большое за помощь
От: Аноним  
Дата: 20.08.08 08:00
Оценка:
Здравствуйте, AXP, Вы писали:

AXP>Спасибо большое за помощь! Так и сделаю.


но при Resize контрола необходимо будет сделать полный пересчет ))
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.