Browse Source

Add initial AutoDataTemplateBindingHook implementation

artyom 6 years ago
parent
commit
fe7e7054fb

+ 60 - 0
src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Markup.Xaml.Templates;
+using ReactiveUI;
+
+namespace Avalonia.ReactiveUI
+{
+    /// <summary>
+    /// AutoDataTemplateBindingHook is a binding hook that checks ItemsControls
+    /// that don't have DataTemplates, and assigns a default DataTemplate that
+    /// loads the View associated with each ViewModel.
+    /// </summary>
+    public class AutoDataTemplateBindingHook : IPropertyBindingHook
+    {
+        private static Lazy<DataTemplate> DefaultItemTemplate { get; } = new Lazy<DataTemplate>(() =>
+        {
+            var template = @"
+<DataTemplate xmlns='https://github.com/avaloniaui'
+              xmlns:reactiveUi='http://reactiveui.net'>
+    <reactiveUi:ViewModelViewHost
+        ViewModel='{Binding Mode=OneWay}'
+        VerticalContentAlignment='Stretch'
+        HorizontalContentAlignment='Stretch' />
+</DataTemplate>";
+            
+            var loader = new AvaloniaXamlLoader();
+            return (DataTemplate)loader.Load(template);
+        });
+
+        /// <inheritdoc/>
+        public bool ExecuteHook(
+            object source, object target,
+            Func<IObservedChange<object, object>[]> getCurrentViewModelProperties,
+            Func<IObservedChange<object, object>[]> getCurrentViewProperties,
+            BindingDirection direction)
+        {
+            var viewProperties = getCurrentViewProperties();
+            var lastViewProperty = viewProperties.LastOrDefault();
+            if (lastViewProperty == null)
+                return true;
+            
+            var itemsControl = lastViewProperty.Sender as ItemsControl;
+            if (itemsControl == null)
+                return true;
+
+            var propertyName = viewProperties.Last().GetPropertyName();
+            if (propertyName != "Items" &&
+                propertyName != "ItemsSource")
+                return true;
+            
+            if (itemsControl.ItemTemplate != null)
+                return true;
+
+            itemsControl.ItemTemplate = DefaultItemTemplate.Value;
+            return true;
+        }
+    }
+}

+ 56 - 0
tests/Avalonia.ReactiveUI.UnitTests/AutoDataTemplateBindingHookTest.cs

@@ -0,0 +1,56 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Xunit;
+using ReactiveUI;
+using Avalonia.ReactiveUI;
+using Avalonia.UnitTests;
+using Avalonia.Controls;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Splat;
+
+namespace Avalonia.ReactiveUI.UnitTests
+{
+    public class AutoDataTemplateBindingHookTest
+    {
+        public class NestedViewModel : ReactiveObject { }
+
+        public class NestedView : ReactiveUserControl<NestedViewModel> { }
+
+        public class ExampleViewModel : ReactiveObject 
+        {
+            public ObservableCollection<NestedViewModel> Items { get; } = new ObservableCollection<NestedViewModel>();
+        }
+
+        public class ExampleView : ReactiveUserControl<ExampleViewModel>
+        {
+            public ListBox List { get; } = new ListBox();
+
+            public ExampleView()
+            {
+                Content = List;
+                ViewModel = new ExampleViewModel();
+                this.OneWayBind(ViewModel, x => x.Items, x => x.List.Items);
+            }
+        }
+
+        public AutoDataTemplateBindingHookTest() 
+        {
+            Locator.CurrentMutable.RegisterConstant(new AutoDataTemplateBindingHook(), typeof(IPropertyBindingHook));
+            Locator.CurrentMutable.Register(() => new ExampleView(), typeof(IViewFor<ExampleViewModel>));
+        }
+
+        [Fact]
+        public void Should_Apply_Data_Template_Binding_When_No_Template_Is_Set()
+        {
+            var view = new ExampleView();
+            var root = new TestRoot 
+            { 
+                Child = view
+            };
+            Assert.NotNull(view.List.ItemTemplate);
+        }
+    }
+}