Browse Source

Make IStyleable.Classes an IPerspexReadOnlyList.

So we can mock it.
Steven Kirk 10 years ago
parent
commit
5d2658d856

BIN
_NCrunch_Perspex/StoredText/e0ab357f917440988efaba9c0cd4a9c0


+ 1 - 0
src/Markup/Perspex.Markup.Xaml/Converters/ClassesTypeConverter.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Globalization;
 using OmniXaml.TypeConversion;
+using Perspex.Controls;
 using Perspex.Styling;
 
 namespace Perspex.Markup.Xaml.Converters

+ 25 - 0
src/Perspex.Controls/Classes.cs

@@ -0,0 +1,25 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System.Collections.Generic;
+using Perspex.Collections;
+
+namespace Perspex.Controls
+{
+    public class Classes : PerspexList<string>
+    {
+        public Classes()
+        {            
+        }
+
+        public Classes(IEnumerable<string> items)
+            : base(items)
+        {
+        }
+
+        public Classes(params string[] items)
+            : base(items)
+        {            
+        }
+    }
+}

+ 4 - 1
src/Perspex.Controls/Control.cs

@@ -129,7 +129,7 @@ namespace Perspex.Controls
                 if (_classes != value)
                 {
                     _classes.Clear();
-                    _classes.Add(value);
+                    _classes.AddRange(value);
                 }
             }
         }
@@ -216,6 +216,9 @@ namespace Perspex.Controls
         /// </summary>
         IPerspexReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
 
+        /// <inheritdoc/>
+        IPerspexReadOnlyList<string> IStyleable.Classes => Classes;
+
         /// <summary>
         /// Gets the type by which the control is styled.
         /// </summary>

+ 5 - 0
src/Perspex.Controls/IControl.cs

