Browse Source

Tidied up PseudoClass stuff.

Make it more like AffectsMeasure etc. To do this required a
PerspexProperty.Initialized observable.
pull/10/head
Steven Kirk 11 years ago
parent
commit
1930178de3
  1. 17
      Perspex.Base/PerspexObject.cs
  2. 25
      Perspex.Base/PerspexProperty.cs
  3. 56
      Perspex.Controls/Control.cs
  4. 17
      Perspex.Controls/Primitives/ScrollBar.cs
  5. 6
      Perspex.Controls/Primitives/ToggleButton.cs
  6. 4
      Perspex.Controls/TabItem.cs
  7. 6
      Perspex.Controls/TreeViewItem.cs
  8. 19
      Perspex.UnitTests/PerspexPropertyTests.cs
  9. 1
      Perspex.sln

17
Perspex.Base/PerspexObject.cs

@ -77,6 +77,23 @@ namespace Perspex
private Dictionary<PerspexProperty, PriorityValue> values =
new Dictionary<PerspexProperty, PriorityValue>();
/// <summary>
/// Initializes a new instance of the <see cref="PerspexObject"/> class.
/// </summary>
public PerspexObject()
{
foreach (var p in this.GetAllValues())
{
var e = new PerspexPropertyChangedEventArgs(
this,
p.Property,
PerspexProperty.UnsetValue,
p.CurrentValue);
p.Property.NotifyInitialized(e);
}
}
/// <summary>
/// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
/// </summary>

25
Perspex.Base/PerspexProperty.cs

@ -29,6 +29,11 @@ namespace Perspex
/// </summary>
private Dictionary<Type, object> defaultValues = new Dictionary<Type, object>();
/// <summary>
/// Observable fired when this property changes on any <see cref="PerspexObject"/>.
/// </summary>
private Subject<PerspexPropertyChangedEventArgs> initialized = new Subject<PerspexPropertyChangedEventArgs>();
/// <summary>
/// Observable fired when this property changes on any <see cref="PerspexObject"/>.
/// </summary>
@ -89,6 +94,21 @@ namespace Perspex
/// <returns></returns>
public BindingMode DefaultBindingMode { get; private set; }
/// <summary>
/// Gets an observable that is fired when this property is initialized on a
/// new <see cref="PerspexObject"/> instance.
/// </summary>
/// <remarks>
/// This observable is fired each time a new <see cref="PerspexObject"/> is constructed
/// for all properties registered on the object's type. The default value of the property
/// for the object is passed in the args' NewValue (OldValue will always be
/// <see cref="UnsetValue"/>.
/// </remarks>
public IObservable<PerspexPropertyChangedEventArgs> Initialized
{
get { return this.initialized; }
}
/// <summary>
/// Gets an observable that is fired when this property changes on any
/// <see cref="PerspexObject"/> instance.
@ -270,6 +290,11 @@ namespace Perspex
return this.Name;
}
internal void NotifyInitialized(PerspexPropertyChangedEventArgs e)
{
this.initialized.OnNext(e);
}
internal void NotifyChanged(PerspexPropertyChangedEventArgs e)
{
this.changed.OnNext(e);

56
Perspex.Controls/Control.cs

@ -33,7 +33,7 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ITemplatedControl> TemplatedParentProperty =
PerspexProperty.Register<Control, ITemplatedControl>("TemplatedParent", inherits: true);
private Classes classes;
private Classes classes = new Classes();
private DataTemplates dataTemplates;
@ -44,13 +44,8 @@ namespace Perspex.Controls
static Control()
{
AffectsMeasure(IsVisibleProperty);
}
public Control()
{
this.classes = new Classes();
this.AddPseudoClass(IsPointerOverProperty, ":pointerover");
this.AddPseudoClass(IsFocusedProperty, ":focus");
PseudoClass(IsPointerOverProperty, ":pointerover");
PseudoClass(IsFocusedProperty, ":focus");
}
public Brush Background
@ -159,25 +154,44 @@ namespace Perspex.Controls
internal set { this.SetValue(TemplatedParentProperty, value); }
}
protected override void OnAttachedToVisualTree(IRenderRoot root)
protected static void PseudoClass(PerspexProperty<bool> property, string className)
{
IStyler styler = Locator.Current.GetService<IStyler>();
styler.ApplyStyles(this);
PseudoClass(property, x => x, className);
}
protected void AddPseudoClass(PerspexProperty<bool> property, string className)
protected static void PseudoClass<T>(
PerspexProperty<T> property,
Func<T, bool> selector,
string className)
{
this.GetObservable(property).Subscribe(x =>
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null);
Contract.Requires<ArgumentNullException>(className != null);
Contract.Requires<ArgumentNullException>(property != null);
if (string.IsNullOrWhiteSpace(className))
{
if (x)
{
this.classes.Add(className);
}
else
throw new ArgumentException("Cannot supply an empty className.");
}
Observable.Merge(property.Changed, property.Initialized)
.Subscribe(e =>
{
this.classes.Remove(className);
}
});
if (selector((T)e.NewValue))
{
((Control)e.Sender).Classes.Add(className);
}
else
{
((Control)e.Sender).Classes.Remove(className);
}
});
}
protected override void OnAttachedToVisualTree(IRenderRoot root)
{
IStyler styler = Locator.Current.GetService<IStyler>();
styler.ApplyStyles(this);
}
}
}

