Just after writing that last post I decided to open the blinds to my window and immediately caught a glimpse of two young deer laying in our backyard, just chillaxin. So I quickly put my 100-400mm L on my XTi, snuck out the front and around the side of the house and caught a few shots.
Carlos Sastre has taken yellow all the way home today to win the Tour De France in the overall best rider (time) category. Congratulations to him and the whole CSC team who also took the overall best team classification as well as the white jersey (best young rider). Carlos beat out Cadel Evans by attacking on Alp d’Huez and holding everyone off in the final time trial.
The biking season is just coming into full swing for me. Unfortunately I got a late start because I wanted to work hard first this summer and make some money, then ease off and get into a nice rythm. Before this past week my average weekly mileage had been stuck around 20-30 miles. This week I have ridden 53 with the intention of riding more had my bike not been in the shop for half of it.
One of the goals of this season was much like the goal of the previous season: to get my 16 mile time to under 50 minutes. Today I set out to do this and finished with a time of 00:48:22.xxx! This means I averaged roughly 20 miles per hour over the entire course (if I did my math correctly) which is well above my previous best of 17mph average.
Now that I have knocked one item off my list it is time to continue and work on the others: weight loss and overall distance. Weight loss will come, I am sure of that, but my bike is definately hurting me on distance. It sucks my energy because of how much friction it has and is unreliable when ascending. My highest mileage to date is 40 miles, and the goal is to get to 75 miles by the end of the season.
Who knows, I may just do it.
The SharePoint Happenings Blog has a great post about customizing forms for your lists. I am using this to get rid of certain fields that I do not want to be entered when a new item is added. For instance, I default the Status field to “In Development” (Open/Assigned) and force the user to pick this by hiding the field.
What a post by David Millar. If you don’t follow professional cycling, then you may not know about the doping problem that has plagued the sport. Many say that one of the biggest reasons behind it is that the races are just too demanding. The Tour de France is incredibly long and has only a few rest days. The riders are expected to go over gigantic mountains, travel all over france and be able to do this day in and day out for days and days.
I say that is hogwash. No one is expecting the riders to do it faster than before. Races like the Tour are meant to be so long that a large part of the field doesn’t make it to Paris. Finishing the whole Tour is supposed to be as prestigious as wearing the yellow jersey but the cyclists who dope would have us think otherwise. I say any doper who gets caught should get a lifetime ban from the sport. Simple, objective, ultimate. Clean the sport out; there are plenty of others out there to fill the holes.
Had a great ride today; felt like I was on top of my form the whole time and was able to keep up a 17mph average for most the trip. I slowed during a few of the ascents, but this is pretty normal of course. I ended up doing 18 miles total and definately could have gone farther had there been more sunlight.
So to celebrate, I installed Diablo II and quickly got my Paladin to level 4 which of course means getting the Cold Plains waypoint! Man this game is addicting; I can’t wait until Diablo III comes out and consumes my life.
Ever go out on a run, or in this case a ride, and quickly come to the realization that you are just not in it enough to finish what you have set out to do? Today I wanted to tackle at least 40 miles. The last time I did this I was feeling great until about mile 32 when my right calf suddenly locked up on me. This time I hit the first hill but already knew that today was just not my day. I decided to stick with it and go as long as I could stand which I knew would be at least 16 miles more than the 6 I had already traveled. I ended up at around 28 miles which isn’t too bad for me. My muscles are barely tired but my right knee is hurting quite a bit.
I think something is up with the bike because it just doesn’t feel right anymore. After the two falls I had two weeks ago, something is rubbing and making it quite a bit more difficult to pedal. The only problem is that I can’t seem to figure out what the problem is and can only say it is coming from the rear. Hopefully a new bike will be bought soon enough that I do not have to put up with the problems of a 20 year old bike anymore.
One more thought… Yesterday I got new riding shorts, ditching the hand me downs as well as my underwear. The new shorts feel great and the long ride felt fine on everything down in that area. Much better than riding with underwear and a ripped up chamois pad. I was a little iffy about going “commando” but after wearing the shorts around the house for an hour or so I realized how great they were.
Today I bring you more of the same! Well, not quite, but today’s post is about navigation. Having covered the left / quick launch navigation, I decided it was time to tackle the top navigation. Reasons? Drop down menus are definately nice, and I think there is a possibility to again get away from tables while adding some nice styling and better functionality. Enter javascript!
Step 1: Get this JavaScript menu (.js) and put it into “C:\…\12\TEMPLATE\LAYOUTS\1033\”
Step 2: Time to get Visual Studio open again, remember that menu we made for the left hand side navigation? Well we can use the same class and reconfigure it a bit for the top nav. This code adds some extra functionality that I will cover in a step or two. Once you are done creating the class, follow the same steps mention in the 4th installment of this series to get the navigation control installed. Read more »
Michael O’Donovan brings us a blog post about custom (left) navigation in SharePoint. That is what I will be tackling today so I can move away from the ugly Tables and use a more common UL/LI scheme.
Here is my class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
| using System;
using System.Collections;
using System.Globalization;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.XPath;
using Microsoft.SharePoint.Utilities;
namespace MMOinsite.SharePoint.Components
{
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public class ListNavigation : HierarchicalDataBoundControl
{
private XmlDocument navigationTree;
private string currentNodeKey = string.Empty;
private string linkClass = "NavLeftLink";
private string listItemClass = "NavLeftListItem";
private string listClass = "NavLeftList";
private string linkSelectedClass = "NavLeftListItemSelected";
private bool appendLevel = false;
private string appendLevelClass = "NavLeftListItem";
protected override void PerformDataBinding()
{
base.PerformDataBinding();
// Oh god, no data source! ABORT!
if (!IsBoundUsingDataSourceID && (DataSource == null))
return;
HierarchicalDataSourceView view = GetData("");
if (view == null)
throw new InvalidOperationException("Cannot get any data from the data source control.");
IHierarchicalEnumerable enumerable = view.Select();
if (enumerable != null)
{
navigationTree = new XmlDocument();
navigationTree.LoadXml("<nodes></nodes>");
try
{
AddNavigationItem(navigationTree.DocumentElement, enumerable, 1);
}
finally { }
ViewState["navXml"] = navigationTree.OuterXml;
}
}
private void AddNavigationItem(XmlElement currentNode, IHierarchicalEnumerable enumerable, int depth)
{
foreach (object item in enumerable)
{
IHierarchyData data = enumerable.GetHierarchyData(item);
NavigationItem navItem = BuildNavigationItem(data, depth);
XmlElement newNavElement = BuildNavigationNode(currentNode, navItem);
currentNode.AppendChild(newNavElement);
if(data.HasChildren)
{
IHierarchicalEnumerable newEnumerable = data.GetChildren();
if(newEnumerable != null)
AddNavigationItem(newNavElement, newEnumerable, depth + 1);
}
}
}
private NavigationItem BuildNavigationItem(IHierarchyData data, int depth)
{
NavigationItem tmpNavItem = new NavigationItem();
try
{
INavigateUIData navNode = (INavigateUIData)data;
tmpNavItem.Title = navNode.Value;
tmpNavItem.Url = navNode.NavigateUrl;
tmpNavItem.Level = depth;
if (currentNodeKey.Length == 0)
{
SiteMapDataSource dataSource = GetDataSource() as SiteMapDataSource;
if (dataSource != null)
if (dataSource.Provider.CurrentNode != null)
currentNodeKey = dataSource.Provider.CurrentNode.Url.ToLower();
}
if (string.Compare(tmpNavItem.Url, currentNodeKey, true, CultureInfo.InvariantCulture) == 0)
tmpNavItem.Selected = true;
}
catch (Exception ex)
{
throw ex;
}
return tmpNavItem;
}
private NavigationItem BuildNavigationItem(XmlNode node)
{
NavigationItem tmpNavItem = new NavigationItem();
tmpNavItem.Title = node.Attributes["title"].InnerText;
tmpNavItem.Url = node.Attributes["url"].InnerText;
tmpNavItem.Selected = Convert.ToBoolean(node.Attributes["isSelected"].InnerText);
tmpNavItem.Level = Convert.ToInt32(node.Attributes["level"].InnerText);
return tmpNavItem;
}
private XmlElement BuildNavigationNode(XmlElement parentNode, NavigationItem navItem)
{
XmlElement tmpNode = navigationTree.CreateElement("node");
tmpNode.SetAttribute("url", navItem.Url);
tmpNode.SetAttribute("title", navItem.Title);
tmpNode.SetAttribute("level", navItem.Level.ToString());
tmpNode.SetAttribute("isSelected", navItem.Selected.ToString());
return tmpNode;
}
protected override void Render(HtmlTextWriter writer)
{
if (navigationTree == null && ViewState["navXml"] != null)
{
navigationTree = new XmlDocument();
navigationTree.LoadXml(ViewState["navXml"].ToString());
}
XmlNodeList nodeList = navigationTree.SelectNodes("nodes/node");
if (nodeList.Count > 0)
{
WriteListStart(writer);
foreach (XmlNode node in nodeList)
{
Render(node, writer);
}
WriteListEnd(writer);
}
}
protected virtual void WriteListStart(HtmlTextWriter writer)
{
writer.Write(string.Format("<ul class=\"{0}\">", ListClass));
}
protected virtual void WriteListEnd(HtmlTextWriter writer)
{
writer.Write("</ul>");
}
protected virtual void Render(XmlNode navNode, HtmlTextWriter writer)
{
NavigationItem navItem = BuildNavigationItem(navNode);
writer.Write(
string.Format(
"<li class=\"{0}{1}\">",
ListItemClass,
(AppendLevel) ? string.Format(" {0}{1}", AppendLevelClass, navItem.Level) : ""
)
);
writer.Write(
string.Format(
"<a class=\"{0}\" href=\"{1}\">{2}</a>",
(navItem.Selected) ? LinkSelectedClass : LinkClass,
navItem.Url,
navItem.Title
)
);
if (navNode.ChildNodes.Count > 0)
{
WriteListStart(writer);
XmlNodeList nodeList = navNode.SelectNodes("node");
foreach (XmlNode childNode in nodeList)
Render(childNode, writer);
WriteListEnd(writer);
}
writer.Write("</li>");
}
public string ListClass
{
get { return listClass; }
set { listClass = value; }
}
public string LinkSelectedClass
{
get { return linkSelectedClass; }
set { linkSelectedClass = value; }
}
public string LinkClass
{
get { return linkClass; }
set { linkClass = value; }
}
public string ListItemClass
{
get { return listItemClass; }
set { listItemClass = value; }
}
public bool AppendLevel
{
get { return appendLevel; }
set { appendLevel = value; }
}
public string AppendLevelClass
{
get { return appendLevelClass; }
set { appendLevelClass = value; }
}
}
} |
But how do we install it? Good question John. Why thank you John! Copy it to your server. Open the GAC (C:\WINDOWS\assembly) and drag and drop. Now perform an IISRESET and add the control to your master page in the usual way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| <%@ Register Tagprefix="asp" Namespace="MMOinsite.SharePoint.Components" Assembly="MMOinsite.SharePoint.Components, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abb33cdce6ddaae1" %>
...
<Sharepoint:SPNavigationManager id="QuickLaunchNavigationManager"
runat="server"
QuickLaunchControlId="QuickLaunchMenu"
ContainedControl="QuickLaunch"
EnableViewState="false" >
<div>
<SharePoint:DelegateControl runat="server" ControlId="QuickLaunchDataSource">
<Template_Controls>
<asp:SiteMapDataSource
SiteMapProvider="SPNavigationProvider"
ShowStartingNode="False"
id="QuickLaunchSiteMap"
StartingNodeUrl="sid:1025"
runat="server"
/>
</Template_Controls>
</SharePoint:DelegateControl>
<asp:ListNavigation id="CustomListNavigation" DataSourceId="QuickLaunchSiteMap" runat="server" />
</div>
</SharePoint:SPNavigationManager> |
It is important to note that the UL/LI scheme is incredibly flexible. You can make them horizontal, drop down menus or keep them as standard vertical lists. Once the control is in place, the designer (CSS for the win here) can take over. For this reason, this type of navigation scheme can also make its way to the top navigation bar. But I will leave that up to you as I have got to get back to work!
Update (9:58): Here are the CSS styles I use with setting AppendLevel to true in the control tag of the master page. There is some pretty wacky CSS going on here and I am sure it can be cleaned up, but it works in IE7 and is a good place to start.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| .NavLeftLink{
color: #2D2D2D;
}
.NavLeftListItem{
background: #F2F8FF;
margin-left: 0px;
padding: 0px;
width: 100%;
}
.NavLeftListItem2{
}
.NavLeftListItem a{
padding: 2px 0px 2px 5px;
border-top: solid 1px #25475A;
width: 100%;
}
.NavLeftListItem a:hover{
background: #D4F7B9;
}
.NavLeftListItem1 a{
background: #BACCD6;
font-size: 1.2em;
font-weight: bold;
border-bottom: solid 1px #25475A;
margin-bottom: 1px;
}
.NavLeftListItem2 a{
background: #F2F8FF;
border: 0px;
padding-left: 20px;
font-size: 1em;
font-weight: normal;
background-image: url(/_layouts/images/navBullet.gif);
background-repeat: no-repeat;
background-position: 5px top;
}
.NavLeftListItem2 a:hover{
background-image: url(/_layouts/images/navBullet.gif);
background-repeat: no-repeat;
background-position: 5px top;
}
.NavLeftListItemSelected{
background: #D4F7B9;
}
.NavLeftList{
margin-top: 0px;
margin-left: 0px;
list-style: none;
border-bottom: solid 1px #25475A;
}
.NavLeftList .NavLeftList{
border: 0px;
} |
Wow. What an amazing writer. Read the David Millar Diaries.