@@ -13,6 +13,11 @@ namespace Perspex.Controls
     /// </summary>
     public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IStyleable, IStyleHost
     {
+        /// <summary>
+        /// Gets or sets the control's styling classes.
+        /// </summary>
+        new Classes Classes { get; set; }
+
         /// <summary>
         /// Gets or sets the control's data context.
         /// </summary>

+ 1 - 0
src/Perspex.Controls/Perspex.Controls.csproj

@@ -42,6 +42,7 @@
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
       <Link>Properties\SharedAssemblyInfo.cs</Link>
     </Compile>
+    <Compile Include="Classes.cs" />
     <Compile Include="DockPanel.cs" />
     <Compile Include="HotkeyManager.cs" />
     <Compile Include="INameScope.cs" />

+ 3 - 4
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@@ -593,7 +593,6 @@ namespace Perspex.Controls.Primitives
             try
             {
                 var selectable = container as ISelectable;
-                var styleable = container as IStyleable;
 
                 _ignoreContainerSelectionChanged = true;
 
@@ -601,15 +600,15 @@ namespace Perspex.Controls.Primitives
                 {
                     selectable.IsSelected = selected;
                 }
-                else if (styleable != null)
+                else
                 {
                     if (selected)
                     {
-                        styleable.Classes.Add(":selected");
+                        container.Classes.Add(":selected");
                     }
                     else
                     {
-                        styleable.Classes.Remove(":selected");
+                        container.Classes.Remove(":selected");
                     }
                 }
             }

+ 3 - 4
src/Perspex.Controls/TreeView.cs

@@ -179,21 +179,20 @@ namespace Perspex.Controls
         private void MarkContainerSelected(IControl container, bool selected)
         {
             var selectable = container as ISelectable;
-            var styleable = container as IStyleable;
 
             if (selectable != null)
             {
                 selectable.IsSelected = selected;
             }
-            else if (styleable != null)
+            else
             {
                 if (selected)
                 {
-                    styleable.Classes.Add(":selected");
+                    container.Classes.Add(":selected");
                 }
                 else
                 {
-                    styleable.Classes.Remove(":selected");
+                    container.Classes.Remove(":selected");
                 }
             }
         }

+ 8 - 1
src/Perspex.Diagnostics/ViewModels/TreeNode.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Collections.Specialized;
 using System.Reactive;
 using System.Reactive.Linq;
 using Perspex.Controls;
@@ -18,7 +19,13 @@ namespace Perspex.Diagnostics.ViewModels
             Control = control;
             Type = control.GetType().Name;
 
-            control.Classes.Changed.Select(_ => Unit.Default)
+            var classesChanged = Observable.FromEventPattern<
+                    NotifyCollectionChangedEventHandler, 
+                    NotifyCollectionChangedEventHandler>(
+                x => control.Classes.CollectionChanged += x,
+                x => control.Classes.CollectionChanged -= x);
+
+            classesChanged.Select(_ => Unit.Default)
                 .StartWith(Unit.Default)
                 .Subscribe(_ =>
                 {

+ 0 - 1
src/Perspex.Styling/Perspex.Styling.csproj

@@ -45,7 +45,6 @@
     <Compile Include="ILogical.cs" />
     <Compile Include="LogicalTree\LogicalExtensions.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Styling\Classes.cs" />
     <Compile Include="Styling\IGlobalStyles.cs" />
     <Compile Include="Styling\ISetter.cs" />
     <Compile Include="Styling\IStyle.cs" />

+ 0 - 155
src/Perspex.Styling/Styling/Classes.cs

@@ -1,155 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Linq;
-using System.Reactive;
-using System.Reactive.Subjects;
-
-namespace Perspex.Styling
-{
-    public class Classes : ICollection<string>, INotifyCollectionChanged
-    {
-        private readonly List<string> _inner;
-
-        private readonly Subject<NotifyCollectionChangedEventArgs> _beforeChanged
-            = new Subject<NotifyCollectionChangedEventArgs>();
-
-        private readonly Subject<NotifyCollectionChangedEventArgs> _changed
-            = new Subject<NotifyCollectionChangedEventArgs>();
-
-        private readonly Subject<NotifyCollectionChangedEventArgs> _afterChanged
-            = new Subject<NotifyCollectionChangedEventArgs>();
-
-        public Classes()
-        {
-            _inner = new List<string>();
-        }
-
-        public Classes(params string[] classes)
-        {
-            _inner = new List<string>(classes);
-        }
-
-        public Classes(IEnumerable<string> classes)
-        {
-            _inner = new List<string>(classes);
-        }
-
-        public event NotifyCollectionChangedEventHandler CollectionChanged;
-
-        public int Count => _inner.Count;
-
-        public bool IsReadOnly => false;
-
-        public IObservable<NotifyCollectionChangedEventArgs> BeforeChanged => _beforeChanged;
-
-        public IObservable<NotifyCollectionChangedEventArgs> Changed => _changed;
-
-        public IObservable<NotifyCollectionChangedEventArgs> AfterChanged => _afterChanged;
-
-        public void Add(string item)
-        {
-            Add(Enumerable.Repeat(item, 1));
-        }
-
-        public void Add(params string[] items)
-        {
-            Add((IEnumerable<string>)items);
-        }
-
-        public void Add(IEnumerable<string> items)
-        {
-            items = items.Except(_inner);
-
-            NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(
-                NotifyCollectionChangedAction.Add,
-                items);
-
-            _beforeChanged.OnNext(e);
-            _inner.AddRange(items);
-            RaiseChanged(e);
-        }
-
-        public void Clear()
-        {
-            NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(
-                NotifyCollectionChangedAction.Reset);
-
-            _beforeChanged.OnNext(e);
-            _inner.Clear();
-            RaiseChanged(e);
-        }
-
-        public bool Contains(string item)
-        {
-            return _inner.Contains(item);
-        }
-
-        public void CopyTo(string[] array, int arrayIndex)
-        {
-            _inner.CopyTo(array, arrayIndex);
-        }
-
-        public IEnumerator<string> GetEnumerator()
-        {
-            return _inner.GetEnumerator();
-        }
-
-        public override string ToString()
-        {
-            return string.Join(" ", this);
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return _inner.GetEnumerator();
-        }
-
-        public bool Remove(string item)
-        {
-            return Remove(Enumerable.Repeat(item, 1));
-        }
-
-        public bool Remove(params string[] items)
-        {
-            return Remove((IEnumerable<string>)items);
-        }
-
-        public bool Remove(IEnumerable<string> items)
-        {
-            items = items.Intersect(_inner);
-
-            if (items.Any())
-            {
-                NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(
-                    NotifyCollectionChangedAction.Remove,
-                    items);
-
-                _beforeChanged.OnNext(e);
-
-                foreach (string item in items)
-                {
-                    _inner.Remove(item);
-                }
-
-                RaiseChanged(e);
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        private void RaiseChanged(NotifyCollectionChangedEventArgs e)
-        {
-            CollectionChanged?.Invoke(this, e);
-            _changed.OnNext(e);
-            _afterChanged.OnNext(e);
-        }
-    }
-}

+ 2 - 1
src/Perspex.Styling/Styling/IStyleable.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using Perspex.Collections;
 
 namespace Perspex.Styling
 {
@@ -13,7 +14,7 @@ namespace Perspex.Styling
         /// <summary>
         /// Gets the list of classes for the control.
         /// </summary>
-        Classes Classes { get; }
+        IPerspexReadOnlyList<string> Classes { get; }
 
         /// <summary>
         /// Gets the type by which the control is styled.

+ 9 - 1
src/Perspex.Styling/Styling/Selectors.cs

@@ -3,6 +3,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
 using System.Reactive.Linq;
 using System.Reflection;
 
@@ -176,10 +178,16 @@ namespace Perspex.Styling
 
         private static SelectorMatch MatchClass(IStyleable control, string name)
         {
+            var changed = Observable.FromEventPattern<
+                    NotifyCollectionChangedEventHandler,
+                    NotifyCollectionChangedEventHandler>(
+                x => control.Classes.CollectionChanged += x,
+                x => control.Classes.CollectionChanged -= x);
+
             return new SelectorMatch(
                 Observable
                     .Return(control.Classes.Contains(name))
-                    .Concat(control.Classes.Changed.Select(e => control.Classes.Contains(name))));
+                    .Concat(changed.Select(e => control.Classes.Contains(name))));
         }
 
         private static SelectorMatch MatchDescendent(IStyleable control, Selector previous)

+ 1 - 0
tests/Perspex.LeakTests/StyleTests.cs

@@ -123,6 +123,7 @@ namespace Perspex.LeakTests
 
             dotMemory.Check(memory =>
                 Assert.Equal(1, memory.GetObjects(where => where.Type.Is<StyleActivator>()).ObjectsCount));
+            Assert.False(true);
         }
     }
 }

+ 3 - 1
tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs

@@ -7,6 +7,8 @@ using OmniXaml;
 using OmniXaml.ObjectAssembler.Commands;
 using OmniXaml.TypeConversion;
 using OmniXaml.Typing;
+using Perspex.Collections;
+using Perspex.Controls;
 using Perspex.Markup.Xaml.Converters;
 using Perspex.Styling;
 using Xunit;
@@ -74,7 +76,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Converters
             public static readonly PerspexProperty<string> FooProperty =
                 PerspexProperty.Register<Class1, string>("Foo");
 
-            public Classes Classes
+            public IPerspexReadOnlyList<string> Classes
             {
                 get { throw new NotImplementedException(); }
             }

+ 6 - 1
tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

@@ -2,11 +2,13 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Reactive.Linq;
 using System.Reactive.Subjects;
 using System.Threading.Tasks;
 using Perspex.Collections;
+using Perspex.Controls;
 using Perspex.Styling;
 using Xunit;
 
@@ -43,7 +45,7 @@ namespace Perspex.Styling.UnitTests
         }
 
         [Fact]
-        public async Task Child_Matches_Control_When_It_Is_Child_OfType_And_Class()
+        public async void Child_Matches_Control_When_It_Is_Child_OfType_And_Class()
         {
             var parent = new TestLogical1();
             var child = new TestLogical2();
@@ -52,6 +54,7 @@ namespace Perspex.Styling.UnitTests
 
             var selector = new Selector().OfType<TestLogical1>().Class("foo").Child().OfType<TestLogical2>();
             var activator = selector.Match(child).ObservableResult;
+            var result = new List<bool>();
 
             Assert.False(await activator.Take(1));
             parent.Classes.Add("foo");
@@ -96,6 +99,8 @@ namespace Perspex.Styling.UnitTests
                 }
             }
 
