星期日, 9月 04, 2022

[C#] ComboBoxRenderer

根據官方文章 - ComboBoxRenderer 類別 的筆記,練習時發現原本的 Code 在 Font 變化時,ComboBox Size 不會跟著變化,改寫成透過 MeasureString() 來取得字型高度並設定 ComboBox Size

CustomComboBox Code,CustomComboBox 是繼承 Control 來做的喔
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace ComboBoxRendererSample
{
    public class CustomComboBox : Control
    {
        private Size arrowSize;
        private Rectangle arrowRectangle;
        private Rectangle topTextBoxRectangle;    // ComboBox 本身文字顯示
        private Rectangle bottomTextBoxRectangle; // ComboBox 下拉選項文字顯示
        private ComboBoxState textBoxState = ComboBoxState.Normal;
        private ComboBoxState arrowState = ComboBoxState.Normal;

        public Font Font
        {
            get => base.Font;
            set
            {
                base.Font = value;
                Invalidate();
            }
        }

        public CustomComboBox() : base()
        {
            Location = new Point(10, 10);
            Size = new Size(300, 200);
            Font = new Font("微軟正黑體", 20);
            Text = "Click the button";
        }

        // Draw the combo box in the current state.
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (ComboBoxRenderer.IsSupported == false)
            {
                Parent.Text = "Visual Styles Disabled";
                return;
            }

            // ComboBox 高度部分改用 MeasureString() 根據字型來調整
            SizeF stringSizeFormat = e.Graphics.MeasureString(Text, Font);
            int Height = (int)stringSizeFormat.Height;

            arrowSize = new Size(18, Height);

            arrowRectangle = new Rectangle(
                ClientRectangle.X + ClientRectangle.Width - arrowSize.Width - 1,
                ClientRectangle.Y + 1,
                arrowSize.Width,
                Height);

            topTextBoxRectangle = new Rectangle(
                ClientRectangle.X,
                ClientRectangle.Y,
                ClientRectangle.Width,
                Height + 2);

            bottomTextBoxRectangle = new Rectangle(
                ClientRectangle.X,
                ClientRectangle.Y + topTextBoxRectangle.Height,
                ClientRectangle.Width,
                topTextBoxRectangle.Height);

            // Always draw the main text box and drop down arrow in their current states
            ComboBoxRenderer.DrawTextBox(e.Graphics, topTextBoxRectangle, Text, Font, textBoxState);

            ComboBoxRenderer.DrawDropDownButton(e.Graphics, arrowRectangle, arrowState);

            // Only draw the bottom text box if the arrow has been clicked
            if (textBoxState == ComboBoxState.Pressed)
            {
                string bottomText = "Using ComboBoxRenderer";
                ComboBoxRenderer.DrawTextBox(e.Graphics, bottomTextBoxRectangle, bottomText, Font, textBoxState);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (ComboBoxRenderer.IsSupported == false ||
                arrowRectangle.Contains(e.Location) == false)
                return;

            // Draw the arrow in the pressed state.
            arrowState = ComboBoxState.Pressed;

            // The user has activated the combo box.
            if (textBoxState == ComboBoxState.Normal)
            {
                Text = "Clicked!";
                textBoxState = ComboBoxState.Pressed;
            }
            // The user has deactivated the combo box.
            else
            {
                Text = "Click here";
                textBoxState = ComboBoxState.Normal;
            }

            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            if (ComboBoxRenderer.IsSupported == false ||
                arrowRectangle.Contains(e.Location) == false)
                return;

            arrowState = ComboBoxState.Normal;
            Invalidate();
        }
    }
}
CustomComboBox 會隨字型變化 Size 且下拉箭頭會在高度中央,該篇個人重點

[C#] ComboBoxRenderer-1

範例執行初始畫面

[C#] ComboBoxRenderer-2

點擊 TextBox 部分不會有反應,必須點擊下拉箭頭

[C#] ComboBoxRenderer-3

再次點擊下拉箭頭,BottomTextBox (這名稱取的有點奇怪) 會收回

[C#] ComboBoxRenderer-4

星期五, 9月 02, 2022

[C#] 調整 AutoResize 設定來觀察文字顯示效果

根據這兩篇官方文章內容
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Sizing
{
    public partial class Form1 : Form
    {
        // 重置功能使用參數
        private DataGridView dataGridView1;
        private string boringMeatloaf = "ground beef";
        private string boringMeatloafRanking = "*";
        private bool boringRecipe;
        private bool shortMode;
        private string thirdColumnHeader = "Main Ingredients";
        private string otherRestaurant = "Gomes's Saharan Sushi";

        public Form1()
        {
            InitializeComponent();

            InitializeDataGridView();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        #region 初始化相關

        private void btnReset_Click(object sender, EventArgs e)
        {
            Controls.Remove(dataGridView1);
            InitializeDataGridView();
        }

        private void InitializeDataGridView()
        {
            dataGridView1 = new DataGridView();
            dataGridView1.Location = new Point(0, 0);
            dataGridView1.Size = new Size(600, 400);
            Controls.Add(dataGridView1);

            AddColumns();
            PopulateRows();

            shortMode = false;
            boringRecipe = true;

            void AddColumns()
            {
                dataGridView1.ColumnCount = 4;
                dataGridView1.ColumnHeadersVisible = true;

                // 設定 HeaderCell Style
                DataGridViewCellStyle columnHeaderStyle = new DataGridViewCellStyle();
                columnHeaderStyle.BackColor = Color.Aqua;
                columnHeaderStyle.Font = new Font("Verdana", 12, FontStyle.Bold);
                dataGridView1.ColumnHeadersDefaultCellStyle = columnHeaderStyle;

                int colIndex = 0;
                dataGridView1.Columns[colIndex++].Name = "Recipe";
                dataGridView1.Columns[colIndex++].Name = "Category";
                dataGridView1.Columns[colIndex++].Name = thirdColumnHeader;
                dataGridView1.Columns[colIndex++].Name = "Rating";
            }

            void PopulateRows()
            {
                string[] row1 = { "Meatloaf", "Main Dish", boringMeatloaf, boringMeatloafRanking };
                string[] row2 = { "Key Lime Pie", "Dessert", "lime juice, evaporated milk", "****" };
                string[] row3 = { "Orange-Salsa Pork Chops", "Main Dish", "pork chops, salsa, orange juice", "****" };
                string[] row4 = { "Black Bean and Rice Salad", "Salad", "black beans, brown rice", "****" };
                string[] row5 = { "Chocolate Cheesecake", "Dessert", "cream cheese", "***" };
                string[] row6 = { "Black Bean Dip", "Appetizer", "black beans, sour cream", "***" };
                object[] rows = new object[] { row1, row2, row3, row4, row5, row6 };

                foreach (string[] row in rows)
                    dataGridView1.Rows.Add(row);

                foreach (DataGridViewRow row in dataGridView1.Rows)
                {
                    if (row.IsNewRow)
                        break;

                    row.HeaderCell.Value = "Restaurant " + row.Index;
                }
            }
        }

        #endregion

        #region 文字變化相關

        private void btnChangeColumn3Header_Click(object sender, EventArgs e)
        {
            // 設定第三欄位 ColumnHeader 文字說明
            shortMode = !shortMode;
            string headerText = shortMode ? "S" : thirdColumnHeader;
            dataGridView1.Columns[2].HeaderText = headerText;
        }

        private void btnChangeMeatloafRecipe_Click(object sender, EventArgs e)
        {
            // 設定第一筆資料的資料內容
            boringRecipe = !boringRecipe;

            if (boringRecipe)
            {
                SetMeatloaf(boringMeatloaf, boringMeatloafRanking);
            }
            else
            {
                string greatMeatloafRecipe =
                    "1 lb. lean ground beef, "
                    + "1/2 cup bread crumbs, 1/4 cup ketchup,"
                    + "1/3 tsp onion powder, "
                    + "1 clove of garlic, 1/2 pack onion soup mix "
                    + " dash of your favorite BBQ Sauce";

                SetMeatloaf(greatMeatloafRecipe, "***");
            }

            void SetMeatloaf(string recipe, string rating)
            {
                dataGridView1.Rows[0].Cells[2].Value = recipe;
                dataGridView1.Rows[0].Cells[3].Value = rating;
            }
        }

        private void btnChangeRestaurant2_Click(object sender, EventArgs e)
        {
            // 設定第三筆 RowHeader 文字說明
            bool isOtherRestaurant = dataGridView1.Rows[2].HeaderCell.Value.Equals(otherRestaurant);
            string rowHeaderText = isOtherRestaurant ? "Restaurant 2" : otherRestaurant;
            dataGridView1.Rows[2].HeaderCell.Value = rowHeaderText;
        }

        #endregion

        #region AutoResize - Column 相關

        private void btnSizeThirdColumn_Click(object sender, EventArgs e)
        {
            // 設定第三個欄位的 Header 寬度,可以顯示全部內容
            dataGridView1.AutoResizeColumn(2, DataGridViewAutoSizeColumnMode.ColumnHeader);
        }

        private void btnSizeColumnHeaders_Click(object sender, EventArgs e)
        {
            // 設定全部 ColumnHeader 高度
            dataGridView1.AutoResizeColumnHeadersHeight(2);
        }

        private void btnSizeAllColumns_Click(object sender, EventArgs e)
        {
            // 設定全部 Column 寬度可以顯示全部內容
            dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
        }

        #endregion

        #region AutoResize - Row 相關

        private void btnSizeAllRowsandRowHeaders_Click(object sender, EventArgs e)
        {
            // 設定全部 Row 寬度
            dataGridView1.AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells);
        }

        private void btnSizeFirstRowHeaderUsingAllHeaders_Click(object sender, EventArgs e)
        {
            // 根據第一筆資料來設定 RowHeader 寬度
            dataGridView1.AutoResizeRowHeadersWidth(0, DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders);
            // 設定 RowHeader 寬度
            // dataGridView1.AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders);
        }

        private void btnSizeThirdRow_Click(object sender, EventArgs e)
        {
            // 搭配 btnSizeAllRowsandRowHeaders_Click 才看得出效果
            dataGridView1.AutoResizeRow(2, DataGridViewAutoSizeRowMode.AllCellsExceptHeader);
        }

        private void btnSizeAllRows_Click(object sender, EventArgs e)
        {
            // 搭配 btnSizeAllRowsandRowHeaders_Click 才看得出效果
            dataGridView1.AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders);
        }

        #endregion
    }
}
依執行的畫面,後續截圖為了閱讀方便,會把右側按鈕功能截圖處理
文字變化相關,紀錄每個按鈕變化後的文字
Row 相關變化,依按鈕順序由上而下操作,就可以看出每一個功能對於文字變化,單獨某個按鈕會感覺不出文字效果