Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Разработка Windows Forms > AutoSize только по одной оси


Автор: Atomic 20.12.2006, 21:25
Нужно, задав жестко полю с текстом (label, textbox или др.) ширину, сделать автоподстройку высоты поля так, чтобы весь текст (мультилайн) был виден без скроллинга.
Из близкого нашел в label свойство AutoSize, но оно работает сразу по обоим осям, что не подходит. 
Может, кто-нибудь знает, как получить желаемое?

Автор: Dmitriy_Donetsk 21.12.2006, 00:40
Возможно решение, которое я предложу не самое оптимальное, но тем 
не менее работает.
Итак, создаем textBox1, устанавливаем Width, который не будет меняться,
далее в зависимости от количества строк в textBox1 (multiline) устанавливаем
высоту (Height):


Код

 private void Form1_Load(object sender, EventArgs e)
        {
            string[] tempArray = new string[textBox1.Lines.Length];
            tempArray = textBox1.Lines;
            //в tempArray.Length - Количество строк в textBox1 

            textBox1.Height = tempArray.Length*14;  
            // 14 - число пикселей для 1 строки,будет 1 лишняя пустая строка,
           // в принципе не страшно, если взять 13, то последнюю строку обрежит -> ставь 14 
        }

Автор: Atomic 21.12.2006, 10:39
В этом решении есть много минусов, но другого и я не вижу.
Только одно но - при уменьшении TextBoxа меньше ширины строки, он переносит текст на следующую. Таким образом количество строк становится неопределенным. Известно только, что их не меньше textBox1.Lines.Length.
После доработки, у меня получился такой вариант:
Код

private void panel2_Resize(object sender, EventArgs e)
{
    textBox1.Width = panel2.Width;//Это я так ширину устанавливаю.
    // А теперь подсчитам, сколько строк у нас реально получается
    int n = 1;// это - кол-во строк. 1 строка есть всегда.
    int k = 0;// а это вспомогательная переменная - кол-во букв в строке
    for (int i = 0; i < textBox1.Text.Length; i++ ) 
        switch (textBox1.Text[i])
        {
            case '\n': n++; k = 0; break;// перенос строки - увеличиваем кол-во строк на 1
            case '\r': break;// второй символ переноса строки - нам и одного хватит. Но убрать его нужно, иначе он будет портить подсчет.
            default: 
                k++;
                if (k * 6 >= this.panel2.Width-6 ) { k = 0; n++; }// если ширина строки достигла максимально допустимой, 
                                                                             // то увеличиваем счетчик строк.
                // здесь k*6 - длинна полученной строки, а вторая 6 - это textbox1.margin.left+...rigth. 
                // Здесь нужно как-то иначе получить ширину символа.
                break;
        }

    textBox1.Height = n * 18;// '14' мне оказалось мало - при уменьшении textBoxа до ~5 символов терялось несколько строк. 
                                 // С '18' строки не теряются, при крайних значениях ширины высота получается очень точно,
                                 // но при средних значениях добавляется несколько лишних строк. Но это не так смертельно.
                                 // А вообще, нужно от куда-то брать эту константу (как и ширину символов), а не подбирать.
}


Но мне почему-то не очень нравится такое решение. Хотелось бы автоматизации.

Автор: Naum 21.12.2006, 10:56
Dmitriy_Donetsk, 

зачем string tempArray, если нужно только количество строк?
Цитата(Dmitriy_Donetsk @  21.12.2006,  01:40 Найти цитируемый пост)
// 14 - число пикселей для 1 строки,будет 1 лишняя пустая строка,
           // в принципе не страшно, если взять 13, то последнюю строку обрежит -> ставь 14 

А если шрифт другой.
Код

using (Graphics g=this.CreateGraphics())
{
   textBox1.Height=textBox1.Lines.Length*g.MeasureString("M",textBox1.Font).Height;
}

