diff --git a/Perspex.UnitTests/Perspex.UnitTests.csproj b/Perspex.UnitTests/Perspex.UnitTests.csproj
index c462e9c134..1e9eec4ba5 100644
--- a/Perspex.UnitTests/Perspex.UnitTests.csproj
+++ b/Perspex.UnitTests/Perspex.UnitTests.csproj
@@ -76,7 +76,7 @@
-
+
diff --git a/Perspex.UnitTests/Styling/ActivatorTests.cs b/Perspex.UnitTests/Styling/StyleActivatorTests.cs
similarity index 85%
rename from Perspex.UnitTests/Styling/ActivatorTests.cs
rename to Perspex.UnitTests/Styling/StyleActivatorTests.cs
index 14e9ab0c94..79e8eb8c73 100644
--- a/Perspex.UnitTests/Styling/ActivatorTests.cs
+++ b/Perspex.UnitTests/Styling/StyleActivatorTests.cs
@@ -1,4 +1,5 @@
-namespace Perspex.UnitTests.Styling
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+namespace Perspex.UnitTests.Styling
{
using System;
using System.Collections.Generic;
@@ -9,16 +10,15 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Perspex.Styling;
- using Activator = Perspex.Styling.StyleActivator;
[TestClass]
- public class ActivatorTests
+ public class StyleActivatorTests
{
[TestMethod]
public void Activator_And_Should_Follow_Single_Input()
{
var inputs = new[] { new TestSubject(false) };
- var target = new Activator(inputs, ActivatorMode.And);
+ var target = new StyleActivator(inputs, ActivatorMode.And);
var result = new TestObserver();
target.Subscribe(result);
@@ -42,7 +42,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.And);
+ var target = new StyleActivator(inputs, ActivatorMode.And);
var result = new TestObserver();
target.Subscribe(result);
@@ -67,7 +67,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.And);
+ var target = new StyleActivator(inputs, ActivatorMode.And);
var result = new TestObserver();
target.Subscribe(result);
@@ -93,7 +93,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.And);
+ var target = new StyleActivator(inputs, ActivatorMode.And);
var result = new TestObserver();
target.Subscribe(result);
@@ -110,7 +110,7 @@
public void Activator_Or_Should_Follow_Single_Input()
{
var inputs = new[] { new TestSubject(false) };
- var target = new Activator(inputs, ActivatorMode.Or);
+ var target = new StyleActivator(inputs, ActivatorMode.Or);
var result = new TestObserver();
target.Subscribe(result);
@@ -134,7 +134,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.Or);
+ var target = new StyleActivator(inputs, ActivatorMode.Or);
var result = new TestObserver();
target.Subscribe(result);
@@ -158,7 +158,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.Or);
+ var target = new StyleActivator(inputs, ActivatorMode.Or);
var result = new TestObserver();
target.Subscribe(result);
@@ -183,7 +183,7 @@
new TestSubject(false),
new TestSubject(true),
};
- var target = new Activator(inputs, ActivatorMode.Or);
+ var target = new StyleActivator(inputs, ActivatorMode.Or);
var result = new TestObserver();
target.Subscribe(result);
@@ -196,5 +196,21 @@
Assert.AreEqual(1, inputs[1].SubscriberCount);
Assert.AreEqual(1, inputs[2].SubscriberCount);
}
+
+ [TestMethod]
+ public void Completed_Activator_Should_Signal_OnCompleted()
+ {
+ var inputs = new[]
+ {
+ Observable.Return(false),
+ };
+
+ var target = new StyleActivator(inputs, ActivatorMode.Or);
+ var completed = false;
+
+ target.Subscribe(_ => { }, () => completed = true);
+
+ Assert.IsTrue(completed);
+ }
}
}
diff --git a/Perspex/Styling/Style.cs b/Perspex/Styling/Style.cs
index f8b9fc1546..6c8999f646 100644
--- a/Perspex/Styling/Style.cs
+++ b/Perspex/Styling/Style.cs
@@ -43,11 +43,21 @@ namespace Perspex.Styling
string description = "Style " + this.Selector.ToString();
StyleActivator activator = this.Selector.GetActivator(control);
- if (!(activator.CurrentValue == false && activator.HasCompleted))
+ if (activator.CurrentValue || !activator.HasCompleted)
{
+ IObservable observable = activator;
+
+ // If the activator has completed, then we want its value to be true forever.
+ // Because of this we can't pass the activator directly as it will complete
+ // immediately and remove the binding.
+ if (activator.HasCompleted)
+ {
+ observable = Observable.Never().StartWith(true);
+ }
+
foreach (Setter setter in this.Setters)
{
- StyleBinding binding = new StyleBinding(activator, setter.Value, description);
+ StyleBinding binding = new StyleBinding(observable, setter.Value, description);
control.Bind(setter.Property, binding, this.Selector.Priority);
}
}
diff --git a/Perspex/Styling/StyleActivator.cs b/Perspex/Styling/StyleActivator.cs
index faa55d87d8..9bb406392a 100644
--- a/Perspex/Styling/StyleActivator.cs
+++ b/Perspex/Styling/StyleActivator.cs
@@ -21,26 +21,25 @@ namespace Perspex.Styling
{
private ActivatorMode mode;
- private List values = new List();
+ private bool[] values;
private List subscriptions = new List();
private List> observers = new List>();
public StyleActivator(
- IEnumerable> inputs,
+ IList> inputs,
ActivatorMode mode = ActivatorMode.And)
{
int i = 0;
this.mode = mode;
+ this.values = new bool[inputs.Count];
foreach (IObservable input in inputs)
{
int capturedIndex = i;
- this.values.Add(false);
-
IDisposable subscription = input.Subscribe(
x => this.Update(capturedIndex, x),
x => this.Finish(capturedIndex),
@@ -79,9 +78,18 @@ namespace Perspex.Styling
{
Contract.Requires(observer != null);
- this.observers.Add(observer);
observer.OnNext(this.CurrentValue);
- return Disposable.Create(() => this.observers.Remove(observer));
+
+ if (this.HasCompleted)
+ {
+ observer.OnCompleted();
+ return Disposable.Empty;
+ }
+ else
+ {
+ this.observers.Add(observer);
+ return Disposable.Create(() => this.observers.Remove(observer));
+ }
}
private void Update(int index, bool value)
@@ -111,10 +119,14 @@ namespace Perspex.Styling
private void Finish(int i)
{
- // If the observable has finished on 'false' and we're in And mode then it will never
- // go back to true so we can unsubscribe from all the other subscriptions now.
- // Similarly in Or mode; if the completed value is true then we're done.
- bool unsubscribe = this.mode == ActivatorMode.And ? !this.values[i] : this.values[i];
+ // We can unsubscribe from everything if the completed observable:
+ // - Is the only subscription.
+ // - Has finished on 'false' and we're in And mode
+ // - Has finished on 'true' and we're in Or mode
+ var value = this.values[i];
+ var unsubscribe =
+ (this.values.Length == 1) ||
+ (this.mode == ActivatorMode.And ? !value : value);
if (unsubscribe)
{
diff --git a/Perspex/Styling/StyleBinding.cs b/Perspex/Styling/StyleBinding.cs
index 365588b8de..622f667017 100644
--- a/Perspex/Styling/StyleBinding.cs
+++ b/Perspex/Styling/StyleBinding.cs
@@ -15,15 +15,14 @@ namespace Perspex.Styling
///
/// This class takes an activator and a value. The activator is an observable which produces
/// a bool. When the activator produces true, this observable will produce .
- /// When the activator produces false (and before the activator returns a value) it will
- /// produce .
+ /// When the activator produces false it will produce .
///
internal class StyleBinding : IObservable