17
Perspex.Controls/Primitives/ScrollBar.cs

@ -26,21 +26,10 @@ namespace Perspex.Controls.Primitives
public static readonly PerspexProperty<Orientation> OrientationProperty =
PerspexProperty.Register<ScrollBar, Orientation>("Orientation");
public ScrollBar()
static ScrollBar()
{
this.GetObservable(OrientationProperty).Subscribe(o =>
{
if (o == Orientation.Horizontal)
{
this.Classes.Remove(":vertical");
this.Classes.Add(":horizontal");
}
else
{
this.Classes.Remove(":horizontal");
this.Classes.Add(":vertical");
}
});
PseudoClass(OrientationProperty, x => x == Orientation.Horizontal, ":horizontal");
PseudoClass(OrientationProperty, x => x == Orientation.Vertical, ":vertical");
}
public double Minimum

6
Perspex.Controls/Primitives/ToggleButton.cs

@ -13,10 +13,14 @@ namespace Perspex.Controls.Primitives
public static readonly PerspexProperty<bool> IsCheckedProperty =
PerspexProperty.Register<ToggleButton, bool>("IsChecked");
static ToggleButton()
{
PseudoClass(IsCheckedProperty, ":checked");
}
public ToggleButton()
{
this.Click += (s, e) => this.IsChecked = !this.IsChecked;
this.AddPseudoClass(IsCheckedProperty, ":checked");
}
public bool IsChecked

4
Perspex.Controls/TabItem.cs

@ -13,10 +13,10 @@ namespace Perspex.Controls
public static readonly PerspexProperty<bool> IsSelectedProperty =
PerspexProperty.Register<TabItem, bool>("IsSelected");
public TabItem()
static TabItem()
{
this.AddPseudoClass(IsSelectedProperty, ":selected");
AffectsRender(IsSelectedProperty);
PseudoClass(IsSelectedProperty, ":selected");
}
public bool IsSelected

6
Perspex.Controls/TreeViewItem.cs

@ -21,9 +21,13 @@ namespace Perspex.Controls
TreeView treeView;
static TreeViewItem()
{
PseudoClass(IsSelectedProperty, ":selected");
}
public TreeViewItem()
{
this.AddPseudoClass(IsSelectedProperty, ":selected");
AffectsRender(IsSelectedProperty);
}

19
Perspex.UnitTests/PerspexPropertyTests.cs

@ -83,22 +83,33 @@ namespace Perspex.UnitTests
Assert.AreEqual("Bar", target.GetDefaultValue<Class2>());
}
[TestMethod]
public void Initialized_Observable_Fired()
{
string value = null;
Class1.FooProperty.Initialized.Subscribe(x => value = (string)x.NewValue);
var target = new Class1();
Assert.AreEqual("default", value);
}
[TestMethod]
public void Changed_Observable_Fired()
{
var target = new Class1();
bool fired = false;
string value = null;
Class1.FooProperty.Changed.Subscribe(x => fired = true);
Class1.FooProperty.Changed.Subscribe(x => value = (string)x.NewValue);
target.SetValue(Class1.FooProperty, "newvalue");
Assert.IsTrue(fired);
Assert.AreEqual("newvalue", value);
}
private class Class1 : PerspexObject
{
public static readonly PerspexProperty<string> FooProperty =
PerspexProperty.Register<Class1, string>("Foo");
PerspexProperty.Register<Class1, string>("Foo", "default");
}
private class Class2 : Class1

1
Perspex.sln

@ -76,7 +76,6 @@ Global
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU

Loading…
Cancel
Save