А лучше добавить в класс формы поле (например, textHeight) и в событиях Form1_Load и textBox1_FontChanged пересчитывать его. А затем вместо g.MeasureString....... написать textHeight.

Автор: Naum 21.12.2006, 11:35
А можно еще проще
Код

using (Grapgics g=this.CreateGraphics())
{
   textBox1.Heigth=Convert.ToInt32(g.MeasureString(textBox1.Text.TrimEnd(" ".ToCharArray),texBox1.Font).Height);
}


Добавлено @ 11:37 
Можное еще добавить "технологический зазор" +2.

Автор: DarkDragon 21.12.2006, 22:11
Naum, а у меня еще один вариант:
Код

textBox1.Height = textBox1.Lines.Length * textBox1.Font.Height;

 smile 


Автор: DarkDragon 21.12.2006, 22:40
Цитата(Naum @  21.12.2006,  11:35 Найти цитируемый пост)
А можно еще проще

или еще, еще проще   smile 
Код

        private void AutoHeight(Control ctrl)
        {
            using (Graphics g = ctrl.CreateGraphics())
            {
                ctrl.Height = Convert.ToInt32(g.MeasureString(ctrl.Text, ctrl.Font,ctrl.Width).Height) + ctrl.Font.Height;
            }
        }

AutoHeight (label1);

Автор: Atomic 22.12.2006, 00:53
textBox1.Font.Height - это хорошо. Нужно где нибудь записать на салфетке.
А Convert.ToInt32(g.MeasureString(ctrl.Text, ctrl.Font,ctrl.Width).Height даже лучше, потому, что там есть .Width.
Но заметил еще одну гадость - этот нехороший textBox переносит по словам. Соответственно, алгоритм подсчета строк должен быть довольно сложным. Или давать строки с запасом.

Автор: Naum 22.12.2006, 10:20
Цитата(Atomic @  22.12.2006,  01:53 Найти цитируемый пост)
Convert.ToInt32(g.MeasureString(ctrl.Text, ctrl.Font,ctrl.Width).Height даже лучше

Может быть, но считаться быстрее будет конечно textBox1.Font.Heigt.

Автор: DarkDragon 22.12.2006, 15:21
Цитата(Atomic @  22.12.2006,  00:53 Найти цитируемый пост)
Но заметил еще одну гадость - этот нехороший textBox переносит по словам. Соответственно, алгоритм подсчета строк должен быть довольно сложным. Или давать строки с запасом. 

Ф-ция MeasureString - учитывает и WordWrap (перенос слов), но можно его отключить используя StringFormat, устанавливая флаг на NoWrap, но здесь это не нужно. Впринцепе можно пользоваться AutoHeight и на label, textbox и т. д..

Автор: Atomic 24.12.2006, 16:15
Цитата(DarkDragon @  22.12.2006,  15:21 Найти цитируемый пост)
Впринцепе можно пользоваться AutoHeight и на label, textbox и т. д.. 

Да, действительно. И как раз то, что и требовалось. 

Автор: yury_sh 29.1.2007, 15:29
К сожалению метод с MeasureString иногда врет. Не знаю, в чем дело - то ли фонт чуть-чуть не тот, то ли лейбл рисует текст чуть иначе чем функция MeasureString. Кроме того, чтобы его правильно реализовать, нужно учитывать рамку и ...Padding (?). И вообще, он зависит от имплементации лейбла - кто его знает с каким отступом, межстрочным интервалом и т.п. лейбл рисует текст.

Но есть способ еще красивее. Во всяком случае для лейбла работает как из пушки.

Код

int AutoHeight(Label lbl)
{
  lbl.Text = s;
  Size sz = lblHelp.GetPreferredSize(lbl.Size);
  return lblHelp.Height = sz.Height;
}


Документация по GetPreferredSize явно не говорит об этом, но экспериментально выходит, что функция всегда возвращает ширину <= переданной, и высоту необходимую при этой ширине.

Автор: Naum 29.1.2007, 15:50
yury_sh, имхо, заслуженный +1.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)