Пишу контролл на 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...
А>если в двух словах А>... можно бросить компонетн VScrollBar (вроде так называется), и при увеличении вашего массива увеличивать свойство Maximum, а начинать рисовать с элемента под номером которое возвращает свойство Value...
тогда все будем именно так, это легко. А если текст ьудет:
----------
Длиннющая строка 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>Я котнечно могу прогнать весь массив и посчитать, но это слишком ресурсоемко тем более для кпк...
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, AXP, Вы писали: А>у Graphics есть такая функция MeasureString, она покажет сколько места нужно для твоей строки.
Только с жирным и курсивом DrawString и MeasureString криво работает,
лучше использовать TextRenderer.DrawText и TextRenderer.MeasureText
AXP>То при прорисовке строки 1 и 2 будут занимать место не одной строки а двух (а то и более, в зависимости от длины строки).
AXP>Соотв, при скролле надо учитывать и эти "дополнительные" строки. Вот и вопрос — как их лучше учесть? AXP>Я котнечно могу прогнать весь массив и посчитать, но это слишком ресурсоемко тем более для кпк...
При добавлении новой строки можно просчитывать сколько она строк будет занимать,
для TextRenderer.MeasureText Graphics не нужен, так что это будет быстро, а
в Paint соответственно просто отрисовывать, будет не ресурсоемко..