⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 article.htm

📁 outlookbar 一款不错的C#开发控件
💻 HTM
📖 第 1 页 / 共 3 页
字号:
band.  Whenever a band is added, the "selected band height" must be 
recalculated.  This is implemented as a method because it'll be used later 
as part of button height changes and window size changes:</p>
<pre>private void UpdateBarInfo()
{
	selectedBandHeight=ClientRectangle.Height-(Controls.Count * buttonHeight);
}
</pre>
<p>
The band layout calculation must now take into account the placement of the band, using the criteria:<ul>
  <li>Is this band before the selected band?</li>
  <li>Is this band the selected band?</li>
  <li>Is this band after the selected band?</li>
  </ul>
  <p>
<pre>private void RecalcLayout(BandPanel bandPanel, int index)
{
	<span style="background-color: #FF00FF">int vPos=(index <= selectedBand) ? buttonHeight*index : buttonHeight*index+selectedBandHeight;</span>
	<span style="background-color: #FF00FF">int height=selectedBand==index ? selectedBandHeight : buttonHeight;</span>

	// the band dimensions
	bandPanel.Location=new Point(0, <span style="background-color: #FF00FF">vPos</span>);
	bandPanel.Size=new Size(ClientRectangle.Width, <span style="background-color: #FF00FF">height</span>);

	// the contained button dimensions
	bandPanel.Controls[0].Location=new Point(0, 0);
	bandPanel.Controls[0].Size=new Size(ClientRectangle.Width, buttonHeight);

	// the contained content panel dimensions
	bandPanel.Controls[1].Location=new Point(0, buttonHeight);
	bandPanel.Controls[1].Size=new Size(ClientRectangle.Width, <span style="background-color: #FF00FF">height</span>);
}</pre>
This results in some wonderfully functional capability and pretty much completes 
the band activation issue.  The Outlook bar's buttons can now be clicked on, and they move around as required:<p>
<img src="CSOutlookBar\screenShot4.jpg" width="312" height="288"> <p>

One minor detail.&nbsp; The <code>AddBand</code> method can no longer simply set 
up the location and size of the newly added band, because this influences the 
height of the first (selected) band.&nbsp; As a result,
the application is responsible now for selecting the desired band after all the bands have been created.  Nothing like changing the rules on the fly, is there!<p>

<p><b>Application Resizing</b></p>
Of major concern at this point is: what happens to the bar when the containing form is resized?  For this, an event handler must be added.  Another wonderful feature
of .NET's event mechanism is that multiple handlers can be associated with the same event.  So regardless of other resizing handlers associated with the form, the Outlook
bar can add its own:<p>
<pre>public void Initialize()
{
	// parent must exist!
	Parent.SizeChanged+=new EventHandler(SizeChangedEvent);
}
<pre>Unfortunately, because this must be done after the Outlook bar has been added to the form (or other control), it requires a separate initialization method so that the
parent is guaranteed to exist.<p>

The event handler itself is very straightforward.  It recalculates the Outlook bar panel size, updates the selected band height, and calls 
the <code>RedrawBands</code> method: <pre>private void SizeChangedEvent(object sender, EventArgs e)
{
	Size=new Size(Size.Width, ((Control)sender).ClientRectangle.Size.Height);
	UpdateBarInfo();
	RedrawBands();
}
</pre>
Note that this handler only deals with vertical resizing.  A separate mechanism is required if you wish to deal with horizontal resizing,
for example if the control is contained in a splitter window.
<h3><a name="#IconPanel">IconPanel</a></h3>

Time for a little design.  The icon panel should have the following features:<p>

