RaUI/Source/ryControls/Sheng.Winform.Controls/ShengComboSelector2/ShengComboSelector2.cs

892 lines
26 KiB
C#
Raw Normal View History

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
}
}