RaUI/Source/ryControls/Sheng.Winform.Controls/ShengComboSelector2/ShengComboSelector2.cs
鑫Intel c3d4ddf574 ### 2021-07-29更新
------
#### MyDbV4   V3.0.2107.2901
- *.[新增]新增支持计算文件MD5。
- *.[新增]部分DataProvider功能移植到DbExtension里,增加扩展性。
- *.[新增]UnixTimeToDateTime和JSTimeToDateTime新增支持long参数。
- *.[合并]合并RyWeb项目到MyDb里。

#### ryControlsV4    V3.0.2107.2901
  -  *.[改进]优化减少大量IDE警告和消息。
2021-07-29 17:09:32 +08:00

892 lines
26 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using Sheng.Winform.Controls.PopupControl;
using System.Collections;
using Sheng.Winform.Controls.Kernal;
using Sheng.Winform.Controls.Drawing;
using System.ComponentModel;
namespace Sheng.Winform.Controls
{
/*
* 控件的高度根据内容自动调整,忽略外部设置
*/
/// <summary>
/// 控件的高度根据内容自动调整,忽略外部设置
/// </summary>
public class ShengComboSelector2 : Control, IShengValidate
{
#region
/// <summary>
/// 边框宽度
/// </summary>
const int _borderSize = 1;
/*
* 下拉框弹出之后,再次点击控件,下拉框又会闪出来
* 是因为点控件,下拉框自动关闭,但因为点了控件,所以下拉框又会闪出来
* 所以在下拉框关闭时,记下时间,点击时判断与上次下拉框关闭时的时间间隔
* 如果过小,就不打开下拉框
*/
private DateTime _dropDownHideTime;
private readonly Popup _popup;
private readonly ShengListView _listView;
#region
private ShengComboSelectorState _state = ShengComboSelectorState.None;
private bool Selected
{
get
{
return (_state & ShengComboSelectorState.Selected) == ShengComboSelectorState.Selected;
}
set
{
bool selected = Selected;
if (value)
_state |= ShengComboSelectorState.Selected;
else
_state ^= ShengComboSelectorState.Selected;
if (selected != Selected)
Refresh();
}
}
private bool Hovered
{
get
{
return (_state & ShengComboSelectorState.Hovered) == ShengComboSelectorState.Hovered;
}
set
{
bool hovered = Hovered;
if (value)
_state |= ShengComboSelectorState.Hovered;
else
_state ^= ShengComboSelectorState.Hovered;
if (hovered != Hovered)
Refresh();
}
}
#endregion
#region
//箭头的大小是指的箭头的高度宽度将是高度的2倍
private readonly int _buttonSize =6;
/// <summary>
/// 文本和description文本之间的间距
/// </summary>
private readonly int _textSpaceBetween = 2;
private readonly TextFormatFlags _textFlags = TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
#endregion
#endregion
#region
private int _maxItem = 5;
/// <summary>
/// 最大显示的项数
/// </summary>
public int MaxItem
{
get { return _maxItem; }
set { _maxItem = value; }
}
private ShengComboSelectorTheme _theme = new ShengComboSelectorTheme();
/// <summary>
///
/// </summary>
public ShengComboSelectorTheme Theme
{
get { return _theme; }
set { _theme = value; }
}
private bool _showDescription = true;
/// <summary>
/// 是否显示说明字段,此属性不影响弹出面板是否显示
/// 弹出面板是否显示由DescriptionMember是否设置决定
/// </summary>
public bool ShowDescription
{
get { return _showDescription; }
set
{
_showDescription = value;
this.Height = MeasureHeight();
}
}
/// <summary>
///
/// </summary>
public string DisplayMember
{
get { return _listView.DisplayMember; }
set { _listView.DisplayMember = value; }
}
private string _descriptionMember;
/// <summary>
///
/// </summary>
public string DescriptionMember
{
get { return _descriptionMember; }
set
{
_descriptionMember = value;
_listView.SetExtendMember(ShengListViewDescriptiveMembers.DescriptioinMember, value);
}
}
/// <summary>
/// 下拉列表的布局模式
/// </summary>
public ShengListViewLayoutMode LayoutMode
{
get { return _listView.LayoutMode; }
set { _listView.LayoutMode = value; }
}
/// <summary>
///
/// </summary>
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
this.Height = MeasureHeight();
}
}
#region
private bool allowEmpty = true;
/// <summary>
/// 是否允许空
/// </summary>
[Description("是否允许空")]
[Category("Sheng.Winform.Controls")]
public bool AllowEmpty
{
get
{
return this.allowEmpty;
}
set
{
this.allowEmpty = value;
}
}
#endregion
#endregion
#region
/// <summary>
///
/// </summary>
public ShengComboSelector2()
{
if (DesignMode)
return;
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.Selectable, true);
this.Padding = new System.Windows.Forms.Padding(5);
_listView = new ShengListView
{
Padding = new System.Windows.Forms.Padding(0),
BorderStyle = BorderStyle.None,
AllowMultiSelection = false,
LayoutMode = ShengListViewLayoutMode.Descriptive
};
//SelectedItemChanaged事件不行如果就点当前已选中的项就关不掉下拉框了
_listView.Click += new EventHandler(ListView_Click);
_listView.SelectedItemChanaged += new EventHandler(ListView_SelectedItemChanaged);
_listView.ItemTextGetting += new EventHandler<ShengListViewGetItemTextEventArgs>(ListView_ItemTextGetting);
_popup = new Popup(_listView);
_popup.Closed += new ToolStripDropDownClosedEventHandler(Popup_Closed);
this._dropDownHideTime = DateTime.UtcNow;
ApplyTheme();
}
#endregion
#region
void ListView_Click(object sender, EventArgs e)
{
_popup.Close();
}
void ListView_SelectedItemChanaged(object sender, EventArgs e)
{
object value = GetSelectedValue();
OnSelectedValueChanged(value);
}
void ListView_ItemTextGetting(object sender, ShengListViewGetItemTextEventArgs e)
{
if (this.ItemTextGetting != null)
{
ItemTextGettingEventArgs args = new ItemTextGettingEventArgs(e.Item);
ItemTextGetting(this, args);
e.Text = args.Text;
}
}
void Popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
_dropDownHideTime = DateTime.UtcNow;
this.Selected = false;
}
#endregion
#region
#region private
private void ApplyTheme()
{
this.BackColor = _theme.BackgroundColor;
_listView.Theme.HoverColorStart = _theme.HoveredBackColor;
_listView.Theme.HoverColorEnd = Color.FromArgb(125, _theme.HoveredBackColor);
_listView.Theme.ItemBorderColor = _theme.HoveredBorderColor;
_listView.Theme.SelectedColorStart = _theme.SelectedBackColor;
_listView.Theme.SelectedColorEnd = Color.FromArgb(125, _theme.HoveredBackColor);
}
private void OnSelectedValueChanged(object value)
{
//允许value为null
//从原来有选定的变为没选定了,事件肯定还是要触发的
if (this.SelectedValueChanged != null)
{
SelectedValueChanged(this, new OnSelectedValueChangedEventArgs(value));
}
}
/// <summary>
/// 测量当前控件应该的高度
/// </summary>
/// <returns></returns>
private int MeasureHeight()
{
int textHeight = this.FontHeight;
int height = _borderSize * 2 + textHeight + Padding.Top + Padding.Bottom;
if (ShowDescription)
height = height + textHeight + _textSpaceBetween;
return height;
}
/// <summary>
/// 整个可用的绘图表面
/// </summary>
private Rectangle GetBorderRectangle()
{
Rectangle rect = new Rectangle(this.ClientRectangle.Location, this.ClientRectangle.Size);
rect.Width--;
rect.Height--;
return rect;
}
private Rectangle GetContentRectangle()
{
Rectangle contentRectangle = GetBorderRectangle();
//return Rectangle.Inflate(contentRectangle, -1, -1);
contentRectangle.X += 1;
contentRectangle.Y += 1;
contentRectangle.Width -= 1;
contentRectangle.Height -= 1;
return contentRectangle;
}
/// <summary>
/// 右边按钮的绘图表面
/// </summary>
private Rectangle GetButtonAreaRectangle()
{
Rectangle clientRectangle = ClientRectangle;
Rectangle rectangle = new Rectangle
{
//箭头的大小是指的箭头的高度宽度将是高度的2倍
Size = new Size(_buttonSize * 2, _buttonSize)
};
rectangle.Location = new Point(
clientRectangle.Width - rectangle.Width - Padding.Right,
(clientRectangle.Height - rectangle.Height) / 2);
return rectangle;
}
private Rectangle GetTextRectangle()
{
Rectangle clientRectangle = GetContentRectangle();
Rectangle buttonRectangle = GetButtonAreaRectangle();
Rectangle textRectangle = new Rectangle
{
Location = new Point(Padding.Left, clientRectangle.Y + Padding.Top)
};
textRectangle.Width = clientRectangle.Width - (clientRectangle.Width - buttonRectangle.Left)
- textRectangle.Left - 5; //多减5为字和按钮的间距
textRectangle.Height = this.FontHeight;
return textRectangle;
}
private Rectangle GetDescriptionRectangle()
{
Rectangle clientRectangle = GetContentRectangle();
Rectangle textRectangle = GetTextRectangle();
Rectangle buttonRectangle = GetButtonAreaRectangle();
Rectangle descriptionRectangle = new Rectangle();
int descriptionY = textRectangle.Y + textRectangle.Height + _textSpaceBetween;
descriptionRectangle.Location = new Point(Padding.Left, descriptionY);
descriptionRectangle.Width = clientRectangle.Width - (clientRectangle.Width - buttonRectangle.Left)
- descriptionRectangle.Left - 5; //多减5为字和按钮的间距
descriptionRectangle.Height = this.FontHeight;
return descriptionRectangle;
}
private void Render(Graphics g)
{
DrawBorderground(g);
DrawBackground(g);
DrawText(g);
DrawButton(g);
//DrawFocusRectangle(g);
}
/// <summary>
/// 绘制边框
/// </summary>
/// <param name="g"></param>
private void DrawBorderground(Graphics g)
{
Rectangle borderRectangle = ClientRectangle;
//using (Pen borderPen = new Pen(Theme.BorderColor))
//{
// g.DrawRectangle(borderPen, borderRectangle);
//}
Brush brush;
if (this.Selected)
brush = _theme.CreateSelectedBorderBrush(borderRectangle);
else if(this.Hovered)
brush = _theme.CreateHoveredBorderBrush(borderRectangle);
else
brush = _theme.CreateBorderBrush(borderRectangle);
g.FillRectangle(brush, borderRectangle);
Rectangle rectangle = borderRectangle;
rectangle.X += 1;
rectangle.Y += 1;
rectangle.Width -= 2;
rectangle.Height -= 2;
g.FillRectangle(Brushes.White, rectangle);
}
/// <summary>
/// 绘制背景
/// </summary>
/// <param name="g"></param>
private void DrawBackground(Graphics g)
{
Rectangle rectangle = GetContentRectangle();
if (this.Hovered && this.Selected == false)
{
//留出一个白色的内框
rectangle.X += 1;
rectangle.Y += 1;
rectangle.Width -= 2;
rectangle.Height -= 2;
}
Brush brush;
if (this.Enabled)
{
if (this.Selected)
brush = _theme.CreateSelectedBackgroundBrush(rectangle);
else if (this.Hovered)
brush = _theme.CreateHoveredBackgroundBrush(rectangle);
else
{
//在正常状态下,还是要把指定的控件背景色考虑进去
//比如在验证控件数据的时候,不合法数据会有一个突出的颜色显示
//如现在会把不合法数据的控件背景色改成粉色
using (Brush normalBackgroundBrush = new SolidBrush(this.BackColor))
{
g.FillRectangle(normalBackgroundBrush, rectangle);
}
//这个CreateBackgroundBrush上面大部分是透明色下面是一个淡灰色
brush = _theme.CreateBackgroundBrush(rectangle);
}
}
else
{
brush = _theme.CreateDisabledBackgroundBrush(rectangle);
}
g.FillRectangle(brush, rectangle);
}
/// <summary>
/// 绘制文本
/// </summary>
/// <param name="g"></param>
private void DrawText(Graphics g)
{
object selectedValue = GetSelectedValue();
if (selectedValue == null)
return;
#region
object textObj = ReflectionPool.GetPropertyValue(selectedValue, DisplayMember);
if (textObj != null)
{
string text = textObj.ToString();
if (text != String.Empty)
{
Color textColor;
if (this.Selected)
textColor = _theme.SelectedTextColor;
else if (this.Hovered)
textColor = _theme.HoveredTextColor;
else
textColor = _theme.TextColor;
Font textFont = new System.Drawing.Font(this.Font, FontStyle.Bold);
Rectangle textRectangle = GetTextRectangle();
// g.FillRectangle(Brushes.Red, textRectangle);
TextRenderer.DrawText(g, text, textFont, textRectangle, textColor, _textFlags);
}
}
#endregion
if (this.ShowDescription)
{
#region Description
object descriptionObj = ReflectionPool.GetPropertyValue(selectedValue, DescriptionMember);
if (descriptionObj != null)
{
string description = descriptionObj.ToString();
if (description != String.Empty)
{
Color textColor;
if (this.Selected)
textColor = _theme.SelectedDescriptionTextColor;
else if (this.Hovered)
textColor = _theme.HoveredDescriptionColor;
else
textColor = _theme.DescriptionTextColor;
Rectangle descriptionRectangle = GetDescriptionRectangle();
// g.FillRectangle(Brushes.Red, descriptionRectangle);
TextRenderer.DrawText(g, description, this.Font, descriptionRectangle, textColor, _textFlags);
}
}
#endregion
}
}
private void DrawButton(Graphics g)
{
Rectangle rectangle = GetButtonAreaRectangle();
// g.FillRectangle(Brushes.Red, rectangle);
int arrowX = rectangle.Left + (rectangle.Width - rectangle.Width / 2);
int arrowY = rectangle.Y;
Point startPoint = new Point(arrowX, arrowY);
Point endPoint = new Point(arrowX, arrowY + rectangle.Height);
GraphicsPath arrowPath = DrawingTool.GetArrowPath(startPoint, endPoint, rectangle.Height);
using (Brush arrowBrush = new LinearGradientBrush(rectangle,
_theme.ArrowColorStart, _theme.ArrowColorEnd, 45))
{
g.FillPath(arrowBrush, arrowPath);
}
arrowPath.Dispose();
}
private void DrawFocusRectangle(Graphics g)
{
if (this.Focused)
ControlPaint.DrawFocusRectangle(g, GetBorderRectangle());
}
#endregion
#region protected
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnSizeChanged(EventArgs e)
{
this.Height = MeasureHeight();
base.OnSizeChanged(e);
}
/// <summary>
///
/// </summary>
/// <param name="pevent"></param>
protected override void OnPaintBackground(PaintEventArgs pevent)
{
pevent.Graphics.Clear(_theme.BackgroundColor);
// base.OnPaintBackground(pevent);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
Render(e.Graphics);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnMouseEnter(EventArgs e)
{
if (this.Enabled)
{
this.Hovered = true;
}
base.OnMouseEnter(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnMouseLeave(EventArgs e)
{
if (this.Enabled)
{
//if (_popup.Visible == false)
//{
this.Hovered = false;
//}
}
base.OnMouseLeave(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnEnter(EventArgs e)
{
//this.Selected = true;
base.OnEnter(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnLeave(EventArgs e)
{
//this.Selected = false;
base.OnLeave(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnGotFocus(EventArgs e)
{
this.Refresh();
base.OnGotFocus(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnLostFocus(EventArgs e)
{
this.Refresh();
base.OnLostFocus(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
this.Focus();
if ((DateTime.UtcNow - _dropDownHideTime).TotalSeconds > 0.2)
{
Selected = true;
ShowDropDown();
}
}
private void ShowDropDown()
{
//每次打开都要判读如果把PopupWidth做成属性在set里设置
//那么控件resize时popup的宽度可能就不对了
_popup.Width = this.Width;
int showItem;
int itemCount = _listView.Items.Count;
if (itemCount > this.MaxItem)
showItem = this.MaxItem;
else if (itemCount == 0)
showItem = 1;
else
showItem = itemCount;
_popup.Height = showItem * _listView.ItemHeight;
_popup.Height = _listView.Height;
_popup.Show(this);
_popup.Select();
}
#endregion
#region public
/// <summary>
///
/// </summary>
/// <param name="dataSource"></param>
public void DataBind(IList dataSource)
{
_listView.DataBind(dataSource);
this.Refresh();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public object GetSelectedValue()
{
return _listView.GetSelectedValue();
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
public void SetSelectedValue(object obj)
{
_listView.SetSelectedValue(obj);
}
/// <summary>
///
/// </summary>
public void Clear()
{
_listView.Clear();
this.Refresh();
}
#endregion
#endregion
#region
/// <summary>
/// 当前热点项生改变
/// </summary>
public event EventHandler<OnSelectedValueChangedEventArgs> SelectedValueChanged;
/// <summary>
///
/// </summary>
public class OnSelectedValueChangedEventArgs:EventArgs
{
private readonly object _value;
/// <summary>
///
/// </summary>
public object Value
{
get { return _value; }
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
public OnSelectedValueChangedEventArgs(object value)
{
_value = value;
}
}
/// <summary>
/// 通过外能事件获取用于绘制项的文本
/// </summary>
public event EventHandler<ItemTextGettingEventArgs> ItemTextGetting;
/// <summary>
///
/// </summary>
public class ItemTextGettingEventArgs : EventArgs
{
/// <summary>
///
/// </summary>
public object Item { get; private set; }
/// <summary>
///
/// </summary>
public string Text { get; set; }
/// <summary>
///
/// </summary>
/// <param name="item"></param>
public ItemTextGettingEventArgs(object item)
{
Item = item;
}
}
#endregion
#region ISEValidate
private string title;
/// <summary>
/// 标题
/// </summary>
[Description("标题")]
[Category("Sheng.Winform.Controls")]
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
}
}
private bool highLight = true;
/// <summary>
/// 验证失败时是否需要高亮显示(改变背景色)
/// </summary>
[Description("验证失败时是否需要高亮显示(改变背景色)")]
[Category("Sheng.Winform.Controls")]
public bool HighLight
{
get
{
return this.highLight;
}
set
{
this.highLight = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public bool SEValidate(out string msg)
{
msg = String.Empty;
if (this.AllowEmpty == false && this.GetSelectedValue() == null)
{
msg += String.Format("[ {0} ] {1}", this.Title, "不允许为空");
return false;
}
#region CustomValidate
if (CustomValidate != null)
{
if (CustomValidate(this, out string customValidateMsg) == false)
{
msg += String.Format("[ {0} ] {1}", this.Title, customValidateMsg);
return false;
}
}
#endregion
return true;
}
/// <summary>
///
/// </summary>
public CustomValidateMethod CustomValidate
{
get;
set;
}
#endregion
}
}