在创建自定义TabControl(名为MainTabControl)时,我遇到了两个问题:
1.无法从Designer中拖放控件,因为当鼠标悬停时,MainTabControl会自动选中,而不是当前的TabPage。当我释放拖动时,我会收到错误。
“无法将”Control“添加到TabControl。只能将TabPages直接添加到TabControls”。
1.当MainTabControl没有选项卡时,不能通过鼠标单击来选择它,只有套索选择才有效。
设计师
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace ThunderbirdLibrary.CustomControls
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class MainTabControlDesigner : ParentControlDesigner
{
DesignerActionListCollection _actionLists;
DesignerVerbCollection _verbs;
IDesignerHost _designerHost;
IComponentChangeService _changeService;
bool clicked = false;
public override DesignerActionListCollection ActionLists
{
get
{
if (_actionLists == null)
{
_actionLists = new DesignerActionListCollection
{
new MainTabControlActionList(this.Component)
};
}
return _actionLists;
}
}
public override DesignerVerbCollection Verbs
{
get
{
if(_verbs == null)
{
_verbs = new DesignerVerbCollection()
{
new DesignerVerb("Add Tab", new EventHandler(OnAddTab)),
new DesignerVerb("Remove Tab", new EventHandler(OnRemoveTab))
};
MainTabControl mainTabControl = Control as MainTabControl;
if (mainTabControl != null)
{
if (mainTabControl.TabPages.Count == 0) _verbs[1].Enabled = false;
else _verbs[1].Enabled = true;
}
}
return _verbs;
}
}
public override void Initialize(IComponent component)
{
base.Initialize(component);
_designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (_changeService != null)
_changeService.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged);
}
public override void InitializeNewComponent(IDictionary defaultValues)
{
base.InitializeNewComponent(defaultValues);
try
{
MainTabControl control = Control as MainTabControl;
OnAddTab(this, EventArgs.Empty);
control.SelectedIndex = 0;
((MainTabPage)control.TabPages[0]).ImageName = "AccountSetup";
}
catch
{
throw new Exception("Couldn't initialize MainTabControl with MainTabPage");
}
}
// Properties Window by default is updated only after select and deselect the control
// Here "ComponentChanging" and "ComponentChanged" comes in play and notify IDE designer about the changes
public void OnAddTab(object sender, EventArgs e)
{
MainTabControl parentControl = Control as MainTabControl;
DesignerTransaction transaction = null;
try
{
transaction = _designerHost.CreateTransaction("Add Tab");
RaiseComponentChanging(TypeDescriptor.GetProperties(parentControl)["TabPages"]);
MainTabPage newPage = (MainTabPage)_designerHost.CreateComponent(typeof(MainTabPage));
newPage.Text = newPage.Name;
parentControl.TabPages.Add(newPage);
parentControl.SelectedTab = newPage;
RaiseComponentChanged(TypeDescriptor.GetProperties(parentControl)["TabPages"], null, null);
transaction.Commit();
}
catch
{
MessageBox.Show("Exception occured during adding the tab");
transaction?.Cancel();
}
}
public void OnRemoveTab(object sender, EventArgs e)
{
MainTabControl parentControl = Control as MainTabControl;
DesignerTransaction transaction = null;
try
{
transaction = _designerHost.CreateTransaction("Remove Tab");
RaiseComponentChanging(TypeDescriptor.GetProperties(parentControl)["TabPages"]);
_designerHost.DestroyComponent(parentControl.SelectedTab);
RaiseComponentChanged(TypeDescriptor.GetProperties(parentControl)["TabPages"], null, null);
transaction.Commit();
}
catch
{
MessageBox.Show("Exception occured during removing the tab");
transaction?.Cancel();
}
}
public void OnComponentChanged(object sender, ComponentChangedEventArgs e)
{
MainTabControl parentControl = e.Component as MainTabControl;
if(parentControl != null && e.Member.Name == "TabPages")
{
foreach (DesignerVerb verb in Verbs)
{
if(verb.Text == "Remove Tab")
{
if (parentControl.TabPages.Count == 0) verb.Enabled = false;
else verb.Enabled = true;
}
}
}
}
// Determine whether to pass click to the control
protected override bool GetHitTest(Point point)
{
ISelectionService selectionService = (ISelectionService)GetService(typeof(ISelectionService));
object selectedObject = selectionService.PrimarySelection;
return selectedObject != null && selectedObject.Equals(Control);
}
}
}
字符串
MainTabControl.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media.Animation;
namespace ThunderbirdLibrary.CustomControls;
[Designer(typeof(MainTabControlDesigner))]
public partial class MainTabControl : TabControl
{
#region Private Members
private readonly Size DefaultItemSize;
//private int mouseAreaTriggerOffset = 3;
private readonly int standardOffset;
private Rectangle imageRec;
private Rectangle textRec;
private Rectangle closeButtonRec;
private Rectangle closeAreaRec;
private int ellipsisWidth;
private bool isRecalculatingTabSize;
private Point mouseDownLocation;
#endregion
#region Properties
[Editor(typeof(MainTabPageCollectionEditor), typeof(UITypeEditor))]
public new TabPageCollection TabPages
{
get
{
return base.TabPages;
}
}
#endregion
#region Constructor
public MainTabControl()
{
InitializeComponent();
this.DrawMode = TabDrawMode.OwnerDrawFixed;
this.SizeMode = TabSizeMode.Fixed;
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
isRecalculatingTabSize = false;
// Set Sizes of header parts that (probably) won't be recalculated
DefaultItemSize = new Size(250, 33);
standardOffset = (int)(this.DefaultItemSize.Width * 0.02);
imageRec = new Rectangle(
standardOffset,
standardOffset,
DefaultItemSize.Height / 2,
DefaultItemSize.Height / 2);
closeAreaRec = new Rectangle(
DefaultItemSize.Width - standardOffset - DefaultItemSize.Height / 2,
(DefaultItemSize.Height - DefaultItemSize.Height / 2) / 2,
DefaultItemSize.Height / 2,
DefaultItemSize.Height / 2);
using (Graphics g = this.CreateGraphics())
{
closeButtonRec = new Rectangle(
new Point(
DefaultItemSize.Width - closeAreaRec.Width - standardOffset + closeButtonRec.Width / 4,
(DefaultItemSize.Height - closeAreaRec.Height) / 2
),
g.MeasureString("x", Font).ToSize());
ellipsisWidth = (int)g.MeasureString("...", Font).Width;
textRec = new Rectangle(
2 * standardOffset + imageRec.Width,
(int)((DefaultItemSize.Height - g.MeasureString("Test String", Font).Height) / 2),
0,
0);
}
}
#endregion
#region EventHandler
protected override void OnControlRemoved(ControlEventArgs e)
{
base.OnControlRemoved(e);
if (e.Control is MainTabPage)
{
RecalculateTabSize();
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if(e.Control is MainTabPage)
{
RecalculateTabSize();
}
}
protected override void OnSizeChanged(EventArgs e)
{
if (!isRecalculatingTabSize)
{
base.OnSizeChanged(e);
isRecalculatingTabSize = true;
RecalculateTabSize();
isRecalculatingTabSize = false;
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
mouseDownLocation = e.Location;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
for (int i = 0; i < this.TabCount; i++)
{
Rectangle tabRec = this.GetTabRect(i);
Rectangle absoluteCloseArea = new Rectangle()
{
Size = closeAreaRec.Size,
Location = new Point()
{
X = tabRec.X + closeAreaRec.X,
Y = tabRec.Y + ItemSize.Height / 4
}
};
MainTabPage page = TabPages[i] as MainTabPage;
if (absoluteCloseArea.Contains(e.Location))
{
page.isMouseOverCloseArea = true;
Invalidate(absoluteCloseArea);
}
else if (page.isMouseOverCloseArea)
{
page.isMouseOverCloseArea = false;
Invalidate(absoluteCloseArea);
}
}
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
for (int i = 0; i < this.TabCount; i++)
{
Rectangle tabRec = this.GetTabRect(i);
Rectangle absoluteCloseArea = new Rectangle()
{
Size = closeAreaRec.Size,
Location = new Point()
{
X = tabRec.X + closeAreaRec.X,
Y = tabRec.Y + ItemSize.Height / 4
}
};
if (absoluteCloseArea.Contains(e.Location) && absoluteCloseArea.Contains(mouseDownLocation))
{
TabPages.RemoveAt(i);
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
for(int i = 0; i < TabCount; i++)
{
MainTabPage mainTabPage = TabPages[i] as MainTabPage;
if(mainTabPage != null)
{
Rectangle tabRec = this.GetTabRect(i);
// Background
using (Brush recBrush = new SolidBrush(i == SelectedIndex ?
mainTabPage.SelectedTabBackColor : mainTabPage.TabBackColor))
using (Pen recPen = new Pen(Brushes.Black))
{
e.Graphics.FillRectangle(recBrush, tabRec);
e.Graphics.DrawRectangle(recPen, tabRec);
}
// Image
if (mainTabPage.HasImage)
{
using(Image image = (Image)Resource.ResourceManager.GetObject(mainTabPage.ImageName))
{
e.Graphics.DrawImage(image, new Rectangle(
tabRec.Left + imageRec.X,
tabRec.Top + imageRec.Y,
imageRec.Width,
imageRec.Height));
}
}
// Text
bool isTabSelected = i == this.SelectedIndex;
var trimmingResult = TrimString(textRec.Size, Font, mainTabPage.Text, isTabSelected, e.Graphics);
e.Graphics.DrawString(trimmingResult.text, Font, Brushes.White,
tabRec.Left + textRec.X, tabRec.Top + textRec.Y);
// Close button AND Close Area
if (mainTabPage.IsClosable && trimmingResult.isCloseButton)
{
if (mainTabPage.isMouseOverCloseArea)
{
SolidBrush closeAreaColor = (i == this.SelectedIndex)
? new SolidBrush(mainTabPage.SelectedtabCloseAreaColor)
: new SolidBrush(mainTabPage.TabCloseAreaColor);
e.Graphics.FillRectangle(closeAreaColor, new Rectangle(
tabRec.Left + closeAreaRec.X,
tabRec.Top + closeAreaRec.Y,
closeAreaRec.Width,
closeAreaRec.Height));
closeAreaColor.Dispose();
}
e.Graphics.DrawString("x", Font, Brushes.White,
tabRec.Left + closeButtonRec.X, tabRec.Top + closeButtonRec.Y);
}
// Upperline
if (mainTabPage.IsUpperLine && isTabSelected)
{
using (Pen pen = new Pen(mainTabPage.SelectedTabUpperLine, 3))
{
e.Graphics.DrawLine(pen, tabRec.Left + standardOffset, 4, tabRec.Right - standardOffset, 4);
}
}
}
}
}
#endregion
#region Helper Methods
private (string text, bool isCloseButton) TrimString(Size maxSize, Font font, string text, bool isSelected, Graphics g)
{
int currentWidth = (int)g.MeasureString(text, font).Width;
if (currentWidth < textRec.Width)
{
return (text, true);
}
else
{
// Decide if text area merge with close button area size
int availableWidthForText = isSelected ? textRec.Width : textRec.Width + standardOffset + closeAreaRec.Width;
while (currentWidth + ellipsisWidth >= availableWidthForText && text.Length > 0)
{
text = text.Substring(0, text.Length - 1);
currentWidth = (int)g.MeasureString(text, font).Width;
}
return (string.Concat(text, "..."), false || isSelected);
}
}
public void RecalculateTabSize()
{
if (TabPages.Count == 0)
return;
int averageTabWidth = this.Size.Width / this.TabPages.Count;
if (averageTabWidth <= 0)
return;
this.ItemSize = averageTabWidth > DefaultItemSize.Width ?
DefaultItemSize : new Size(averageTabWidth - 1, DefaultItemSize.Height);
// Recalculate text, closeArea and close Button Rectangles
textRec = new Rectangle(
textRec.X,
textRec.Y,
this.ItemSize.Width - 4 * standardOffset - imageRec.Width - closeAreaRec.Width,
this.ItemSize.Height / 2);
closeAreaRec = new Rectangle(
ItemSize.Width - standardOffset - ItemSize.Height / 2,
(ItemSize.Height - ItemSize.Height / 2) / 2,
ItemSize.Height / 2,
ItemSize.Height / 2);
closeButtonRec = new Rectangle(
new Point(
ItemSize.Width - closeAreaRec.Width - standardOffset + closeButtonRec.Width / 4,
(ItemSize.Height - closeAreaRec.Height) / 2),
closeAreaRec.Size);
}
#endregion
}
型
1条答案
按热度按时间1tu0hz3e1#
我找到了一个变通办法。