Browse Source

Fix ItemsRepeater overwriting DataContext

Ported from https://github.com/microsoft/microsoft-ui-xaml/commit/0e24e05dbdb231aa0f4e3ec22e13ad528f155eb6
Steven Kirk 5 years ago
parent
commit
0236ced64f
1 changed files with 33 additions and 16 deletions
  1. 33 16
      src/Avalonia.Controls/Repeater/ViewManager.cs

+ 33 - 16
src/Avalonia.Controls/Repeater/ViewManager.cs

@@ -526,43 +526,60 @@ namespace Avalonia.Controls
             return element;
         }
 
+        // There are several cases handled here with respect to which element gets returned and when DataContext is modified.
+        //
+        // 1. If there is no ItemTemplate:
+        //    1.1 If data is an IControl -> the data is returned
+        //    1.2 If data is not an IControl -> a default DataTemplate is used to fetch element and DataContext is set to data
+        //
+        // 2. If there is an ItemTemplate:
+        //    2.1 If data is not an IControl -> Element is fetched from ElementFactory and DataContext is set to the data
+        //    2.2 If data is an IControl:
+        //        2.2.1 If Element returned by the ElementFactory is the same as the data -> Element (a.k.a. data) is returned as is
+        //        2.2.2 If Element returned by the ElementFactory is not the same as the data
+        //                 -> Element that is fetched from the ElementFactory is returned and
+        //                    DataContext is set to the data's DataContext (if it exists), otherwise it is set to the data itself
         private IControl GetElementFromElementFactory(int index)
         {
             // The view generator is the provider of last resort.
             var data = _owner.ItemsSourceView.GetAt(index);
-            var itemTemplateFactory = _owner.ItemTemplateShim;
-            IControl element = null;
-            var itemsSourceContainsElements = false;
+            var providedElementFactory = _owner.ItemTemplateShim;
 
-            if (itemTemplateFactory == null)
+            ItemTemplateWrapper GetElementFactory()
             {
-                element = data as IControl;
+                if (providedElementFactory == null)
+                {
+                    var factory = FuncDataTemplate.Default;
+                    _owner.ItemTemplate = factory;
+                    return _owner.ItemTemplateShim;
+                }
 
-                // No item template provided and ItemsSource contains objects derived from UIElement.
-                // In this case, just use the data directly as elements.
-                itemsSourceContainsElements = element != null;
+                return providedElementFactory;
             }
 
-            if (element == null)
+            IControl GetElement()
             {
-                if (itemTemplateFactory == null)
+                if (providedElementFactory == null)
                 {
-                    // If no ItemTemplate was provided, use a default 
-                    var factory = FuncDataTemplate.Default;
-                    _owner.ItemTemplate = factory;
-                    itemTemplateFactory = _owner.ItemTemplateShim;
+                    if (data is IControl dataAsElement)
+                    {
+                        return dataAsElement;
+                    }
                 }
 
-                element = itemTemplateFactory.GetElement(_owner, data);
+                var elementFactory = GetElementFactory();
+                return elementFactory.GetElement(_owner, data);
             }
 
+            var element = GetElement();
+
             var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
             if (virtInfo == null)
             {
                 virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
             }
 
-            if (!itemsSourceContainsElements)
+            if (data != element)
             {
                 // Prepare the element
                 element.DataContext = data;