+            IPerspexReadOnlyList<string> IStyleable.Classes => Classes;
+
             public IDisposable Bind(PerspexProperty property, IObservable<object> source, BindingPriority priority)
             {
                 throw new NotImplementedException();

+ 1 - 0
tests/Perspex.Styling.UnitTests/SelectorTests_Class.cs

@@ -5,6 +5,7 @@ using System.Linq;
 using System.Reactive.Linq;
 using System.Threading.Tasks;
 using Moq;
+using Perspex.Controls;
 using Perspex.Styling;
 using Xunit;
 

+ 3 - 0
tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs

@@ -7,6 +7,7 @@ using System.Reactive.Linq;
 using System.Reactive.Subjects;
 using System.Threading.Tasks;
 using Perspex.Collections;
+using Perspex.Controls;
 using Perspex.Styling;
 using Xunit;
 
@@ -128,6 +129,8 @@ namespace Perspex.Styling.UnitTests
                 }
             }
 
+            IPerspexReadOnlyList<string> IStyleable.Classes => Classes;
+
             public IDisposable Bind(PerspexProperty property, IObservable<object> source, BindingPriority priority)
             {
                 throw new NotImplementedException();

+ 1 - 1
tests/Perspex.Styling.UnitTests/SelectorTests_Multiple.cs

@@ -44,7 +44,7 @@ namespace Perspex.Styling.UnitTests
             activator.Subscribe(x => values.Add(x));
 
             Assert.Equal(new[] { false }, values);
-            control.Classes.Add("foo", "bar");
+            control.Classes.AddRange(new[] { "foo", "bar" });
             Assert.Equal(new[] { false, true }, values);
             control.Classes.Remove("foo");
             Assert.Equal(new[] { false, true, false }, values);

+ 4 - 0
tests/Perspex.Styling.UnitTests/TestControlBase.cs

@@ -3,6 +3,8 @@
 
 using System;
 using System.Reactive.Subjects;
+using Perspex.Collections;
+using Perspex.Controls;
 
 namespace Perspex.Styling.UnitTests
 {
@@ -36,6 +38,8 @@ namespace Perspex.Styling.UnitTests
             }
         }
 
+        IPerspexReadOnlyList<string> IStyleable.Classes => Classes;
+
         public IDisposable Bind(PerspexProperty property, IObservable<object> source, BindingPriority priority)
         {
             throw new NotImplementedException();

+ 4 - 0
tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs

@@ -4,6 +4,8 @@
 using System;
 using System.Collections.Generic;
 using System.Reactive.Subjects;
+using Perspex.Collections;
+using Perspex.Controls;
 
 namespace Perspex.Styling.UnitTests
 {
@@ -42,6 +44,8 @@ namespace Perspex.Styling.UnitTests
             }
         }
 
+        IPerspexReadOnlyList<string> IStyleable.Classes => Classes;
+
         public IObservable<T> GetObservable<T>(PerspexProperty<T> property)
         {
             throw new NotImplementedException();