diff --git a/src/Umbraco.Core/DictionaryExtensions.cs b/src/Umbraco.Core/DictionaryExtensions.cs
index de12e0b3b1..194b461fd5 100644
--- a/src/Umbraco.Core/DictionaryExtensions.cs
+++ b/src/Umbraco.Core/DictionaryExtensions.cs
@@ -131,30 +131,48 @@ namespace Umbraco.Core
return n;
}
- ///
- /// Returns a new dictionary of this ... others merged leftward.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// Reference: http://stackoverflow.com/questions/294138/merging-dictionaries-in-c
- ///
- public static T MergeLeft(this T me, params IDictionary[] others)
- where T : IDictionary, new()
- {
- var newMap = new T();
- foreach (var p in (new List> { me }).Concat(others).SelectMany(src => src))
- {
- newMap[p.Key] = p.Value;
- }
- return newMap;
- }
+ ///
+ /// Merges all key/values from the sources dictionaries into the destination dictionary
+ ///
+ ///
+ ///
+ ///
+ /// The source dictionary to merge other dictionaries into
+ ///
+ /// By default all values will be retained in the destination if the same keys exist in the sources but
+ /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that
+ /// it will just use the last found key/value if this is true.
+ ///
+ /// The other dictionaries to merge values from
+ public static void MergeLeft(this T destination, IEnumerable> sources, bool overwrite = false)
+ where T : IDictionary
+ {
+ foreach (var p in sources.SelectMany(src => src).Where(p => overwrite || destination.ContainsKey(p.Key) == false))
+ {
+ destination[p.Key] = p.Value;
+ }
+ }
- ///
+ ///
+ /// Merges all key/values from the sources dictionaries into the destination dictionary
+ ///
+ ///
+ ///
+ ///
+ /// The source dictionary to merge other dictionaries into
+ ///
+ /// By default all values will be retained in the destination if the same keys exist in the sources but
+ /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that
+ /// it will just use the last found key/value if this is true.
+ ///
+ /// The other dictionary to merge values from
+ public static void MergeLeft(this T destination, IDictionary source, bool overwrite = false)
+ where T : IDictionary
+ {
+ destination.MergeLeft(new[] {source}, overwrite);
+ }
+
+ ///
/// Returns the value of the key value based on the key, if the key is not found, a null value is returned
///
/// The type of the key.
diff --git a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs
new file mode 100644
index 0000000000..2f35aede2d
--- /dev/null
+++ b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Mvc;
+using NUnit.Framework;
+using Umbraco.Web.Mvc;
+
+namespace Umbraco.Tests.Mvc
+{
+ [TestFixture]
+ public class ViewDataDictionaryExtensionTests
+ {
+ [Test]
+ public void Merge_View_Data()
+ {
+ var source = new ViewDataDictionary();
+ var dest = new ViewDataDictionary();
+ source.Add("Test1", "Test1");
+ dest.Add("Test2", "Test2");
+
+ dest.MergeViewDataFrom(source);
+
+ Assert.AreEqual(2, dest.Count);
+ }
+
+ [Test]
+ public void Merge_View_Data_Retains_Destination_Values()
+ {
+ var source = new ViewDataDictionary();
+ var dest = new ViewDataDictionary();
+ source.Add("Test1", "Test1");
+ dest.Add("Test1", "MyValue");
+ dest.Add("Test2", "Test2");
+
+ dest.MergeViewDataFrom(source);
+
+ Assert.AreEqual(2, dest.Count);
+ Assert.AreEqual("MyValue", dest["Test1"]);
+ Assert.AreEqual("Test2", dest["Test2"]);
+ }
+
+ }
+}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 0d2d690a46..d8fae9ebac 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -187,6 +187,7 @@
+
diff --git a/src/Umbraco.Web/Macros/PartialViewMacroController.cs b/src/Umbraco.Web/Macros/PartialViewMacroController.cs
index 765846c5c5..b858d1c283 100644
--- a/src/Umbraco.Web/Macros/PartialViewMacroController.cs
+++ b/src/Umbraco.Web/Macros/PartialViewMacroController.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Web.Mvc;
using Umbraco.Web.Models;
+using Umbraco.Web.Mvc;
using umbraco.cms.businesslogic.macro;
using umbraco.interfaces;
using System.Linq;
@@ -10,6 +11,7 @@ namespace Umbraco.Web.Macros
///
/// Controller to render macro content for Parital View Macros
///
+ [MergeParentContextViewData]
internal class PartialViewMacroController : Controller
{
private readonly UmbracoContext _umbracoContext;
diff --git a/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs b/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs
new file mode 100644
index 0000000000..219447d353
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs
@@ -0,0 +1,36 @@
+using System.Web.Mvc;
+
+namespace Umbraco.Web.Mvc
+{
+ ///
+ /// This attribute can be used for when child actions execute and will automatically merge in the viewdata from the parent context to the
+ /// child action result.
+ ///
+ ///
+ /// This will retain any custom viewdata put into the child viewdata if the same key persists in the parent context's view data. You can always still
+ /// access the parent's view data normally.
+ /// This just simplifies working with ChildActions and view data.
+ ///
+ /// NOTE: This does not mean that the parent context's view data will be merged before the action executes, if you need access to the parent context's view
+ /// data during controller execution you can access it normally.
+ ///
+ public class MergeParentContextViewDataAttribute : ActionFilterAttribute
+ {
+ ///
+ /// Merge in the parent context's view data if this is a child action when the result is being executed
+ ///
+ ///
+ public override void OnResultExecuting(ResultExecutingContext filterContext)
+ {
+ if (filterContext.IsChildAction)
+ {
+ if (filterContext.ParentActionViewContext != null && filterContext.ParentActionViewContext.ViewData != null)
+ {
+ filterContext.Controller.ViewData.MergeViewDataFrom(filterContext.ParentActionViewContext.ViewData);
+ }
+ }
+
+ base.OnResultExecuting(filterContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs
index 37cb38751a..23caa16619 100644
--- a/src/Umbraco.Web/Mvc/SurfaceController.cs
+++ b/src/Umbraco.Web/Mvc/SurfaceController.cs
@@ -12,6 +12,7 @@ namespace Umbraco.Web.Mvc
/// The base controller that all Presentation Add-in controllers should inherit from
///
[MergeModelStateToChildAction]
+ [MergeParentContextViewData]
public abstract class SurfaceController : PluginController
{
diff --git a/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs b/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs
index 5e9ad89b9d..ef611e96c4 100644
--- a/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs
+++ b/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs
@@ -2,7 +2,7 @@ using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
- internal static class ViewDataContainerExtensions
+ internal static class ViewDataContainerExtensions
{
private class ViewDataContainer : IViewDataContainer
{
diff --git a/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs b/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs
new file mode 100644
index 0000000000..930163bfe4
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs
@@ -0,0 +1,18 @@
+using System.Web.Mvc;
+using Umbraco.Core;
+
+namespace Umbraco.Web.Mvc
+{
+ internal static class ViewDataDictionaryExtensions
+ {
+ ///
+ /// Merges the source view data into the destination view data
+ ///
+ ///
+ ///
+ public static void MergeViewDataFrom(this ViewDataDictionary destination, ViewDataDictionary source)
+ {
+ destination.MergeLeft(source);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 249849013f..cddb3dc51f 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -299,6 +299,8 @@
+
+