<ul><li>Icons are placed in centered down the midline of the panel;</li>
<li>Icons have captions;</li>
<li>Clicking on an icon triggers an event that is supplied by the application;</li>
<li>Icons are ordered from top to bottom in the order that they are added to the band;</li>
<li>The icon panel defaults to a white background;</li><li>Icons need to support transparency.</li>
</ul><p>First off, setting the background color of the panel to white reveals a bug in the size calculation of the parent band panel.  
This is corrected to include both the button height and content panel height:</p>
<pre>private void RecalcLayout(BandPanel bandPanel, int index)
{
	int vPos=(index <= selectedBand) ? buttonHeight*index : buttonHeight*index+selectedBandHeight;
	int height=selectedBand==index ? <span style="background-color: #FF00FF">selectedBandHeight+buttonHeight</span> : buttonHeight;
	...
</pre>

Icons in the icon panel consist of the icon image itself and a caption.  Both are centered.  The caption must be implemented as a <code>Label</code> contained by the
<code>IconPanel</code>.  Thus, for every icon in the icon panel, there are two controls created: the <code>PanelIcon</code> (derived from <code>PictureBox</code>) and the
caption, which is instantiated as a <code>Label</code>.  The code ends up looking like this:<p>
<pre>public class IconPanel : ContentPanel
{
	protected int iconSpacing;
	protected int margin;

	public int IconSpacing
	{
		get
		{
			return iconSpacing;
		}
	}

	public int Margin
	{
		get
		{
			return margin;
		}
	}

	public IconPanel()
	{
		margin=10;
		iconSpacing=32+15+10;	// icon height + text height + margin
		BackColor=Color.White;
		AutoScroll=true;
	}

	public void AddIcon(string caption, Image image, EventHandler onClickEvent)
	{
		int index=Controls.Count/2;	// two entries per icon
		PanelIcon panelIcon=new PanelIcon(this, image, index, onClickEvent);
		Controls.Add(panelIcon);

		Label label=new Label();
		label.Text=caption;
		label.Visible=true;
		label.Location=new Point(0, margin+image.Size.Height+index*iconSpacing);
		label.Size=new Size(Size.Width, 15);
		label.TextAlign=ContentAlignment.TopCenter;
		label.Click+=onClickEvent;
		label.Tag=panelIcon;
		Controls.Add(label);
	}
}

public class PanelIcon : PictureBox
{
	public int index;
	public IconPanel iconPanel;

	public PanelIcon(IconPanel parent, Image image, int index, EventHandler onClickEvent)
	{
		this.index=index;
		this.iconPanel=parent;
		Image=image;
		Visible=true;
		Location=new Point(iconPanel.outlookBar.Size.Width/2-image.Size.Width/2,
			iconPanel.Margin + index*iconPanel.IconSpacing);
		Size=image.Size;
		Click+=onClickEvent;
		Tag=this;
	}
}
</pre>

Note that both the <code>PictureBox</code> and the <code>Label</code> can be clicked.  The <code>Tag</code> property is set to the <code>PanelIcon</code> in both cases,
should the application require some additional information regarding the sender of the event.<p>

Incidentally, changing the background to some other color verifies that icon transparency is functioning correctly.<p>

During this implementation of the icon bar, certain design issues have come up:<p>

<ul><li>Can the application specify the caption font?</li><li>Can the application specify the caption color?</li>
<li>Can the application change the background color of the panel?  What about textures?</li>
<li>Changing the bar width is going to require a lot of recalculations for text and icon centering.</li>
<li>There are no setter functions for the vertical margin and the icon spacing.</li>
<li>The icon spacing needs to be adjusted if we plan to support large icons.</li>
<li>The icon spacing is hard coded and assumes an icon size of 32x32.</li>
</ul><p>
These are all valid issues, but for my needs right now, I don't need to consider them.
(I'd rather go walking on the beach, in other words). <p>
The current implementation results in a nice presentation:</p>
<img src="CSOutlookBar\screenShot5.jpg" width="208" height="358"><p>
For some reason, the scroll bar was slightly off screen to the right and the slightly too long.  The correct appearance:<p>
<img src="CSOutlookBar\screenShot6.jpg" width="208" height="252"><p>
is only achieved by modifying the dimensions of that content panel:<p>
<pre>bandPanel.Controls[1].Size=new Size(ClientRectangle.Width-2, height-8);
</pre>
which I have no explanation for, as the size is being determined by the client area of the band panel.  If anyone else has some ideas, let me know!
<h2><a name="#EyeCandy">Eye Candy</a></h2>


<p>I wanted to implement one piece of &quot;eye candy&quot;, which is to highlight the icon background when the mouse enters the icon area.
My first implementation was straightforward, adding <code>MouseEnter</code> and <code>MouseLeave</code> event handlers to the <code>PanelIcon</code> class:</p>
<pre>private void OnMouseEnter(object sender, EventArgs e)
{
	BackColor=Color.LightCyan;
	BorderStyle=BorderStyle.FixedSingle;
	Location=Location-new Size(1, 1);
}

private void OnMouseLeave(object sender, EventArgs e)
{
	BackColor=bckgColor;
	BorderStyle=BorderStyle.None;
	Location=Location+new Size(1, 1);
}
</pre>
Note that in this code, I'm also setting the border style.  When I first set just the background, everything worked great.  When I added turning on and off a
border around the icon, things started to go wrong.  First off, the icon shifted right and down by one pixel, which wasn't too pleasing visually.  Secondly, the
border wouldn't disappear if the mouse left the icon from the right or the bottom edge.  To fix the shifting of the icon, I simply moved to location up and to the
left on entry and back down and to the right on exit.  However, the icon background was still not being cleared properly, and this only happened when a border was
specified.  As it turns out, the problem has to do with the border, and the fact that as soon as the <code>OnMouseLeave</code> event is called and the icon is
repositioned (forward and down), this results in the <code>OnMouseEnter</code> event being called.  Subsequently, there is no corresponding <code>OnMouseLeave</code>
called again!<p>

The fix to this was to "enter" the icon at a slightly smaller size, using the <code>MouseMove</code> event to determine the mouse position, and to set up a flag
to keep track of whether the mouse was "entered" or not.  The following code illustrates the solution:<p>
<pre>private void OnMouseMove(object sender, MouseEventArgs args)
{
	if ( (args.X < Size.Width-2) &&
		(args.Y < Size.Width-2) &&
		(!mouseEnter) )
	{
		BackColor=Color.LightCyan;
		BorderStyle=BorderStyle.FixedSingle;
		Location=Location-new Size(1, 1);
		mouseEnter=true;
	}
}

private void OnMouseEnter(object sender, EventArgs e)
{
}

private void OnMouseLeave(object sender, EventArgs e)
{
	if (mouseEnter)
	{
		BackColor=bckgColor;
		BorderStyle=BorderStyle.None;
		Location=Location+new Size(1, 1);
		mouseEnter=false;
	}
}
</pre>
This code works great, and now I have a lovely highlighted icon with a border around it when the mouse enters the icon:<p>
<img src="CSOutlookBar\screenShot7.jpg" width="185" height="288"> <p>&nbsp;</p>
<h2><a name="#Conclusion">Conclusion</a></h2>
What?  That's it?  Yes, that's it.  I now have a lovely icon Outlook bar that is quite sufficient for my needs.  There's lots of room for expansion (and error checking!),
but those things can be added in later.  Enough goofing around, it's time to use this thing in my client's application and get some billable hours on the books.<p>
For those interested, the sample bar is generated using a few lines of code in the <code>Form</code> constructor:<p>
<pre>OutlookBar outlookBar=new OutlookBar();
outlookBar.Location=new Point(0, 0);
outlookBar.Size=new Size(150, this.ClientSize.Height);
outlookBar.BorderStyle=BorderStyle.FixedSingle;
Controls.Add(outlookBar);
outlookBar.Initialize();

IconPanel iconPanel1=new IconPanel();
IconPanel iconPanel2=new IconPanel();
IconPanel iconPanel3=new IconPanel();
outlookBar.AddBand("Outlook Shortcuts", iconPanel1);
outlookBar.AddBand("My Shortcuts", iconPanel2);
outlookBar.AddBand("Other Shortcuts", iconPanel3);

iconPanel1.AddIcon("Outlook Today", Image.FromFile("img1.ico"), new EventHandler(PanelEvent));
iconPanel1.AddIcon("Calendar", Image.FromFile("img2.ico"), new EventHandler(PanelEvent));
iconPanel1.AddIcon("Contacts", Image.FromFile("img3.ico"), new EventHandler(PanelEvent));
iconPanel1.AddIcon("Tasks", Image.FromFile("img4.ico"), new EventHandler(PanelEvent));
outlookBar.SelectBand(0);
</pre>
Enjoy!
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>
&nbsp;<p>
&nbsp;</pre></pre></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -