Commit 6fa58cee 顾剑亮

upload

1 个父辈 a2a230ca
正在显示 209 个修改的文件 包含 4858 行增加27 行删除
{
"CurrentProjectSetting": null
}
\ No newline at end of file
此文件类型无法预览
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}
\ No newline at end of file
此文件类型无法预览

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30523.141
VisualStudioVersion = 16.0.31112.23
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsFormsApp1", "WindowsFormsApp1\WindowsFormsApp1.csproj", "{AD9F4ADD-A002-4CED-8FFF-BCE1B07538B0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FaceControl", "FaceControl\FaceControl.csproj", "{BB46377E-CC9F-4AC2-9367-87503B59EFE7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{543499F4-8839-47F3-AC66-27627B259238}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......@@ -11,15 +13,19 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AD9F4ADD-A002-4CED-8FFF-BCE1B07538B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD9F4ADD-A002-4CED-8FFF-BCE1B07538B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD9F4ADD-A002-4CED-8FFF-BCE1B07538B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD9F4ADD-A002-4CED-8FFF-BCE1B07538B0}.Release|Any CPU.Build.0 = Release|Any CPU
{BB46377E-CC9F-4AC2-9367-87503B59EFE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB46377E-CC9F-4AC2-9367-87503B59EFE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB46377E-CC9F-4AC2-9367-87503B59EFE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB46377E-CC9F-4AC2-9367-87503B59EFE7}.Release|Any CPU.Build.0 = Release|Any CPU
{543499F4-8839-47F3-AC66-27627B259238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{543499F4-8839-47F3-AC66-27627B259238}.Debug|Any CPU.Build.0 = Debug|Any CPU
{543499F4-8839-47F3-AC66-27627B259238}.Release|Any CPU.ActiveCfg = Release|Any CPU
{543499F4-8839-47F3-AC66-27627B259238}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0C8A353-FACB-4930-9D25-C6ECB846F05A}
SolutionGuid = {B1F3BB0E-683B-4A34-82B4-BC20F5468010}
EndGlobalSection
EndGlobal
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace Asa.FaceControl
{
public static class ImageConvert
{
public static Bitmap ToDisabledGray(Bitmap bmp, int lower = 50)
{
if (bmp == null) return null;
int channel;
if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
channel = 3;
else if (bmp.PixelFormat == PixelFormat.Format32bppArgb)
channel = 4;
else
return bmp;
int width = bmp.Width;
int height = bmp.Height;
int widthByte = width * channel;
BitmapData data1 = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
byte[] buffer = new byte[data1.Stride * height];
int stride = data1.Stride;
System.Runtime.InteropServices.Marshal.Copy(data1.Scan0, buffer, 0, buffer.Length);
bmp.UnlockBits(data1);
byte r, g, b;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < widthByte; j += channel)
{
r = buffer[i * stride + j];
g = buffer[i * stride + j + 1];
b = buffer[i * stride + j + 2];
int gray = (r + g + b) / 3 - lower;
if (gray < 0) gray = 0;
buffer[i * stride + j] = (byte)gray;
buffer[i * stride + j + 1] = (byte)gray;
buffer[i * stride + j + 2] = (byte)gray;
}
}
Bitmap image = new(width, height, bmp.PixelFormat);
BitmapData data2 = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, image.PixelFormat);
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, data2.Scan0, buffer.Length);
image.UnlockBits(data2);
return image;
}
}
}

namespace Asa.FaceControl
{
partial class ControlBase
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// ControlBase
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.DoubleBuffered = true;
this.Name = "ControlBase";
this.Size = new System.Drawing.Size(90, 45);
this.Load += new System.EventHandler(this.ControlBase_Load);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.ControlBase_Paint);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ControlBase_MouseDown);
this.MouseEnter += new System.EventHandler(this.ControlBase_MouseEnter);
this.MouseLeave += new System.EventHandler(this.ControlBase_MouseLeave);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ControlBase_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ControlBase_MouseUp);
this.Resize += new System.EventHandler(this.ControlBase_Resize);
this.ResumeLayout(false);
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FormBase
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FormBase
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(457, 369);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "FormBase";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "FormBase";
this.Load += new System.EventHandler(this.FormBase_Load);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.FormBase_Paint);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.FormBase_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.FormBase_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.FormBase_MouseUp);
this.Resize += new System.EventHandler(this.FormBase_Resize);
this.ResumeLayout(false);
}
#endregion
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
namespace WindowsFormsApp1

namespace Asa.FaceControl
{
partial class Form1
partial class PanelBase
{
/// <summary>
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
......@@ -20,21 +21,23 @@
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
#region 组件设计器生成的代码
/// <summary>
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Text = "Form1";
this.SuspendLayout();
//
// PanelBase
//
this.Name = "PanelBase";
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
public partial class PanelBase : Panel
{
//=====属性变量=====
private int _borderWidth = 2;
private ControlShape _faceShape = ControlShape.Rectangle;
//=====私有变量=====
private readonly Dictionary<ControlShape, Action> dicShape;
//private bool borderEnterChanged;
//=====子类变量=====
internal RectangleF borderRect;
internal GraphicsPath borderPath;
internal IFaceThemeColor theme;
//internal bool borderEnter;
internal bool mouseLeftDown;
internal bool mouseRightDown;
internal StringFormat stringFormat = new(StringFormatFlags.NoClip);
public PanelBase()
{
InitializeComponent();
theme = new DarkThemeColor();
BackColor = theme.BACK;
ForeColor = theme.FORE;
Padding = new Padding(3);
dicShape = new Dictionary<ControlShape, Action>
{
{ ControlShape.Rectangle, ShapeRectangle },
{ ControlShape.RoundRectangle, ShapeRoundRectangle },
{ ControlShape.Ellipse, ShapeEllipse },
{ ControlShape.EllipseRectangle, ShapeEllipseRectangle },
{ ControlShape.Circle, ShapeCircle }
};
Resize += ControlBase_Resize;
Paint += ControlBase_Paint;
//MouseEnter += ControlBase_MouseEnter;
//MouseLeave += ControlBase_MouseLeave;
MouseDown += ControlBase_MouseDown;
MouseUp += ControlBase_MouseUp;
//MouseMove += ControlBase_MouseMove;
}
//=====公共方法=====
public Bitmap ToImage()
{
IntPtr hSrce = API.GetWindowDC(Handle);
IntPtr hDest = API.CreateCompatibleDC(hSrce);
IntPtr hBmp = API.CreateCompatibleBitmap(hSrce, Width, Height);
IntPtr hOldBmp = API.SelectObject(hDest, hBmp);
if (API.BitBlt(hDest, 0, 0, Width, Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt))
{
Bitmap bmp = Image.FromHbitmap(hBmp);
API.SelectObject(hDest, hOldBmp);
API.DeleteObject(hBmp);
API.DeleteDC(hDest);
API.ReleaseDC(Handle, hSrce);
return bmp;
}
return null;
}
//=====子类方法=====
protected virtual void CalcSize()
{
//边框位置
float x = _borderWidth / 2f;
float y = _borderWidth / 2f;
float width = ClientSize.Width - _borderWidth - 1;
float height = ClientSize.Height - _borderWidth - 1;
borderRect = new RectangleF(x, y, width, height);
//边框路径
borderPath = new GraphicsPath();
dicShape[_faceShape].Invoke();
}
protected virtual void DrawEnabled(Graphics g)
{
//borderEnterChanged = borderEnter;
}
protected virtual void DrawDisabled(Graphics g)
{
}
public virtual void LostFocused()
{
}
//=====属性=====
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int BorderWidth
{
get => _borderWidth;
set
{
_borderWidth = value > 0 ? value : 0;
CalcSize();
Refresh();
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public new ControlShape BorderStyle
{
get => _faceShape;
set
{
_faceShape = value;
CalcSize();
Refresh();
}
}
[Browsable(false)]
public new Font Font { get => new("宋体", 9f); set => base.Font = new("宋体", 9f); }
private void ShapeRectangle()
{
borderPath.AddRectangle(borderRect);
}
private void ShapeRoundRectangle()
{
float n = borderRect.Height / 8f;
borderPath.AddLine(borderRect.X + n, borderRect.Y, borderRect.Right - n, borderRect.Y);
borderPath.AddArc(borderRect.Right - n - n, borderRect.Y, n + n, n + n, 270, 90);
borderPath.AddLine(borderRect.Right, borderRect.Y + n, borderRect.Right, borderRect.Bottom - n);
borderPath.AddArc(borderRect.Right - n - n, borderRect.Bottom - n - n, n + n, n + n, 0, 90);
borderPath.AddLine(borderRect.Right - n, borderRect.Bottom, borderRect.X + n, borderRect.Bottom);
borderPath.AddArc(borderRect.X, borderRect.Bottom - n - n, n + n, n + n, 90, 90);
borderPath.AddLine(borderRect.X, borderRect.Bottom - n, borderRect.X, borderRect.Y + n);
borderPath.AddArc(borderRect.X, borderRect.Y, n + n, n + n, 180, 90);
}
private void ShapeEllipse()
{
borderPath.AddEllipse(borderRect);
}
private void ShapeEllipseRectangle()
{
float n = borderRect.Height / 2f;
borderPath.AddLine(borderRect.X + n, borderRect.Y, borderRect.Right - n, borderRect.Y);
borderPath.AddArc(borderRect.Right - n - n, borderRect.Y, n + n, n + n, 270, 180);
borderPath.AddLine(borderRect.Right - n, borderRect.Bottom, borderRect.X + n, borderRect.Bottom);
borderPath.AddArc(borderRect.X, borderRect.Y, n + n, n + n, 90, 180);
}
private void ShapeCircle()
{
if (borderRect.Width > borderRect.Height)
borderPath.AddEllipse((borderRect.Width - borderRect.Height) / 2f, borderRect.Y, borderRect.Height, borderRect.Height);
else
borderPath.AddEllipse(borderRect.X, (borderRect.Height - borderRect.Width) / 2f, borderRect.Width, borderRect.Width);
}
private void DrawBorder(Graphics g)
{
//边框以外的部分
Region reg = new(ClientRectangle);
reg.Exclude(borderPath);
g.FillRegion(new SolidBrush(BackColor), reg);
//边框
if (_borderWidth > 0)
{
//Color color = theme.DISABLED;
//if (Enabled)
// color = borderEnter ? theme.BORDER_ENTER : theme.BORDER_LEAVE;
Color color = Enabled ? theme.BORDER_ENTER : theme.DISABLED;
Pen pen = new(color, _borderWidth);
g.DrawPath(pen, borderPath);
}
}
private void ControlBase_Resize(object sender, EventArgs e)
{
CalcSize();
Refresh();
}
private void ControlBase_Paint(object sender, PaintEventArgs e)
{
BufferedGraphicsContext current = BufferedGraphicsManager.Current;
BufferedGraphics bg = current.Allocate(e.Graphics, e.ClipRectangle);
Graphics g = bg.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.Clear(BackColor); //背景
if (Enabled)
DrawEnabled(g);
else
DrawDisabled(g);
DrawBorder(g);
bg.Render();
bg.Dispose();
}
//private void ControlBase_MouseEnter(object sender, EventArgs e)
//{
//}
//private void ControlBase_MouseLeave(object sender, EventArgs e)
//{
// borderEnter = false;
// Refresh();
//}
private void ControlBase_MouseDown(object sender, MouseEventArgs e)
{
if (TopLevelControl is FormBase frm)
frm.SetLostFocus(frm, Handle);
if (e.Button == MouseButtons.Left)
{
mouseLeftDown = true;
mouseRightDown = false;
}
else if (e.Button == MouseButtons.Right)
{
mouseLeftDown = false;
mouseRightDown = true;
}
Refresh();
}
private void ControlBase_MouseUp(object sender, MouseEventArgs e)
{
mouseLeftDown = false;
mouseRightDown = false;
Refresh();
}
//private void ControlBase_MouseMove(object sender, MouseEventArgs e)
//{
// borderEnter = borderPath.IsVisible(e.Location);
// if (borderEnterChanged != borderEnter)
// Refresh();
//}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceButton
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceButton
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceButton";
this.Size = new System.Drawing.Size(90, 45);
this.ResumeLayout(false);
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceCheckBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceCheckBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceCheckBox";
this.Size = new System.Drawing.Size(130, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("CheckedChanged")]
public partial class FaceCheckBox : ControlBase
{
private ContentAlignment _textAlign = ContentAlignment.MiddleCenter;
private bool _check = false;
private int _checkWidth = 18;
private RectangleF textRect;
private RectangleF checkRect;
private const int SPACE = 6; //图片与文字的间隔
private readonly Pen penBorderEnter;
private readonly Pen penBorderLeave;
private readonly Pen penCheckLine;
public FaceCheckBox()
{
InitializeComponent();
penBorderEnter = new(theme.BORDER_ENTER, 2);
penBorderLeave = new(theme.BORDER_LEAVE, 2);
penCheckLine = new(theme.FORE, 2);
MouseUp += FaceCheckBox_MouseUp;
}
[Browsable(true), Category("杂项"), Description("当checked属性更改值时发生")]
public event EventHandler CheckedChanged;
[Browsable(true), Category("外观"), Description("控件上矩形开关的宽度"), DefaultValue(18)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int CheckWidth
{
get => _checkWidth;
set
{
_checkWidth = value;
CalcSize();
Refresh();
}
}
[Browsable(true), Category(""), Description("单选按钮是否已选中"), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool Checked
{
set
{
if (_check != value)
{
_check = value;
CalcSize();
Refresh();
}
CheckedChanged?.Invoke(this, new EventArgs());
}
get
{
return _check;
}
}
[Browsable(true), Category("外观"), Description("按钮上图像与文本的相对位置"), DefaultValue(ContentAlignment.MiddleCenter)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ContentAlignment TextAlign
{
get => _textAlign;
set
{
_textAlign = value;
CalcSize();
Refresh();
}
}
protected override void CalcSize()
{
base.CalcSize();
using Graphics g = CreateGraphics();
checkRect = new(0, 0, _checkWidth + 2, _checkWidth + 2);
int width = Width - BorderWidth - BorderWidth - _checkWidth - 2;
SizeF sf = g.MeasureString(Text, Font, width, stringFormat);
textRect = new(0, 0, sf.Width, sf.Height);
SizeF size = new();
size.Width = textRect.Width + checkRect.Width + SPACE;
size.Height = Math.Max(textRect.Height, checkRect.Height);
PointF pt = CalcTextAlign(size);
checkRect.X = pt.X + 1;
checkRect.Y = pt.Y + (size.Height - checkRect.Height) / 2f;
textRect.X = pt.X + checkRect.Width + SPACE;
textRect.Y = pt.Y + (size.Height - textRect.Height) / 2f;
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
if (mouseLeftDown)
_check = !_check;
if (_check)
{
g.FillRectangle(new SolidBrush(theme.BORDER_ENTER), checkRect);
float y1 = checkRect.Y + checkRect.Height / 2f;
float x2 = checkRect.X + checkRect.Width / 3f;
float y2 = checkRect.Bottom - 2;
g.DrawLine(penCheckLine, checkRect.X + 2, y1, x2, y2);
g.DrawLine(penCheckLine, x2, y2, checkRect.Right - 2, checkRect.Y + 2);
}
//复选框边框
if (borderEnter)
g.DrawRectangle(penBorderEnter, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
else
g.DrawRectangle(penBorderLeave, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
//文字
g.DrawString(Text, Font, new SolidBrush(ForeColor), textRect, stringFormat);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
Pen pen = new(theme.DISABLED, 2);
g.DrawRectangle(pen, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
//文字
g.DrawString(Text, Font, new SolidBrush(theme.DISABLED), textRect, stringFormat);
}
/// <summary>
/// 文本对齐位置
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
private PointF CalcTextAlign(SizeF size)
{
PointF pt = new();
switch (TextAlign)
{
case ContentAlignment.TopLeft: pt = new PointF(Padding.Left, Padding.Top); break;
case ContentAlignment.TopCenter: pt = new PointF((Width - size.Width) / 2f, Padding.Top); break;
case ContentAlignment.TopRight: pt = new PointF(Width - size.Width - Padding.Right, Padding.Top); break;
case ContentAlignment.MiddleLeft: pt = new PointF(Padding.Left, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleCenter: pt = new PointF((Width - size.Width) / 2f, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleRight: pt = new PointF(Width - size.Width - Padding.Right, (Height - size.Height) / 2f); break;
case ContentAlignment.BottomLeft: pt = new PointF(Padding.Left, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomCenter: pt = new PointF((Width - size.Width) / 2f, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomRight: pt = new PointF(Width - size.Width - Padding.Right, Height - size.Height - Padding.Bottom); break;
}
return pt;
}
private void FaceCheckBox_MouseUp(object sender, MouseEventArgs e)
{
CheckedChanged?.Invoke(this, new EventArgs());
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceComboBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceComboBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceComboBox";
this.Size = new System.Drawing.Size(130, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("SelectedIndexChanged")]
public partial class FaceComboBox : ControlBase
{
private CtlComboArrow arrow;
private TextBox ctlText;
private FaceListBox listbox;
public FaceComboBox()
{
InitializeComponent();
arrow = new();
arrow.MouseEnter += Arrow_MouseEnter;
arrow.Click += Arrow_Click;
listbox = new() { Visible = false };
//listbox.LostFocus += Listbox_LostFocus;
listbox.SelectedIndexChanged += Listbox_SelectedIndexChanged;
ctlText = new() { BorderStyle = System.Windows.Forms.BorderStyle.None, BackColor = theme.BACK, ForeColor = theme.FORE };
ctlText.KeyPress += CtlText_KeyPress;
ctlText.MouseEnter += CtlText_MouseEnter;
ctlText.MouseLeave += CtlText_MouseLeave;
ctlText.TextChanged += CtlText_TextChanged;
Controls.Add(arrow);
Controls.Add(ctlText);
}
[Browsable(true), Category("行为"), Description("属性值更改时发生")]
public event EventHandler SelectedIndexChanged;
[Browsable(true), Category("行为"), Description("控制能否更改编辑控件中的文本"), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReadOnly { set => ctlText.ReadOnly = value; get => ctlText.ReadOnly; }
[Browsable(false)]
public int SelectedIndex { set => listbox.SelectedIndex = value; get => listbox.SelectedIndex; }
[Browsable(false)]
public string SelectedText { set => listbox.Text = value; get => listbox.Text; }
[Browsable(true), Category("外观"), Description("指示应该如何对齐编辑控件的文本"), DefaultValue(HorizontalAlignment.Left)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public HorizontalAlignment TextAlign { set => ctlText.TextAlign = value; get => ctlText.TextAlign; }
[Browsable(true), Category("外观"), Description("用于显示控件中文本的字体")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
CalcSize();
}
}
[Browsable(true), Category("外观"), Description("与控件关联的文本")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text { get => ctlText.Text; set => ctlText.Text = value; }
[Browsable(false)]
public FaceListBox.Item Items { get => listbox.Items; }
protected override void CalcSize()
{
base.CalcSize();
if (arrow != null)
{
arrow.Top = BorderWidth + 1;
arrow.Height = Height - BorderWidth - BorderWidth - 2;
arrow.Width = 30;
arrow.Left = Width - BorderWidth - arrow.Width - 1;
}
if (ctlText != null)
{
ctlText.Width = arrow.Left - BorderWidth - 5;
ctlText.Left = BorderWidth + 3;
ctlText.Top = (Height - ctlText.Height) / 2 + 1;
}
}
public override void LostFocused()
{
base.LostFocused();
HideDropDown();
arrow.Press = false;
}
private void HideDropDown()
{
Control ctl = listbox.Parent;
if (ctl == null) return;
ctl.Controls.Remove(listbox);
listbox.Visible = false;
}
private void Listbox_SelectedIndexChanged(object sender, EventArgs e)
{
ctlText.Text = listbox.Items[listbox.SelectedIndex];
HideDropDown();
arrow.Press = false;
SelectedIndexChanged?.Invoke(this, new EventArgs());
}
private void Arrow_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void Arrow_Click(object sender, EventArgs e)
{
if (arrow.Press)
{
Control ctl = TopLevelControl;
Point pt = PointToScreen(new Point(0, Height));
listbox.Location = ctl.PointToClient(pt);
listbox.Width = Width;
ctl.Controls.Add(listbox);
listbox.BringToFront();
listbox.Visible = true;
listbox.Focus();
}
else
{
HideDropDown();
}
}
private void CtlText_TextChanged(object sender, EventArgs e)
{
//TextChanged?.Invoke(this, new EventArgs());
}
private void CtlText_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void CtlText_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlText_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r')
listbox.Text = ctlText.Text;
//else if (e.KeyChar == '\n')
//{ }
//else
// KeyPress?.Invoke(this, e);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceLabel
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceLabel
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceLabel";
this.Size = new System.Drawing.Size(90, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("Click")]
public partial class FaceLabel : ControlBase
{
private Bitmap _image = null;
private TextImageRelation _textImageRelation = TextImageRelation.Overlay;
private ContentAlignment _textAlign = ContentAlignment.MiddleCenter;
private Bitmap imageDisabled;
private RectangleF textRect;
private RectangleF imageRect;
private readonly Dictionary<TextImageRelation, Action> dicTextImage;
private const int SPACE = 6; //图片与文字的间隔
public FaceLabel()
{
InitializeComponent();
dicTextImage = new Dictionary<TextImageRelation, Action>
{
{ TextImageRelation.Overlay, CalcOverlay },
{ TextImageRelation.ImageAboveText, CalcImageAboveText },
{ TextImageRelation.TextAboveImage, CalcTextAboveImage },
{ TextImageRelation.ImageBeforeText, CalcImageBeforeText },
{ TextImageRelation.TextBeforeImage, CalcTextBeforeImage }
};
}
[Browsable(true), Category("外观"), Description("控件上显示的图像"), DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Bitmap Image
{
get => _image;
set
{
_image = value;
imageDisabled = ImageConvert.ToDisabledGray(value);
CalcSize();
Refresh();
}
}
[Browsable(true), Category("外观"), Description("按钮上图像与文本的相对位置"), DefaultValue(TextImageRelation.Overlay)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public TextImageRelation TextImageRelation
{
get => _textImageRelation;
set
{
_textImageRelation = value;
CalcSize();
Refresh();
}
}
[Browsable(true), Category("外观"), Description("按钮上图像与文本的相对位置"), DefaultValue(ContentAlignment.MiddleCenter)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ContentAlignment TextAlign
{
get => _textAlign;
set
{
_textAlign = value;
CalcSize();
Refresh();
}
}
protected override void CalcSize()
{
base.CalcSize();
using Graphics g = CreateGraphics();
int width = Width - BorderWidth - BorderWidth;
SizeF sf = g.MeasureString(Text, Font, width, stringFormat);
textRect = new(0, 0, sf.Width, sf.Height);
if (_image == null)
{
textRect.Location = CalcTextAlign(sf);
}
else
{
imageRect = new(0, 0, _image.Width, _image.Height);
dicTextImage[TextImageRelation].Invoke();
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
if (_image != null)
g.DrawImage(_image, imageRect);
//文字
g.DrawString(Text, Font, new SolidBrush(ForeColor), textRect, stringFormat);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
if (imageDisabled != null)
g.DrawImage(imageDisabled, imageRect);
//文字
g.DrawString(Text, Font, new SolidBrush(theme.DISABLED), textRect, stringFormat);
}
/// <summary>
/// 文本对齐位置
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
private PointF CalcTextAlign(SizeF size)
{
PointF pt = new();
switch (TextAlign)
{
case ContentAlignment.TopLeft: pt = new PointF(Padding.Left, Padding.Top); break;
case ContentAlignment.TopCenter: pt = new PointF((Width - size.Width) / 2f, Padding.Top); break;
case ContentAlignment.TopRight: pt = new PointF(Width - size.Width - Padding.Right, Padding.Top); break;
case ContentAlignment.MiddleLeft: pt = new PointF(Padding.Left, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleCenter: pt = new PointF((Width - size.Width) / 2f, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleRight: pt = new PointF(Width - size.Width - Padding.Right, (Height - size.Height) / 2f); break;
case ContentAlignment.BottomLeft: pt = new PointF(Padding.Left, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomCenter: pt = new PointF((Width - size.Width) / 2f, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomRight: pt = new PointF(Width - size.Width - Padding.Right, Height - size.Height - Padding.Bottom); break;
}
return pt;
}
/// <summary>
/// 图像和文本共享控件上的同一空间
/// </summary>
private void CalcOverlay()
{
SizeF size = new();
size.Width = Math.Max(textRect.Width, imageRect.Width);
size.Height = Math.Max(textRect.Height, imageRect.Height);
PointF pt = CalcTextAlign(size);
imageRect.X = pt.X + (size.Width - imageRect.Width) / 2f;
imageRect.Y = pt.Y + (size.Height - imageRect.Height) / 2f;
textRect.X = pt.X + (size.Width - textRect.Width) / 2f;
textRect.Y = pt.Y + (size.Height - textRect.Height) / 2f;
}
/// <summary>
/// 图像垂直显示在文本的上方
/// </summary>
private void CalcImageAboveText()
{
SizeF size = new();
size.Width = Math.Max(textRect.Width, imageRect.Width);
size.Height = textRect.Height + imageRect.Height + SPACE;
PointF pt = CalcTextAlign(size);
imageRect.X = pt.X + (size.Width - imageRect.Width) / 2f;
imageRect.Y = pt.Y;
textRect.X = pt.X + (size.Width - textRect.Width) / 2f;
textRect.Y = pt.Y + imageRect.Height + SPACE;
}
/// <summary>
/// 文本垂直显示在图像的上方
/// </summary>
private void CalcTextAboveImage()
{
SizeF size = new();
size.Width = Math.Max(textRect.Width, imageRect.Width);
size.Height = textRect.Height + imageRect.Height + SPACE;
PointF pt = CalcTextAlign(size);
imageRect.X = pt.X + (size.Width - imageRect.Width) / 2f;
imageRect.Y = pt.Y + textRect.Height + SPACE;
textRect.X = pt.X + (size.Width - textRect.Width) / 2f;
textRect.Y = pt.Y;
}
/// <summary>
/// 图像水平显示在文本的前方
/// </summary>
private void CalcImageBeforeText()
{
SizeF size = new();
size.Width = textRect.Width + imageRect.Width + SPACE;
size.Height = Math.Max(textRect.Height, imageRect.Height);
PointF pt = CalcTextAlign(size);
imageRect.X = pt.X;
imageRect.Y = pt.Y + (size.Height - imageRect.Height) / 2f;
textRect.X = pt.X + imageRect.Width + SPACE;
textRect.Y = pt.Y + (size.Height - textRect.Height) / 2f;
}
/// <summary>
/// 文本水平显示在图像的前方
/// </summary>
private void CalcTextBeforeImage()
{
SizeF size = new();
size.Width = textRect.Width + imageRect.Width + SPACE;
size.Height = Math.Max(textRect.Height, imageRect.Height);
PointF pt = CalcTextAlign(size);
imageRect.X = pt.X + textRect.Width + SPACE;
imageRect.Y = pt.Y + (size.Height - imageRect.Height) / 2f;
textRect.X = pt.X;
textRect.Y = pt.Y + (size.Height - textRect.Height) / 2f;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceListBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceListBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceListBox";
this.Size = new System.Drawing.Size(150, 150);
this.ResumeLayout(false);
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceNumericUpDown
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceNumericUpDown
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceNumericUpDown";
this.Size = new System.Drawing.Size(148, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Value")]
[DefaultEvent("ValueChanged")]
public partial class FaceNumericUpDown : ControlBase
{
private float _min = 0;
private float _max = 100;
private float _value = 0;
private int _decimalPlaces = 2;
private CtlNumericDown numDown;
private CtlNumericUp numUp;
private TextBox ctlText;
public FaceNumericUpDown()
{
InitializeComponent();
numDown = new();
numDown.MouseEnter += NumDown_MouseEnter;
numDown.MouseLeave += NumDown_MouseLeave;
numDown.Click += NumDown_Click;
Controls.Add(numDown);
numUp = new();
numUp.MouseEnter += NumUp_MouseEnter;
numUp.MouseLeave += NumUp_MouseLeave;
numUp.Click += NumUp_Click;
Controls.Add(numUp);
ctlText = new() { BorderStyle = System.Windows.Forms.BorderStyle.None, BackColor = theme.BACK, ForeColor = theme.FORE, ImeMode = ImeMode.Disable };
ctlText.KeyPress += CtlText_KeyPress;
ctlText.MouseEnter += CtlText_MouseEnter;
ctlText.MouseLeave += CtlText_MouseLeave;
ctlText.TextChanged += CtlText_TextChanged;
Controls.Add(ctlText);
Increment = 1;
ShowNumeric();
}
[Browsable(true), Category("操作"), Description("控件中的值更改时发生")]
public event EventHandler ValueChanged;
[Browsable(true), Category("数据"), Description("控件的最小值"), DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public float Minimum
{
get => _min;
set
{
_min = value;
if (_value < _min) _value = _min;
ShowNumeric();
CalcSize();
Refresh();
}
}
[Browsable(true), Category("数据"), Description("控件的最大值"), DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public float Maximum
{
get => _max;
set
{
_max = value;
if (_value > _max) _value = _max;
ShowNumeric();
CalcSize();
Refresh();
}
}
[Browsable(true), Category("外观"), Description("控件的当前值"), DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public float Value
{
get => _value;
set
{
if (_value == value) return;
_value = value;
if (_value < _min)
_value = _min;
else if (_value > _max)
_value = _max;
ShowNumeric();
CalcSize();
Refresh();
ValueChanged?.Invoke(this, new EventArgs());
}
}
[Browsable(true), Category("数据"), Description("指示要显示的小数位数"), DefaultValue(2)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int DecimalPlaces
{
get => _decimalPlaces;
set
{
_decimalPlaces = value;
ShowNumeric();
}
}
[Browsable(true), Category("数据"), Description("增加或减少的数量"), DefaultValue(1)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public float Increment { set; get; }
[Browsable(true), Category("行为"), Description("控制能否更改编辑控件中的文本"), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReadOnly { set => ctlText.ReadOnly = value; get => ctlText.ReadOnly; }
[Browsable(true), Category("外观"), Description("指示应该如何对齐编辑控件的文本"), DefaultValue(HorizontalAlignment.Left)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public HorizontalAlignment TextAlign { set => ctlText.TextAlign = value; get => ctlText.TextAlign; }
[Browsable(true), Category("外观"), Description("用于显示控件中文本的字体")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
CalcSize();
}
}
[Browsable(true), Category("外观"), Description("与控件关联的文本")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text { get => _value.ToString(); }
protected override void CalcSize()
{
base.CalcSize();
int h = Height - (BorderWidth + 1) * 2;
if (numUp != null)
{
numUp.Width = 30;
numUp.Height = h;
numUp.Left = Width - BorderWidth - numUp.Width - 1;
numUp.Top = BorderWidth + 1;
}
if (numDown != null)
{
numDown.Width = 30;
numDown.Height = h;
numDown.Left = BorderWidth + 1;
numDown.Top = BorderWidth + 1;
}
if (ctlText != null)
{
ctlText.Width = Width - numUp.Width - numDown.Width - (BorderWidth + 3) * 2;
ctlText.Left = numDown.Right + 2;
ctlText.Top = (Height - ctlText.Height) / 2 + 1;
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
}
private void ShowNumeric()
{
ctlText.Text = string.Format("{0:N" + _decimalPlaces + "}", _value);
ctlText.SelectionStart = ctlText.Text.Length;
}
private void NumUp_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void NumUp_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void NumUp_Click(object sender, EventArgs e)
{
Value += Increment;
}
private void NumDown_Click(object sender, EventArgs e)
{
Value -= Increment;
}
private void NumDown_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void NumDown_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void CtlText_TextChanged(object sender, EventArgs e)
{
//TextChanged?.Invoke(this, new EventArgs());
}
private void CtlText_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlText_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r')
{
bool rtn = float.TryParse(ctlText.Text, out float result);
if (rtn) Value = result;
}
//else if (e.KeyChar == '\n')
//{ }
//else
// KeyPress?.Invoke(this, e);
}
private void CtlText_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FacePanel
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FacePanel
//
this.Name = "FacePanel";
this.Size = new System.Drawing.Size(187, 181);
this.ResumeLayout(false);
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FacePasswordLock
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FacePasswordLock
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FacePasswordLock";
this.Size = new System.Drawing.Size(240, 240);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("NumberEnter")]
public partial class FacePasswordLock : ControlBase
{
public delegate void NumberEnterHandler(object sender, string text);
public event NumberEnterHandler NumberEnter;
private FaceButton[] button;
private FaceTextBox txtNumber;
private readonly Dictionary<string, string> buttonKeys;
private const int SPACE = 6; //按钮之间的间隔
public FacePasswordLock()
{
InitializeComponent();
buttonKeys = new()
{
{ "1", "1" },
{ "2", "2" },
{ "3", "3" },
{ "4", "4" },
{ "5", "5" },
{ "6", "6" },
{ "7", "7" },
{ "8", "8" },
{ "9", "9" },
{ "0", "0" },
{ "←", "{BACKSPACE}" },
{ "Enter", "{ENTER}" }
};
button = new FaceButton[12];
for (int i = 0; i < button.Length; i++)
{
button[i] = new() { Text = buttonKeys.ElementAt(i).Key };
button[i].MouseEnter += FacePasswordLock_MouseEnter;
button[i].MouseLeave += FacePasswordLock_MouseLeave;
button[i].Click += NumberButton_Click;
}
Controls.AddRange(button);
txtNumber = new() { Text = "", ShowQuery = false, Location = new Point(BorderWidth + SPACE, BorderWidth + SPACE), Height = 45, ImeMode = ImeMode.Disable, TabIndex = 0 };
txtNumber.KeyPress += TxtNumber_KeyPress;
txtNumber.KeyEnterPress += TxtNumber_KeyEnterPress;
Controls.Add(txtNumber);
txtNumber.Focus();
}
[Browsable(true)]
public override string Text { get => txtNumber.Text; set => txtNumber.Text = value; }
public void Clear()
{
txtNumber.Text = "";
}
private void TxtNumber_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13) return;
if (e.KeyChar == 8) return;
if (e.KeyChar >= 48 && e.KeyChar <= 57) return;
e.Handled = true;
}
private void TxtNumber_KeyEnterPress(object sender, EventArgs e)
{
NumberEnter?.Invoke(this, txtNumber.Text);
}
private void NumberButton_Click(object sender, EventArgs e)
{
string key = (sender as FaceButton).Text;
txtNumber.Focus();
SendKeys.Send(buttonKeys[key]);
}
private void FacePasswordLock_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void FacePasswordLock_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
protected override void CalcSize()
{
base.CalcSize();
if (button == null) return;
if (txtNumber == null) return;
txtNumber.Width = Width - (BorderWidth + SPACE) * 2;
int startPoint = BorderWidth + SPACE;
int x = startPoint, y = startPoint + txtNumber.Height + SPACE;
int width = (Width - BorderWidth * 2 - SPACE * 4) / 3;
int height = (Height - txtNumber.Height - BorderWidth * 2 - SPACE * 6) / 4;
for (int i = 0; i < button.Length; i++)
{
button[i].Left = x;
button[i].Top = y;
button[i].Width = width;
button[i].Height = height;
x += width + SPACE;
if ((i + 1) % 3 == 0)
{
x = startPoint;
y += height + SPACE;
}
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
//Pen pen = new(theme.BORDER_LEAVE);
//SolidBrush brush = new(ForeColor);
//g.DrawRectangles(pen, buttonRect);
//for (int i = 0; i < buttonTextRect.Length; i++)
// g.DrawString(BUTTON_TEXT[i], Font, brush, buttonTextRect[i]);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
//Pen pen = new(theme.DISABLED);
//SolidBrush brush = new(theme.DISABLED);
//g.DrawRectangles(pen, buttonRect);
//for (int i = 0; i < buttonTextRect.Length; i++)
// g.DrawString(BUTTON_TEXT[i], Font, brush, buttonTextRect[i]);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FacePictureBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FacePictureBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FacePictureBox";
this.Size = new System.Drawing.Size(129, 124);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("Click")]
public partial class FacePictureBox : ControlBase
{
private Bitmap _bmp;
private int buttonOver = -1;
private int buttonDown = -1;
private RectangleF[] buttonRect = new RectangleF[3];
private RectangleF[] textRect = new RectangleF[3];
private Point oldPoint = new(0, 0);
private PointF oldOffset = new(0, 0);
private PointF Offset = new(0, 0);
private int oldZoom = 0;
private int zoom = 30; //缩放比例
private List<PointF> codeCenter = new List<PointF>();
private RectangleF[] codeCenterRect;
private const int BUTTON_SIZE = 50;
private readonly string[] BUTTON_TEXT = new string[3] { "-", "+", "□" };
private readonly Font BUTTON_FONT = new("宋体", 20f);
private readonly Font CENTER_FONT = new("宋体", 11f);
private const int ZOOM_MAX = 200; //200%
private const int ZOOM_MIN = 10; //10%
private const int ZOOM_PRECISE = 5; //5%
public FacePictureBox()
{
InitializeComponent();
MouseWheel += FacePictureBox_MouseWheel;
MouseMove += FacePictureBox_MouseMove;
MouseDown += FacePictureBox_MouseDown;
MouseUp += FacePictureBox_MouseUp;
}
[Browsable(true), Category(""), Description("控件上显示的图像"), DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Bitmap Image
{
get => _bmp;
set
{
_bmp = value;
codeCenter.Clear();
Autofit();
Refresh();
}
}
public void ImagePath(string path)
{
if (System.IO.File.Exists(path))
{
Image = new Bitmap(path);
}
}
public void AddCodeCenter(params PointF[] pt)
{
codeCenter.AddRange(pt);
CalcSize();
Refresh();
}
public void CodeCenterClear()
{
codeCenter.Clear();
CalcSize();
Refresh();
}
protected override void CalcSize()
{
base.CalcSize();
SizeF sf;
Graphics g = CreateGraphics();
float x = borderRect.Right - BUTTON_SIZE * 3;
float y = borderRect.Y + borderRect.Height - BUTTON_SIZE;
for (int i = 0; i < buttonRect.Length; i++)
{
buttonRect[i] = new RectangleF(x, y, BUTTON_SIZE, BUTTON_SIZE);
x += BUTTON_SIZE;
sf = g.MeasureString(BUTTON_TEXT[i], BUTTON_FONT);
textRect[i] = new RectangleF(buttonRect[i].X + (buttonRect[i].Width - sf.Width) / 2f, buttonRect[i].Y + (buttonRect[i].Height - sf.Height) / 2f, sf.Width, sf.Height);
}
codeCenterRect = new RectangleF[codeCenter.Count];
for (int i = 0; i < codeCenterRect.Length; i++)
{
sf = g.MeasureString((i + 1).ToString(), CENTER_FONT);
x = Offset.X + codeCenter[i].X * zoom / 100f - sf.Width / 2f - 3;
y = Offset.Y + codeCenter[i].Y * zoom / 100f - sf.Height / 2f - 3;
codeCenterRect[i] = new RectangleF(x, y, sf.Width, sf.Height);
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
if (_bmp != null)
{
RectangleF destRect = new RectangleF(Offset.X, Offset.Y, _bmp.Width * zoom / 100f, _bmp.Height * zoom / 100f);
RectangleF srcRect = new RectangleF(0, 0, _bmp.Width, _bmp.Height);
g.DrawImage(_bmp, destRect, srcRect, GraphicsUnit.Pixel);
//Region reg = new Region(new Rectangle(0, 0, Width, Height));
//reg.Exclude(borderPath);
//g.FillRegion(new SolidBrush(theme.BACK), reg);
}
//显示比例
string s = string.Format("{0:F0}%", zoom);
SizeF sf = g.MeasureString(s, BUTTON_FONT);
float w = sf.Width + 10;
g.FillRectangle(new SolidBrush(theme.OVER), borderRect.X, borderRect.Bottom - BUTTON_SIZE, w, BUTTON_SIZE);
g.DrawString(s, BUTTON_FONT, new SolidBrush(ForeColor), borderRect.X + (w - sf.Width) / 2f, borderRect.Bottom - BUTTON_SIZE + (BUTTON_SIZE - sf.Height) / 2f);
for (int i = 0; i < codeCenterRect.Length; i++)
{
g.FillRectangle(Brushes.LightGray, codeCenterRect[i]);
g.DrawString((i + 1).ToString(), CENTER_FONT, Brushes.Red, codeCenterRect[i]);
}
for (int i = 0; i < buttonRect.Length; i++)
{
if (i == buttonDown)
{
g.FillRectangle(new SolidBrush(theme.DOWN), buttonRect[i]);
}
else if (i == buttonOver)
{
g.FillRectangle(new SolidBrush(theme.OVER), buttonRect[i]);
}
}
for (int i = 0; i < textRect.Length; i++)
{
g.DrawString(BUTTON_TEXT[i], BUTTON_FONT, new SolidBrush(ForeColor), textRect[i]);
}
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
}
private void FacePictureBox_MouseUp(object sender, MouseEventArgs e)
{
if (!Enabled) return;
if (e.Button == MouseButtons.Left)
{
if (buttonDown == 0)
Small();
else if (buttonDown == 1)
Large();
else if (buttonDown == 2)
Autofit();
buttonDown = -1;
//CalcTextLocation();
Refresh();
}
}
private void FacePictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (!Enabled) return;
if (e.Button == MouseButtons.Left)
{
buttonDown = buttonOver;
oldPoint = e.Location;
oldOffset = Offset;
Refresh();
}
}
private void FacePictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (!Enabled) return;
if (e.Button == MouseButtons.Left)
{
Offset = new PointF(e.X - oldPoint.X + oldOffset.X, e.Y - oldPoint.Y + oldOffset.Y);
}
else
{
buttonOver = -1;
for (int i = 0; i < buttonRect.Length; i++)
{
if (buttonRect[i].Contains(e.Location))
{
buttonOver = i;
break;
}
}
}
CalcSize();
Refresh();
}
private void FacePictureBox_MouseWheel(object sender, MouseEventArgs e)
{
if (!Enabled) return;
if (_bmp == null) return;
oldZoom = zoom;
if (e.Delta > 0)
{
if (zoom < ZOOM_MAX)
zoom += 1;
else
zoom = ZOOM_MAX;
}
else
{
if (zoom > ZOOM_MIN)
zoom -= 1;
else
zoom = ZOOM_MIN;
}
float x = (e.X - Offset.X) * (Width * zoom / 100f) / (Width * oldZoom / 100f);
float y = (e.Y - Offset.Y) * (Height * zoom / 100f) / (Height * oldZoom / 100f);
Offset = new PointF(e.X - x, e.Y - y);
CalcSize();
Refresh();
}
private void Autofit()
{
if (_bmp == null) return;
float w, h;
w = Width;
h = w * _bmp.Height / _bmp.Width;
if (h > Height)
{
h = Height;
w = h * _bmp.Width / _bmp.Height;
}
float scale = w / _bmp.Width * 100f;
if (scale > ZOOM_MAX)
zoom = ZOOM_MAX;
else if (scale < ZOOM_MIN)
zoom = ZOOM_MIN;
else
zoom = Convert.ToInt32(scale);
float x = (Width - _bmp.Width * zoom / 100f) / 2f;
float y = (Height - _bmp.Height * zoom / 100f) / 2f;
Offset = new PointF(x, y);
//Refresh();
}
private void Large()
{
if (_bmp == null) return;
oldZoom = zoom;
if (zoom < ZOOM_MAX)
zoom += ZOOM_PRECISE;
else
zoom = ZOOM_MAX;
float x = (_bmp.Width * zoom / 100f - _bmp.Width * oldZoom / 100f) / 2f;
float y = (_bmp.Height * zoom / 100f - _bmp.Height * oldZoom / 100f) / 2f;
Offset = new PointF(Offset.X - x, Offset.Y - y);
//Refresh();
}
private void Small()
{
if (_bmp == null) return;
oldZoom = zoom;
if (zoom > ZOOM_MIN)
zoom -= ZOOM_PRECISE;
else
zoom = ZOOM_MIN;
float x = (_bmp.Width * zoom / 100f - _bmp.Width * oldZoom / 100f) / 2f;
float y = (_bmp.Height * zoom / 100f - _bmp.Height * oldZoom / 100f) / 2f;
Offset = new PointF(Offset.X - x, Offset.Y - y);
//Refresh();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceRadioBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceRadioBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceRadioBox";
this.Size = new System.Drawing.Size(130, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("CheckedChanged")]
public partial class FaceRadioBox : ControlBase
{
private ContentAlignment _textAlign = ContentAlignment.MiddleCenter;
private bool _check = false;
private int _checkWidth = 18;
private RectangleF textRect;
private RectangleF checkRect;
private const int SPACE = 6; //图片与文字的间隔
private readonly Pen penBorderEnter;
private readonly Pen penBorderLeave;
//private readonly SolidBrush penCheckLine;
public FaceRadioBox()
{
InitializeComponent();
penBorderEnter = new(theme.BORDER_ENTER, 2);
penBorderLeave = new(theme.BORDER_LEAVE, 2);
//penCheckLine = new SolidBrush(theme.FORE);
MouseUp += FaceCheckBox_MouseUp;
MouseDown += FaceRadioBox_MouseDown;
}
[Browsable(true), Category("杂项"), Description("当checked属性更改值时发生")]
public event EventHandler CheckedChanged;
[Browsable(true), Category("外观"), Description("控件上矩形开关的宽度"), DefaultValue(18)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int CheckWidth
{
get => _checkWidth;
set
{
_checkWidth = value;
CalcSize();
Refresh();
}
}
[Browsable(true), Category(""), Description("单选按钮是否已选中"), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool Checked
{
set
{
if (_check != value)
{
_check = value;
CalcSize();
Refresh();
}
if (_check) SetGroup();
CheckedChanged?.Invoke(this, new EventArgs());
}
get
{
return _check;
}
}
[Browsable(true), Category(""), Description("相同的内容为一组,同一组的单选按钮只能选中一个。"), DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Group { set; get; }
[Browsable(true), Category("外观"), Description("按钮上图像与文本的相对位置"), DefaultValue(ContentAlignment.MiddleCenter)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ContentAlignment TextAlign
{
get => _textAlign;
set
{
_textAlign = value;
CalcSize();
Refresh();
}
}
protected override void CalcSize()
{
base.CalcSize();
using Graphics g = CreateGraphics();
checkRect = new(0, 0, _checkWidth + 2, _checkWidth + 2);
int width = Width - BorderWidth - BorderWidth - _checkWidth - 2;
SizeF sf = g.MeasureString(Text, Font, width, stringFormat);
textRect = new(0, 0, sf.Width, sf.Height);
SizeF size = new();
size.Width = textRect.Width + checkRect.Width + SPACE;
size.Height = Math.Max(textRect.Height, checkRect.Height);
PointF pt = CalcTextAlign(size);
checkRect.X = pt.X + 1;
checkRect.Y = pt.Y + (size.Height - checkRect.Height) / 2f;
textRect.X = pt.X + checkRect.Width + SPACE;
textRect.Y = pt.Y + (size.Height - textRect.Height) / 2f;
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
if (mouseLeftDown)
_check = !_check;
if (_check)
{
g.FillEllipse(new SolidBrush(theme.BORDER_ENTER), checkRect);
//float part = _checkWidth / 10f;
//float w = part * 4;
//float h = part * 4;
//float x = checkRect.X + (checkRect.Width - w) / 2f;
//float y = checkRect.Y + (checkRect.Height - h) / 2f;
//g.FillEllipse(penCheckLine, x, y, w, h);
}
//单选框边框
if (borderEnter)
g.DrawEllipse(penBorderEnter, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
else
g.DrawEllipse(penBorderLeave, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
//文字
g.DrawString(Text, Font, new SolidBrush(ForeColor), textRect, stringFormat);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
Pen pen = new(theme.DISABLED, 2);
g.DrawEllipse(pen, checkRect.X, checkRect.Y, checkRect.Width, checkRect.Height);
//文字
g.DrawString(Text, Font, new SolidBrush(theme.DISABLED), textRect, stringFormat);
}
/// <summary>
/// 文本对齐位置
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
private PointF CalcTextAlign(SizeF size)
{
PointF pt = new();
switch (TextAlign)
{
case ContentAlignment.TopLeft: pt = new PointF(Padding.Left, Padding.Top); break;
case ContentAlignment.TopCenter: pt = new PointF((Width - size.Width) / 2f, Padding.Top); break;
case ContentAlignment.TopRight: pt = new PointF(Width - size.Width - Padding.Right, Padding.Top); break;
case ContentAlignment.MiddleLeft: pt = new PointF(Padding.Left, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleCenter: pt = new PointF((Width - size.Width) / 2f, (Height - size.Height) / 2f); break;
case ContentAlignment.MiddleRight: pt = new PointF(Width - size.Width - Padding.Right, (Height - size.Height) / 2f); break;
case ContentAlignment.BottomLeft: pt = new PointF(Padding.Left, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomCenter: pt = new PointF((Width - size.Width) / 2f, Height - size.Height - Padding.Bottom); break;
case ContentAlignment.BottomRight: pt = new PointF(Width - size.Width - Padding.Right, Height - size.Height - Padding.Bottom); break;
}
return pt;
}
private void SetGroup()
{
if (Parent == null) return;
foreach (Control ctl in Parent.Controls)
{
if (ctl is FaceRadioBox rdo)
{
if (rdo == this) continue;
if (rdo.Group == Group)
{
if (rdo.Checked)
{
rdo.Checked = false;
break;
}
}
}
}
}
private void FaceRadioBox_MouseDown(object sender, MouseEventArgs e)
{
if (_check) SetGroup();
}
private void FaceCheckBox_MouseUp(object sender, MouseEventArgs e)
{
CheckedChanged?.Invoke(this, new EventArgs());
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceScrollBar
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceScrollBar
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceScrollBar";
this.Size = new System.Drawing.Size(28, 160);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Value")]
[DefaultEvent("Scroll")]
public partial class FaceScrollBar : ControlBase
{
private int _max = 100;
private int _min = 0;
private int _value = 0;
private CtlScrollUp ctlUp;
private CtlScrollDown ctlDown;
private CtlBar ctlBar;
public FaceScrollBar()
{
InitializeComponent();
ctlUp = new();
ctlUp.Click += CtlUp_Click;
ctlUp.MouseEnter += CtlUp_MouseEnter;
ctlDown = new();
ctlDown.Click += CtlDown_Click;
ctlDown.MouseEnter += CtlDown_MouseEnter;
ctlBar = new();
ctlBar.Click += CtlBar_Click;
ctlBar.MouseEnter += CtlBar_MouseEnter;
ctlBar.ValueChanged += CtlBar_ValueChanged;
ctlBar.SetValue(_min, _max, _value);
Controls.Add(ctlUp);
Controls.Add(ctlDown);
Controls.Add(ctlBar);
}
[Browsable(true), Category("操作"), Description("用户移动滚动框时发生")]
public new event ScrollEventHandler Scroll;
[Browsable(true), Category("行为"), Description("滑块位置的最小值"), DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Minimum
{
get => _min;
set
{
_min = value;
if (ctlBar != null)
{
ctlBar.SetValue(_min, _max, _value);
_value = ctlBar.Value;
}
}
}
[Browsable(true), Category("行为"), Description("滑块位置的最大值"), DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Maximum
{
get => _max;
set
{
_max = value;
if (ctlBar != null)
{
ctlBar.SetValue(_min, _max, _value);
_value = ctlBar.Value;
}
}
}
[Browsable(true), Category("行为"), Description("滑块的位置"), DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Value
{
get => _value;
set
{
if (_value == value) return;
int old = _value;
if (ctlBar != null)
{
ctlBar.Value = value;
_value = ctlBar.Value;
}
ScrollEventArgs args = new(ScrollEventType.EndScroll, old, _value);
Scroll?.Invoke(this, args);
}
}
protected override void CalcSize()
{
base.CalcSize();
int left = BorderWidth + 1;
int h = 30;
int w = Width - BorderWidth - BorderWidth - 2;
if (ctlUp != null)
{
ctlUp.Left = left;
ctlUp.Top = BorderWidth + 1;
ctlUp.Width = w;
ctlUp.Height = h;
}
if (ctlDown != null)
{
ctlDown.Left = left;
ctlDown.Top = Height - BorderWidth - h - 1;
ctlDown.Width = w;
ctlDown.Height = h;
}
if (ctlBar != null)
{
ctlBar.Left = left;
ctlBar.Top = BorderWidth + h + 1;
ctlBar.Width = w;
ctlBar.Height = Height - (BorderWidth + h + 1) * 2;
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
}
private void CtlDown_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlDown_Click(object sender, EventArgs e)
{
Value++;
}
private void CtlUp_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlUp_Click(object sender, EventArgs e)
{
Value--;
}
private void CtlBar_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlBar_ValueChanged(object sender, EventArgs e)
{
Value = ctlBar.Value;
}
private void CtlBar_Click(object sender, EventArgs e)
{
//throw new NotImplementedException();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file

namespace Asa.FaceControl
{
partial class FaceTextBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// FaceTextBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "FaceTextBox";
this.Size = new System.Drawing.Size(148, 45);
this.ResumeLayout(false);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asa.FaceControl
{
[DefaultProperty("Text")]
[DefaultEvent("Click")]
public partial class FaceTextBox : ControlBase
{
private bool _showQuery = true;
private bool _showDel = true;
private CtlQuery ctlQuery;
private CtlDel ctlDel;
private TextBox ctlText;
public FaceTextBox()
{
InitializeComponent();
ctlQuery = new() { Visible = false, TabStop = false, TabIndex = 1 };
ctlQuery.Click += CtlQuery_Click;
ctlQuery.MouseEnter += CtlQuery_MouseEnter;
ctlQuery.MouseLeave += CtlQuery_MouseLeave;
ctlDel = new() { Visible = false, TabStop = false, TabIndex = 2 };
ctlDel.Click += CtlDel_Click;
ctlDel.MouseEnter += CtlDel_MouseEnter;
ctlDel.MouseLeave += CtlDel_MouseLeave;
ctlText = new() { BorderStyle = System.Windows.Forms.BorderStyle.None, BackColor = theme.BACK, ForeColor = theme.FORE, TabIndex = 0 };
ctlText.KeyPress += CtlText_KeyPress;
ctlText.KeyDown += CtlText_KeyDown;
ctlText.KeyUp += CtlText_KeyUp;
ctlText.MouseEnter += CtlText_MouseEnter;
ctlText.MouseLeave += CtlText_MouseLeave;
ctlText.TextChanged += CtlText_TextChanged;
Controls.Add(ctlText);
Controls.Add(ctlQuery);
Controls.Add(ctlDel);
GotFocus += FaceTextBox_GotFocus;
}
[Browsable(true), Category("属性已更改"), Description("在控件上更改Text属性的值时引发的事件")]
public new event EventHandler TextChanged;
[Browsable(true), Category("键"), Description("用户按下并释放某个键后发生")]
public new event KeyPressEventHandler KeyPress;
[Browsable(true), Category("键"), Description("用户按下并释放Enter键后发生")]
public event EventHandler KeyEnterPress;
//[Browsable(true), Category("键"), Description("用户按下某个键后发生")]
//public event EventHandler KeyDown;
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ShowQuery
{
get => _showQuery;
set
{
_showQuery = value;
CalcSize();
Refresh();
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ShowDel
{
get => _showDel;
set
{
_showDel = value;
CalcSize();
Refresh();
}
}
[Browsable(false), Category(""), Description("选中的文本起始位置")]
public int SelectionStart { set => ctlText.SelectionStart = value; get => ctlText.SelectionStart; }
[Browsable(false), Category(""), Description("选中的文本长度")]
public int SelectionLength { set => ctlText.SelectionLength = value; get => ctlText.SelectionLength; }
[Browsable(false), Category(""), Description("选中的文本")]
public string SelectedText { set => ctlText.SelectedText = value; get => ctlText.SelectedText; }
[Browsable(true), Category("行为"), Description("控件中输入的最大字符数")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaxLength { set => ctlText.MaxLength = value; get => ctlText.MaxLength; }
[Browsable(true), Category("行为"), Description("控制能否更改编辑控件中的文本"), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReadOnly { set => ctlText.ReadOnly = value; get => ctlText.ReadOnly; }
[Browsable(true), Category("外观"), Description("指示应该如何对齐编辑控件的文本"), DefaultValue(HorizontalAlignment.Left)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public HorizontalAlignment TextAlign { set => ctlText.TextAlign = value; get => ctlText.TextAlign; }
[Browsable(true), Category("外观"), Description("用于显示控件中文本的字体")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
CalcSize();
}
}
[Browsable(true), Category("外观"), Description("与控件关联的文本")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text { get => ctlText.Text; set => ctlText.Text = value; }
[Browsable(true), DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool Multiline
{
get => ctlText.Multiline;
set
{
ctlText.Multiline = value;
CalcSize();
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color ForeColor
{
get => ctlText.ForeColor;
set
{
if (ctlText != null)
ctlText.ForeColor = value;
}
}
public void AppendText(string s)
{
ctlText.AppendText(s);
}
protected override void CalcSize()
{
base.CalcSize();
int w = BorderWidth + 1;
if (ctlDel != null)
{
if (_showDel)
{
ctlDel.Width = 30;
ctlDel.Height = (int)borderRect.Height - 3;
ctlDel.Left = Width - ctlDel.Width - w;
ctlDel.Top = BorderWidth + 1;
w += ctlDel.Width;
}
ctlDel.Visible = _showDel;
}
if (ctlQuery != null)
{
if (_showQuery)
{
ctlQuery.Width = 30;
ctlQuery.Height = (int)borderRect.Height - 3;
ctlQuery.Left = Width - ctlQuery.Width - w;
ctlQuery.Top = BorderWidth + 1;
w += ctlQuery.Width;
}
ctlQuery.Visible = _showQuery;
}
if (ctlText != null)
{
ctlText.Width = Width - BorderWidth - w - 5;
ctlText.Left = BorderWidth + 3;
if (Multiline)
ctlText.Height = Height - (BorderWidth + 2) * 2;
ctlText.Top = (Height - ctlText.Height) / 2 + 1;
}
}
protected override void DrawEnabled(Graphics g)
{
base.DrawEnabled(g);
}
protected override void DrawDisabled(Graphics g)
{
base.DrawDisabled(g);
}
private void FaceTextBox_GotFocus(object sender, EventArgs e)
{
ctlText.Focus();
}
private void CtlDel_Click(object sender, EventArgs e)
{
ctlText.Text = "";
}
private void CtlDel_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlDel_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void CtlQuery_Click(object sender, EventArgs e)
{
KeyEnterPress?.Invoke(this, e);
}
private void CtlQuery_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlQuery_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
private void CtlText_TextChanged(object sender, EventArgs e)
{
TextChanged?.Invoke(this, new EventArgs());
}
private void CtlText_MouseEnter(object sender, EventArgs e)
{
borderEnter = true;
Refresh();
}
private void CtlText_KeyPress(object sender, KeyPressEventArgs e)
{
OnKeyPress(e);
if (e.KeyChar == '\r')
KeyEnterPress?.Invoke(this, e);
else if (e.KeyChar == '\n')
{ }
else
KeyPress?.Invoke(this, e);
}
private void CtlText_KeyDown(object sender, KeyEventArgs e)
{
OnKeyDown(e);
}
private void CtlText_KeyUp(object sender, KeyEventArgs e)
{
OnKeyUp(e);
}
private void CtlText_MouseLeave(object sender, EventArgs e)
{
borderEnter = false;
Refresh();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
此文件的差异被折叠, 点击展开。
此文件类型无法预览
此文件的差异太大,无法显示。
此文件的差异被折叠, 点击展开。
此文件的差异被折叠, 点击展开。
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件类型无法预览
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
此文件的差异太大,无法显示。
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!