I had an interesting challenge to improve breadcrumbs in navigating a deeply folder nested library. The nice breadcrumb dropdown icon is actually gone in SP2013 by default; that can easily be re-enabled via CSS. an onMouseOver event action in JavaScript can eliminate the need to click on it. However to improve the breadcrumb navigation, I created an SPWeb feature to enhance breadcrumbs by overriding the OnPreRender event. The OnPreRender should only occur if the ‘Visible’ property of the web control is set to true. The PreRender event is raised just before the page is about to render its contents. This is the last chance we have to modify the page output before it is received by the browser.
I deployed this web Scoped solution that overrides the breadcrumbs with OnPreRender page processing, and provides the full folder path, clickable for each link. It recursively searches the page for the “TitleBreadcrumb” control, and builds a replacement, adding an a href, folder link name, and breadcrumb image iteratively. It only affects the page when you are navigating within a library.
namespace MyDocLibraryBreadcrumb { public class MyDocLibBreadcrumbDelegateControl : WebControl { protected override void OnPreRender(EventArgs e) { try { base.OnPreRender(e); // Get the path to the current folder string path = Context.Request.QueryString["RootFolder"]; // if there's no path then there is nothing to do; it implies are are not in the context of the library if (String.IsNullOrEmpty(path)) { return; } // Let's get the current folder SPWeb web = SPContext.Current.Web; SPFolder currentFolder = web.GetFolder(path); // Let's find the breadcrumb control on the current page - it's a ListProperty control where the property is "TitleBreadcrumb". Control c = Utils.FindRecursive(Page.Controls, ctrl => ctrl is ListProperty && ((ListProperty)ctrl).Property == "TitleBreadcrumb"); // If not found, nothing to do, and we are not likely in a library. if (c == null) return; // Let's subsitute the OOTB breadcrumb control with our replacement enhanced one var parent = c.Parent; var index = parent.Controls.IndexOf(c); parent.Controls.RemoveAt(index); parent.Controls.AddAt(index, new LiteralControl { Text = GetReplacementBreadCrumbOutput(currentFolder) }); } catch (Exception ex) { // log errors quietly Utils.WriteMyLog(Utils.GetErrorInfo(ex)); } } /// SPFolder is the parameter to create navigation to /// returns the HTML output private string GetReplacementBreadCrumbOutput(SPFolder folder) { List<BreadcrumbNodeData> nodes = new List<BreadcrumbNodeData>(); // Collect a path from current folder to its root folder SPFolder nodeFolder = folder; while (nodeFolder != null) { // If we're in folder use the folder name as a title. If not use a library title enstead. BreadcrumbNodeData node = new BreadcrumbNodeData(); node.Url = nodeFolder.ServerRelativeUrl; if (string.IsNullOrEmpty(nodeFolder.ParentFolder.Url)) { if (nodeFolder.DocumentLibrary != null) nodes.Add(new BreadcrumbNodeData {Title = nodeFolder.DocumentLibrary.Title, Url = nodeFolder.ServerRelativeUrl}); } else { nodes.Add(new BreadcrumbNodeData { Title = nodeFolder.Name, Url = nodeFolder.ServerRelativeUrl }); } nodeFolder = string.IsNullOrEmpty(nodeFolder.ParentFolder.Url) ? null : nodeFolder.ParentFolder; } // Reverse the collected path because the root folder must be on the left in bredcrumb nodes.Reverse(); // Create an HTML output similar to original. An arrow image we've created from the original string htmlOutput = String.Empty; foreach (var node in nodes) { if (node != nodes.Last()) htmlOutput += String.Format( @"<A href=""{0}"">{1}</A>&nbsp;<IMG style=""vertical-align:middle"" alt=: src=""/_layouts/images/MyDocLibraryBreadcrumb/breadcrumb_arrow.png""/>&nbsp;", node.Url, node.Title); else { htmlOutput += node.Title; } } return htmlOutput; } } /// temporary class to holds navigation node data public class BreadcrumbNodeData { /// Title for URL (it will be a folder name) public string Title { get; set; } /// Url to navigate on click (it will be a server relative URL of the folder) public string Url { get; set; } } public class Utils { public static string GetErrorInfo(Exception ex) { string result = "ex.Message=" + ex.Message; result += ex.InnerException == null ? "|ex.StackTrace=" + ex.StackTrace : String.Empty; if (ex.InnerException != null) result += "[INNER EXCEPTION: ]" + GetErrorInfo(ex.InnerException); return result; } public static void WriteMyLog(string text) { SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("MyDocLibBreadcrumbDelegateControl", TraceSeverity.High, EventSeverity.Error), TraceSeverity.High, text, null); } /// Finds a control based on provided criteria inside controls hierarchy /// <param name="controls">A Controls collection to start search</param> /// <param name="criteria">A criteria to return control</param> /// <returns>The founded control </returns> public static Control FindRecursive(ControlCollection controls, Func<Control, bool> criteria) { foreach (Control control in controls) { if (criteria(control)) return control; var innerControl = FindRecursive(control.Controls, criteria); if (innerControl != null) return innerControl; } return null; } } } |
Want to talk?
Drop us a line. We are here to answer your questions 24*7.