Pertanyaan Cara mendapatkan margin teks persis yang digunakan oleh TextRenderer


System.Windows.Forms.TextRenderer.DrawText metode membuat teks berformat dengan atau tanpa padding kiri dan kanan tergantung pada nilai dari flags parameter:

  • TextFormatFlags.NoPadding - paskan teks dengan erat ke kotak pembatas,
  • TextFormatFlags.GlyphOverhangPadding - Menambahkan beberapa margin kiri dan kanan,
  • TextFormatFlags.LeftAndRightPadding - Menambah marjin yang lebih besar.

Sekarang, pertanyaan saya adalah bagaimana saya bisa mendapatkan jumlah padding yang tepat (kiri dan kanan) ditambah DrawText ke teks untuk konteks perangkat tertentu, string, font dll?

Saya telah menggali .NET 4 dengan .NET Reflector dan menemukan bahwa TextRenderer menghitung "overhang padding" yang merupakan 1/6 dari tinggi font dan kemudian mengalikan nilai ini untuk menghitung margin kiri dan kanan menggunakan koefisien ini:

  • kiri 1.0, kanan 1,5 untuk TextFormatFlags.GlyphOverhangPadding,
  • kiri 2,0, kanan 2,5 untuk TextFormatFlags.LeftAndRightPadding.

Nilai yang dihasilkan dibulatkan dan diteruskan ke DrawTextExA atau DrawTextExW fungsi API asli. Sulit untuk membuat ulang proses ini karena tinggi font diambil bukan dari System.Drawing.Font tapi dari System.Windows.Forms.Internal.WindowsFont dan kelas-kelas ini mengembalikan nilai yang berbeda untuk font yang sama. Dan banyak kelas BCL internal lainnya dari System.Windows.Forms.Internal namespace dilibatkan. Memusnahkan semua dari mereka dan menggunakan kembali kode mereka di aplikasi saya bukanlah sebuah pilihan, karena itu akan menjadi ketergantungan implementasi NET yang serius. Itu sebabnya saya perlu tahu apakah ada beberapa API publik di WinForms atau setidaknya fungsi Windows mana yang dapat saya gunakan untuk mendapatkan nilai margin kiri dan kanan.


catatan: Saya sudah mencoba TextRenderer.MeasureText dengan dan tanpa padding dan membandingkan hasilnya tetapi itu memberi saya hanya jumlah margin kiri dan kanan dan saya membutuhkannya secara terpisah.


Catatan 2: Jika Anda bertanya-tanya mengapa saya membutuhkan ini: Saya ingin menggambar satu string dengan banyak font / warna. Itu termasuk menelepon DrawText sekali untuk setiap substring dengan format seragam NoPadding pilihan (agar teks tidak menyebar) tetapi saya juga ingin menambahkan secara manual normal GlyphOverhangPadding pada awal dan akhir dari keseluruhan teks multi-format.


27
2017-12-13 11:26


asal


Jawaban:


Nilai yang Anda butuhkan untuk menghitung margin kiri dan kanan adalah TEXTMETRIC.tmHeight, yang memungkinkan untuk menggunakan API Win32.

Namun, saya menemukan bahwa tmHeight hanya ketinggian garis dari sebuah font dalam piksel, sehingga tiga pendekatan ini akan memberi Anda nilai yang sama (Anda dapat menggunakan mana yang Anda suka dalam kode Anda):

int apiHeight = GetTextMetrics(graphics, font).tmHeight;
int gdiHeight = TextRenderer.MeasureString(...).Height;
int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));

Untuk mendapatkan margin kiri dan kanan, kami menggunakan kode yang sama seperti yang dilakukan TextRenderer di bawah kap mesin:

private const float ItalicPaddingFactor = 0.5f;

...

float overhangPadding = (gdiHeight / 6.0f);

//NOTE: proper margins for TextFormatFlags.LeftAndRightPadding flag
//int leftMargin = (int)Math.Ceiling(overhangPadding);
//int rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor));

//NOTE: proper margins for TextFormatFlags.GlyphOverhangPadding flag
int leftMargin = (int)Math.Ceiling(overhangPadding);
int rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor));

Size sizeOverhangPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.GlyphOverhangPadding);
Size sizeNoPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.NoPadding);

int overallPadding = (sizeOverhangPadding.Width - sizeNoPadding.Width);

Sekarang Anda dapat dengan mudah memeriksa itu

(leftMargin + rightMargin) == overallPadding

Hanya untuk diperhatikan:

Saya harus menyelesaikan masalah ini untuk mengimplementasikan fitur "Pencarian Sorotan" dalam Kontrol berbasis ListView yang menggunakan render teks GDI:

enter image description here

Bekerja seperti pesona :)


5
2017-12-13 11:48



Jawaban ini adalah kutipan dari sini - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164

Jika Anda pernah menginginkan Label atau TextBox dalam Formulir Windows yang melakukan sedikit lebih seperti di web, maka Anda mungkin sudah tahu bahwa tidak ada cara intuitif untuk membuat Label atau TextBox secara otomatis menyesuaikan tingginya agar sesuai dengan teks yang dikandungnya . Meskipun mungkin tidak intuitif, itu pasti bukan tidak mungkin.

Dalam contoh ini, saya akan menggunakan TextBox (Anda dapat dengan mudah menggunakan Label) yang digalangkan ke bagian atas formulir. Untuk menggunakan ini, tambahkan aTextBox yang disebut MyTextBox ke formulir, dan atur Dock ke DockStyle.Top. Hubungkan event Resize dari TextBox ke event handler ini.

private void MyTextBox_Resize( object sender, EventArgs e )
{
    // Grab a reference to the TextBox
    TextBox tb = sender as TextBox; 

    // Figure out how much space is used for borders, etc. 
    int chromeHeight = tb.Height - tb.ClientSize.Height;

    // Create a proposed size that is very tall, but exact in width. 
    Size proposedSize = new Size( tb.ClientSize.Width, int.MaxValue );

    // Measure the text using the TextRenderer
    Size textSize = TextRenderer.MeasureText( tb.Text, tb.Font,
        proposedSize, TextFormatFlags.TextBoxControl
         | TextFormatFlags.WordBreak );

    // Adjust the height to include the text height, chrome height,
    // and vertical margin
    tb.Height = chromeHeight + textSize.Height 
        + tb.Margin.Vertical; 
}

Jika Anda ingin mengubah ukuran Label atau TextBox yang tidak digalangkan (misalnya, yang ada di FlowLayoutPanel atau Panel lainnya, atau hanya ditempatkan pada formulir), maka Anda dapat menangani Ubah Ukuran Formulir bahkan, dan hanya memodifikasi Kontrol properti secara langsung.


2
2017-10-28 08:58



Ini mungkin tampak (sangat) kasar, tetapi ini adalah satu-satunya implementasi asli yang dapat saya pikirkan: DrawText menarik ke suatu IDeviceContext, yang diimplementasikan oleh Graphics. Sekarang, kita dapat mengambil keuntungan dari itu dengan kode berikut:

Bitmap bmp = new Bitmap(....);
Graphics graphic = Graphics.FromImage(bmp);
textRenderer.DrawText(graphic,....);
graphic.Dispose();

Dengan yang baru Bitmap Anda bisa pergi pixel dengan pixel dan menghitungnya dengan beberapa kondisi. Sekali lagi, metode ini sangat kasar dan boros, tapi setidaknya itu asli ....

Ini tidak diuji tetapi berdasarkan pada sumber-sumber berikut:

http://msdn.microsoft.com/en-us/library/4ftkekek.aspx

http://msdn.microsoft.com/en-us/library/system.drawing.idevicecontext.aspx

http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx

http://www.pcreview.co.uk/forums/graphics-bitmap-t1399954.html


1
2017-08-14 16:14



Saya telah melakukan hal serupa beberapa tahun yang lalu, untuk menyorot hasil pencarian (pola pencarian muncul dalam huruf tebal, dll.). Implementasi saya di DevExpress, jadi kode mungkin tidak relevan. Jika Anda pikir itu berguna, saya dapat menyalinnya, hanya perlu menemukan penerapan itu.

Di System.Windows.Forms, kelas yang akan digunakan Graphics. Ia memiliki a MeasureCharacterRanges () metode yang menerima a StringFormat (Mulailah dengan GenericTypographic dan pergi dari sana). Ini jauh lebih tepat daripada TextRenderer untuk menampilkan string lengkap dengan merantai bagian-bagian dengan gaya, font atau kuas yang berbeda.

Anda telah melangkah lebih jauh dari saya dengan pengukuran padding yang sebenarnya. Kontrol DevExpress memberi Anda kotak batas teks untuk memulai dengan sehingga dilakukan untuk saya.

Ini sebuah artikel oleh Pierre Arnaud yang muncul untuk saya di Google, yang menyentuh pada area ini. Sayangnya tautan GDI + "Rincian Gory" di sana rusak.

Tepuk tangan,
Jonno


1
2017-08-19 16:24



Perbaikannya adalah menghitung apa MeasureText akan menambahkan:

var formatFlags FormatFlags =
    TextFormatFlags.NoPadding |
    TextFormatFlags.SingleLine;

int largeWidth = TextRenderer.MeasureText(
    "  ",
    font,
    new Size(int.MaxValue, int.MaxValue),
    formatFlags
).Width;
int smallWidth = TextRenderer.MeasureText(
    " ",
    font,
    new Size(int.MaxValue, int.MaxValue),
    formatFlags
).Width;

int extra = smallWidth - (largeWidth - smallWidth);

Kami menghitung lebar satu spasi dan lebar dua spasi. Keduanya memiliki lebar tambahan yang ditambahkan, sehingga kita dapat mengekstrapolasikan lebar ekstra yang sedang ditambahkan. Lebar tambahan tampaknya selalu sama, sehingga mengurangkan extra dari setiap lebar yang dikembalikan oleh MeasureText memberikan hasil yang diharapkan.


0
2017-08-29 05:55