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 12 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 = private Dictionary<PerspexProperty, PriorityValue> values =
new Dictionary<PerspexProperty, PriorityValue>(); 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> /// <summary>
/// Event handler for <see cref="INotifyPropertyChanged"/> implementation. /// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
/// </summary> /// </summary>

25
Perspex.Base/PerspexProperty.cs

@ -29,6 +29,11 @@ namespace Perspex
/// </summary> /// </summary>
private Dictionary<Type, object> defaultValues = new Dictionary<Type, object>(); 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> /// <summary>
/// Observable fired when this property changes on any <see cref="PerspexObject"/>. /// Observable fired when this property changes on any <see cref="PerspexObject"/>.
/// </summary> /// </summary>
@ -89,6 +94,21 @@ namespace Perspex
/// <returns></returns> /// <returns></returns>
public BindingMode DefaultBindingMode { get; private set; } 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> /// <summary>
/// Gets an observable that is fired when this property changes on any /// Gets an observable that is fired when this property changes on any
/// <see cref="PerspexObject"/> instance. /// <see cref="PerspexObject"/> instance.
@ -270,6 +290,11 @@ namespace Perspex
return this.Name; return this.Name;
} }
internal void NotifyInitialized(PerspexPropertyChangedEventArgs e)
{
this.initialized.OnNext(e);
}
internal void NotifyChanged(PerspexPropertyChangedEventArgs e) internal void NotifyChanged(PerspexPropertyChangedEventArgs e)
{ {
this.changed.OnNext(e); this.changed.OnNext(e);

56
Perspex.Controls/Control.cs

@ -33,7 +33,7 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ITemplatedControl> TemplatedParentProperty = public static readonly PerspexProperty<ITemplatedControl> TemplatedParentProperty =
PerspexProperty.Register<Control, ITemplatedControl>("TemplatedParent", inherits: true); PerspexProperty.Register<Control, ITemplatedControl>("TemplatedParent", inherits: true);
private Classes classes; private Classes classes = new Classes();
private DataTemplates dataTemplates; private DataTemplates dataTemplates;
@ -44,13 +44,8 @@ namespace Perspex.Controls
static Control() static Control()
{ {
AffectsMeasure(IsVisibleProperty); AffectsMeasure(IsVisibleProperty);
} PseudoClass(IsPointerOverProperty, ":pointerover");
PseudoClass(IsFocusedProperty, ":focus");
public Control()
{
this.classes = new Classes();
this.AddPseudoClass(IsPointerOverProperty, ":pointerover");
this.AddPseudoClass(IsFocusedProperty, ":focus");
} }
public Brush Background public Brush Background
@ -159,25 +154,44 @@ namespace Perspex.Controls
internal set { this.SetValue(TemplatedParentProperty, value); } 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>(); PseudoClass(property, x => x, className);
styler.ApplyStyles(this);
} }
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) throw new ArgumentException("Cannot supply an empty className.");
{ }
this.classes.Add(className);
} Observable.Merge(property.Changed, property.Initialized)
else .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 = public static readonly PerspexProperty<Orientation> OrientationProperty =
PerspexProperty.Register<ScrollBar, Orientation>("Orientation"); PerspexProperty.Register<ScrollBar, Orientation>("Orientation");
public ScrollBar() static ScrollBar()
{ {
this.GetObservable(OrientationProperty).Subscribe(o => PseudoClass(OrientationProperty, x => x == Orientation.Horizontal, ":horizontal");
{ PseudoClass(OrientationProperty, x => x == Orientation.Vertical, ":vertical");
if (o == Orientation.Horizontal)
{
this.Classes.Remove(":vertical");
this.Classes.Add(":horizontal");
}
else
{
this.Classes.Remove(":horizontal");
this.Classes.Add(":vertical");
}
});
} }
public double Minimum public double Minimum

6
Perspex.Controls/Primitives/ToggleButton.cs

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

4
Perspex.Controls/TabItem.cs

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

6
Perspex.Controls/TreeViewItem.cs

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

19
Perspex.UnitTests/PerspexPropertyTests.cs

@ -83,22 +83,33 @@ namespace Perspex.UnitTests
Assert.AreEqual("Bar", target.GetDefaultValue<Class2>()); 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] [TestMethod]
public void Changed_Observable_Fired() public void Changed_Observable_Fired()
{ {
var target = new Class1(); 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"); target.SetValue(Class1.FooProperty, "newvalue");
Assert.IsTrue(fired); Assert.AreEqual("newvalue", value);
} }
private class Class1 : PerspexObject private class Class1 : PerspexObject
{ {
public static readonly PerspexProperty<string> FooProperty = public static readonly PerspexProperty<string> FooProperty =
PerspexProperty.Register<Class1, string>("Foo"); PerspexProperty.Register<Class1, string>("Foo", "default");
} }
private class Class2 : Class1 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.ActiveCfg = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = 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.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.ActiveCfg = Debug|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.Build.0 = 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 {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU

Loading…
Cancel
Save