Ссылка на шрифт .Net DrawString изменяется после вызова - PullRequest
1 голос
/ 28 сентября 2010

Учитывая следующий код. Есть ли потенциал для рисования первого метода DrawString в Arial, а не в Times New Roman?

protected override void  OnPaint(PaintEventArgs pe)
{
    Font f = new Font("Times New Roman", this.TextSize);
    pe.Graphics.DrawString("Test 1", f, Brushes.Black, loc);
    f = new Font("Arial", this.TextSize);
    pe.Graphics.DrawString("Test 2", f, Brushes.Black, loc);
}

У меня проблема с тем, что по сути этот код периодически рисует первую строку не тем шрифтом. Я изменил код, чтобы теперь иметь две статические ссылки на шрифты, но, поскольку мне не удалось воспроизвести код, я не могу быть уверен, исправлена ​​ли проблема или нет.

Примечание: loc - это позиция, которая будет изменена фактическим кодом. Я упустил здесь некоторый код для упрощения

Вот весь метод с моим исправлением. Если вы не видите в этом ничего плохого - я обвиню в каких-то космических лучах или что-то ...

protected override void  OnPaint(PaintEventArgs pe)
{
       base.OnPaint(pe);
        if (bcf == null)
        {
            FontFamily[] families = pfc.Families;
            foreach (FontFamily ff in families)
            {
                if (ff.Name.Equals("Free 3 of 9"))
                {
                    bcf = ff;
                }
            }
        }
        if (bcf != null)
        {
            Font f = new Font(bcf, this.BarcodeSize);
            SizeF s = TextRenderer.MeasureText(barcodeValue, f);
            Rectangle r = pe.ClipRectangle;
            Point loc = new Point(0, 0);
            if (s.Width < r.Width)
            {
                loc.X = (int)Math.Ceiling((r.Width - s.Width) / 2);
            }
            pe.Graphics.DrawString(barcodeValue, f, Brushes.Black, loc);

            float barcodeBottom = s.Height + 5;

            Font fp = new Font("Arial", this.TextSize);
            s = TextRenderer.MeasureText(barcodeValue, fp);
            r = pe.ClipRectangle;
            loc = new Point(0, (int)Math.Ceiling(barcodeBottom));
            if (s.Width < r.Width)
            {
                loc.X = (int)Math.Ceiling((r.Width - s.Width) / 2);
            }
            if (s.Height + loc.Y > r.Height)
            {
                loc.Y = (int)Math.Ceiling(r.Height - (s.Height));
            }
            pe.Graphics.FillRectangle(Brushes.White, new Rectangle(loc, new Size((int)Math.Ceiling(s.Width), (int)Math.Ceiling(s.Height))));
            pe.Graphics.DrawString(barcodeValue, fp, Brushes.Black, loc);
        }
    }

Исправленный код теперь выглядит следующим образом. Сейчас гораздо меньше GDI-звонков:

protected override void  OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
        if (bcf != null)
        {
            Rectangle r = pe.ClipRectangle;
            Point loc = new Point(0, 0);
            if (barcodeDimensions.Width < r.Width)
            {
                loc.X = (int)Math.Ceiling((r.Width - barcodeDimensions.Width) / 2);
            }
            pe.Graphics.DrawString(barcodeValue, barcodeFont, Brushes.Black, loc);

            float barcodeBottom = barcodeDimensions.Height + 5;

            r = pe.ClipRectangle;
            loc = new Point(0, (int)Math.Ceiling(barcodeBottom));
            if (plaintextDimensions.Width < r.Width)
            {
                loc.X = (int)Math.Ceiling((r.Width - plaintextDimensions.Width) / 2);
            }
            if (plaintextDimensions.Height + loc.Y > r.Height)
            {
                loc.Y = (int)Math.Ceiling(r.Height - (plaintextDimensions.Height));
            }
            pe.Graphics.FillRectangle(Brushes.White, new Rectangle(loc, new Size((int)Math.Ceiling(plaintextDimensions.Width), (int)Math.Ceiling(plaintextDimensions.Height))));
            pe.Graphics.DrawString(barcodeValue, plaintextFont, Brushes.Black, loc);
        }
    }

Если бы я планировал сделать это еще более оптимальным, я бы поместил измерительные части прямоугольника в переопределение OnResize, но я думаю, что пока это подойдет ...

Ответы [ 4 ]

2 голосов
/ 28 сентября 2010

Да, странные вещи начинают происходить, когда ваша программа близка к потреблению 10 000 дескрипторов GDI.Это почти наверняка влияет на отображение шрифтов Windows, не вызывая исключений.Ваша программа играет в русскую рулетку с этой потенциальной проблемой, потому что вы не вызываете Dispose () для используемых вами шрифтов.Если сборщик мусора не запускается достаточно часто, вы можете заставить этот пистолет выстрелить.Вам нужно написать это так:

using (Font fp = new Font("Arial", this.TextSize)) {
    // etc..
}

Также обратите внимание на еще одну ошибку в вашем коде, вы используете TextRenderer.MeasureText, но рисуете с Graphics.DrawString.Измерение не идентично.Вы должны использовать Graphics.MeasureString.

2 голосов
/ 28 сентября 2010

Нет, я не вижу, как это могло бы произойти - это не то, что первый вызов знает о переменной f - он знает только о ее значении во время вызова DrawString. Аргумент (Font ссылка) передается по значению, а не по ссылке.

Единственный способ, которым я мог представить, что это вызывает проблему, это если объект Graphics запоминает свой «текущий» шрифт (и сбрасывает его при вызове на DrawString), но откладывает фактический рисунок. Это имело бы всевозможные неприятные последствия - я не вижу, чтобы это происходило.

По сути, что касается вызовов DrawString, это как если бы вы использовали две разные переменные.

0 голосов
/ 28 сентября 2010

Не вижу проблем с предоставленным кодом. Возможно ли в вашем полном коде неправильное расположение чертежей?

Вы на 100% уверены, что строковое значение, отображаемое в Arial, может быть воспроизведено только при вызове DrawString с использованием шрифта TNR?

0 голосов
/ 28 сентября 2010

Скит накрыл базовый корпус.Я много занимался GDI + и никогда не видел описанного вами поведения.Скорее всего, это связано с каким-то другим эффектом, не показанным в этом коде.

...