对 自动适应文本内容的文本框控件 的完善

/// <summary>
    /// 自动适应文本大小的文本框控件
    /// 继承自TextBox
    /// 2007 9 30 bug:键盘输入时,并不能保持中心点不变,原因:前面一个实现时,在每次变化时去求前一次的
    /// 保持的位置,长久下来累计误差比较大,因为中间涉及到整数除2的操作,可能会舍入。修改为开始就保存好不变的位置,
    /// 解决了这一问题
    ///
    /// 2007 10 20 终于解决当字体设置为Times New Roman, 10pt时的,并不能完全适应文本大小的问题。
    /// 原因在于TextRenderer.MeasureText使用的Times New Roman, 10pt实际上设置到textbox时为Times New Roman, 9.75pt
    /// 解决方法:获得LOGFONT,并创建一个临时的font来测量大小。
    /// 同时发现textbox和label的处理font的舍入方式并不一样,也即对LOGFONT.lfHeight的处理。
    /// 从而造成最后有时相同的font,label字体的效果和textbox不一样。
    ///
    /// 进一步研究发现:
    ///     textbox 使用 -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72) 计算lfHeight,只有lfheight超过了0.5才进入下一个整数
    ///        也即 -13.4=-13   -13.5=-14
    ///     然而 label不是这样处理的,label一旦发现比-13大一点点就会认为lfheight=-14
    ///  然后,用发射工具看了label的源代码,发现原来是TextRender.DrawText的问题,里面使用了WindowsFont,
    ///  而Winfont去创建font句柄的时候,使用了
    ///    int num = (int) Math.Ceiling((double) ((WindowsGraphicsCacheManager.MeasurementGraphics.DeviceContext.DpiY * size) / 72f));
    ///    this.logFont.lfHeight = -num;
    ///    Math.Ceiling是求得  返回大于或等于指定的小数的最小整数。没有四舍五入,所以会发生以上的情况。
    ///
    ///   而Control.FontHandle采用Font创建句柄时却采用了四舍五入,不过我没有看到明确的代码,因为返回LOGFONT在api
    ///   GdipGetLogFontA中,不过对textbox实际测试时的结果证明了这一点。
    ///
    /// 我想这应该是微软的bug?
    ///
    /// 为了灵活起见,我设定了一个成员变量,表示时候对字体的高度进行四舍五入。
    /// 如果选true那么进行四舍五入,如果选false那么只是取大于等于的整数,类似于label。
    /// </summary>
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Runtime.InteropServices;
using System.Drawing;
namespace Hob.Toolbox.Controls
{
    /// <summary>
    /// 锁定的位置
    /// </summary>
    public enum LockPosition
    {
        LockTopLeft,
        LockTopHCenter,
        LockTopRight,
        LockVCenterRight,
        LockBottomRight,
        LockBottomHCenter,
        LockBottomLeft,
        LockVCenterLeft,
        LockVCenterHCenter
    }
        public class AutoSizeTextBox : TextBox
    {
        #region windows api
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class LOGFONT
        {
            public int lfHeight = 0;
            public int lfWidth = 0;
            public int lfEscapement = 0;
            public int lfOrientation = 0;
            public int lfWeight = 0;
            public byte lfItalic = 0;
            public byte lfUnderline = 0;
            public byte lfStrikeOut = 0;
            public byte lfCharSet = 0;
            public byte lfOutPrecision = 0;
            public byte lfClipPrecision = 0;
            public byte lfQuality = 0;
            public byte lfPitchAndFamily = 0;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string lfFaceName = string.Empty;
        }
        private const uint WM_NCPAINT = 0x0085;
        private const uint WM_PAINT = 0x000F;
        private const int SWP_NOSIZE = 0x0001;
        private const int SWP_NOMOVE = 0x0002;
        private const int SWP_NOZORDER = 0x0004;
        private const int SWP_NOACTIVATE = 0x0010;
        private const int SWP_FRAMECHANGED = 0x0020;  /* The frame changed: send WM_NCCALCSIZE */
        public const uint WM_SETFONT = 0x0030;
        [DllImport("user32.dll", EntryPoint = "GetWindowDC", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern IntPtr GetWindowDC(IntPtr hwnd);
        [DllImport("user32.dll", EntryPoint = "ReleaseDC", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
        [DllImport("gdi32.dll", EntryPoint = "CreateFontIndirect", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CreateFontIndirect(LOGFONT lf);
        [DllImport("gdi32.dll", EntryPoint = "DeleteObject", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool SendMessage(IntPtr hwnd, uint msg, IntPtr wparam, IntPtr lparam);
        #endregion
        public AutoSizeTextBox()
        {
            //默认多行
            base.Multiline = true;
            base.WordWrap = false;
            base.TextAlign = HorizontalAlignment.Center;
            base.BorderStyle = BorderStyle.FixedSingle;
            m_LockPosition = LockPosition.LockTopLeft;
            m_FrameColor = Color.Gray;
            m_unchangleLocationInit = false;
            m_RoundFontHeight=true;
            m_FontHandleWhenNotRound = IntPtr.Zero;
        }
        /// <summary>
        /// 不变化的位置坐标
        /// </summary>
        private Point m_unchangeLocation;
        bool m_unchangleLocationInit;//是否已经初始化m_unchangeLocation
        /// <summary>
        /// 调整大小
        /// </summary>
        private void AdjustSize()
        {
            ClientSize = GetRightClientSize();
            //调整大小
            if (!m_unchangleLocationInit) return;
            switch (m_LockPosition)//保持位置
            {
                case LockPosition.LockTopLeft:
                    Location = m_unchangeLocation;
                    break;
                case LockPosition.LockTopHCenter:
                    Location = new Point(m_unchangeLocation.X - Size.Width / 2, m_unchangeLocation.Y);
                    break;
                case LockPosition.LockTopRight:
                    Location = new Point(m_unchangeLocation.X - Size.Width, m_unchangeLocation.Y);
                    break;
                case LockPosition.LockVCenterRight:
                    Location = new Point(m_unchangeLocation.X - Size.Width, m_unchangeLocation.Y - Size.Height / 2);
                    break;
                case LockPosition.LockBottomRight:
                    Location = new Point(m_unchangeLocation.X - Size.Width, m_unchangeLocation.Y - Size.Height);
                    break;
                case LockPosition.LockBottomHCenter:
                    Location = new Point(m_unchangeLocation.X - Size.Width / 2, m_unchangeLocation.Y - Size.Height);
                    break;
                case LockPosition.LockBottomLeft:
                    Location = new Point(m_unchangeLocation.X, m_unchangeLocation.Y - Size.Height);
                    break;
                case LockPosition.LockVCenterLeft:
                    Location = new Point(m_unchangeLocation.X, m_unchangeLocation.Y - Size.Height / 2);
                    break;
                case LockPosition.LockVCenterHCenter:
                    Location = new Point(m_unchangeLocation.X - Size.Width / 2, m_unchangeLocation.Y - Size.Height / 2);
                    break;
            }
           
            InvalidateFrame();
        }
        /// <summary>
        /// 重绘边框
        /// </summary>
        private void InvalidateFrame()
        {
            if (BorderStyle == BorderStyle.Fixed3D)
                SetWindowPos(Handle, (IntPtr)0, 0, 0, 0, 0,
                 SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
        }
        /// <summary>
        /// 得到合适的客户区大小
        /// </summary>
        /// <returns></returns>
        private Size GetRightClientSize()
        {
            Size size = GetRightTextSize();
            int borderwidth = GetBorderWidth();
            size.Width += borderwidth * 2;
            size.Height += borderwidth * 2;
            return size;
        }
        /// <summary>
        /// 得到合适的大小(客户区和边框一起)
        /// </summary>
        /// <returns></returns>
        private Size GetRightSize()
        {
            Size size = GetRightClientSize();
            int borderwidth = GetBorderWidth();
            size.Width += borderwidth * 2;
            size.Height += borderwidth * 2;
            return size;
        }
        /// <summary>
        /// 当文本改变时
        /// </summary>
        /// <param name="e"></param>
        protected override void OnTextChanged(EventArgs e)
        {
            //call base
            base.OnTextChanged(e);
            //set auto size
            AdjustSize();
        }
        /// <summary>
        /// 字体变化
        /// </summary>
        /// <param name="e"></param>
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            AdjustSize();
        }
        /// <summary>
        /// 得到合适的尺寸
        /// </summary>
        /// <returns></returns>
        private Size GetRightTextSize()
        {
            String str = Text;
            int len = str.Length;
            if (len<2) str = "t";//至少一个宽度
            int count = Lines.Length;
            if (count >= 1)
                if (Lines[count - 1] == "")
                    str += "t";
            Size size;
            if(m_RoundFontHeight)
            {
                //采用了四舍五入,但是TextRenderer.MeasureText不是四舍五入来计算的
                //所以我们要传给他正确的font
                LOGFONT logfont = new LOGFONT();
                Font.ToLogFont(logfont);
                Font measureFont = Font.FromLogFont(logfont);
                size = TextRenderer.MeasureText(str, measureFont);
                measureFont.Dispose();
            }
            else
                size=TextRenderer.MeasureText(str, Font);
           
            return size;
        }
        //border改变
        protected override void OnBorderStyleChanged(EventArgs e)
        {
            base.OnBorderStyleChanged(e);
            AdjustSize();
        }
        /// <summary>
        /// 得到边框宽度
        /// </summary>
        /// <returns></returns>
        private int GetBorderWidth()
        {
            if (BorderStyle == BorderStyle.Fixed3D)
                return 2;
            else if (BorderStyle == BorderStyle.FixedSingle)
                return 1;
            else //none
                return 0;
        }
        //隐藏Multiline
        [Browsable(false)]
        public override bool Multiline
        {
            get
            {
                return base.Multiline;
            }
            set
            {
                base.Multiline = value;
            }
        }
        //隐藏WordWrap
        [Browsable(false)]
        public new bool WordWrap
        {
            get
            {
                return base.WordWrap;
            }
            set
            {
                base.WordWrap = value;
            }
        }
        [DefaultValue(HorizontalAlignment.Center)]//默认值设为Center,并且构造函数为center
        public new HorizontalAlignment TextAlign
        {
            get
            {
                return base.TextAlign;
            }
            set
            {
                base.TextAlign = value;
            }
        }
        [DefaultValue(BorderStyle.FixedSingle)]
        public new BorderStyle BorderStyle
        {
            get
            {
                return base.BorderStyle;
            }
            set
            {
                base.BorderStyle = value;
            }
        }
        private Color m_FrameColor;
        [Category("Appearance"), Description("边框的颜色"), DefaultValue(typeof(Color), "Gray")]
        public Color FrameColor
        {
            get
            {
                return m_FrameColor;
            }
            set
            {
                m_FrameColor = value;
                InvalidateFrame();
            }
        }
        /// <summary>
        /// 重载wndproc
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref   Message m)
        {
            //字体不采用四舍五入时,那么我们纠正字体
            if (!m_RoundFontHeight && m.Msg == WM_SETFONT)
                m.WParam = CreateFontHandleWhenNotRound();
            base.WndProc(ref m);
            if (m.Msg == WM_PAINT)
                DrawFixedSingle();
            if (m.Msg == WM_NCPAINT)
                DrawFixed3D();
        }
        /// <summary>
        /// 绘制非客户区边框
        /// </summary>
        private void DrawFixed3D()
        {
            if (BorderStyle == BorderStyle.Fixed3D)
            {
                Pen pen = new Pen(this.m_FrameColor);
                IntPtr windc = GetWindowDC(Handle);
                Graphics borderG = Graphics.FromHdc(windc);
                borderG.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
                ReleaseDC(Handle, windc);
                borderG.Dispose();
                pen.Dispose();
            }
        }
        /// <summary>
        /// 绘制客户区边框
        /// </summary>
        private void DrawFixedSingle()
        {
            if (BorderStyle == BorderStyle.FixedSingle)
            {
                Pen pen = new Pen(this.m_FrameColor);
                Graphics dc = Graphics.FromHwnd(Handle);
                dc.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
                dc.Dispose();
                pen.Dispose();
            }
        }
        //锁定的不变位置
        private LockPosition m_LockPosition;
        [Category("Layout"), Description("锁定的不变位置"), DefaultValue(LockPosition.LockTopLeft)]
        //修改名字为TextLockPosition,使得InitializeComponent在text改变后加载TextLockPosition属性
        public LockPosition TextLockPosition
        {
            get
            {
                return m_LockPosition;
            }
            set
            {
                m_LockPosition = value;
                UpdateunchangeLocation();
            }
        }
        /// <summary>
        /// 更新不变的位置
        /// </summary>
        private void UpdateunchangeLocation()
        {
            //求不变的位置坐标
            switch (m_LockPosition)//求保持的位置
            {
                case LockPosition.LockTopLeft:
                    m_unchangeLocation = Location;
                    break;
                case LockPosition.LockTopHCenter:
                    m_unchangeLocation = new Point(Location.X + Size.Width / 2, Location.Y);
                    break;
                case LockPosition.LockTopRight:
                    m_unchangeLocation = new Point(Location.X + Size.Width, Location.Y);
                    break;
                case LockPosition.LockVCenterRight:
                    m_unchangeLocation = new Point(Location.X + Size.Width, Location.Y + Size.Height / 2);
                    break;
                case LockPosition.LockBottomRight:
                    m_unchangeLocation = new Point(Location.X + Size.Width, Location.Y + Size.Height);
                    break;
                case LockPosition.LockBottomHCenter:
                    m_unchangeLocation = new Point(Location.X + Size.Width / 2, Location.Y + Size.Height);
                    break;
                case LockPosition.LockBottomLeft:
                    m_unchangeLocation = new Point(Location.X, Location.Y + Size.Height);
                    break;
                case LockPosition.LockVCenterLeft:
                    m_unchangeLocation = new Point(Location.X, Location.Y + Size.Height / 2);
                    break;
                case LockPosition.LockVCenterHCenter:
                    m_unchangeLocation = new Point(Location.X + Size.Width / 2, Location.Y + Size.Height / 2);
                    break;
            }
            m_unchangleLocationInit = true;
        }
        //重载禁止调整高度
        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
        {
            Size size = GetRightSize();
            if (width != size.Width && ((specified & BoundsSpecified.Width) == BoundsSpecified.Width))
                width = size.Width;
            if (height != size.Height && ((specified & BoundsSpecified.Height) == BoundsSpecified.Height))
                height = size.Height;
            //call base
            base.SetBoundsCore(x, y, width, height, specified);  
        }
        /// <summary>
        /// 覆盖Location
        /// </summary>
        public new Point Location
        {
            get
            {
                return base.Location;
            }
            set
            {
                base.Location = value;
                UpdateunchangeLocation();
            }
        }
        private bool m_RoundFontHeight;
        /// <summary>
        /// 是否对字体高度进行四舍五入
        /// </summary>
        [Category("Appearance"), Description("是否对字体高度进行四舍五入"), DefaultValue(true)]
        public bool RoundFontHeight
        {
            get { return m_RoundFontHeight; }
            set
            {
                m_RoundFontHeight = value;
                if (!value)
                    SetWindowFont();
                //set auto size
                AdjustSize();
            }
        }
        //不采用四舍五入时的font句柄
        private IntPtr m_FontHandleWhenNotRound;
        private IntPtr CreateFontHandleWhenNotRound()
        {
            if(m_FontHandleWhenNotRound==IntPtr.Zero)
            {
                LOGFONT logfont=new LOGFONT();
                Font.ToLogFont(logfont);
                Graphics dc=Graphics.FromHwnd(Handle);
                //求字体高度
                int num = (int)Math.Ceiling(Font.Size*dc.DpiX/ 72f);
                dc.Dispose();
                logfont.lfHeight=-num;
                m_FontHandleWhenNotRound=CreateFontIndirect(logfont);
            }
            return m_FontHandleWhenNotRound;
        }
        private void DeleteFontHandleWhenNotRound()
        {
            if (m_FontHandleWhenNotRound != IntPtr.Zero)
            {
                DeleteObject(m_FontHandleWhenNotRound);
                m_FontHandleWhenNotRound = IntPtr.Zero;
            }
        }
        private void SetWindowFont()
        {
            SendMessage(Handle,WM_SETFONT, (IntPtr)0,(IntPtr)0);
        }
        /// <summary>
        /// 重载dispose,释放非托管资源
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            //do something
            if (!IsDisposed)
            {
                if(disposing)
                {
                    //free managed resource
                }
                //free unmanaged resource
                DeleteFontHandleWhenNotRound();
            }
            base.Dispose(disposing);
        }
    }
}

你可能感兴趣的:(.net,职场,textbox,休闲)