|
|
|
@ -36,32 +36,36 @@ Attached Properties. So the challenge became to improve it. |
|
|
|
|
|
|
|
Delaring a DP in WPF looks something like this: |
|
|
|
|
|
|
|
public static readonly DependencyProperty PropertyDeclaration = |
|
|
|
DependencyProperty.Register( |
|
|
|
"PropertyName", |
|
|
|
typeof(PropertyType), |
|
|
|
typeof(OwnerClass), |
|
|
|
new FrameworkPropertyMetadata( |
|
|
|
default(PropertyType), |
|
|
|
FrameworkPropertyMetadataOptions.Inherits)); |
|
|
|
|
|
|
|
public PropertyType PropertyName |
|
|
|
{ |
|
|
|
get { return (PropertyType)this.GetValue(PropertyDeclaration); } |
|
|
|
set { this.SetValue(PropertyDeclaration, value); } |
|
|
|
} |
|
|
|
```csharp |
|
|
|
public static readonly DependencyProperty PropertyDeclaration = |
|
|
|
DependencyProperty.Register( |
|
|
|
"PropertyName", |
|
|
|
typeof(PropertyType), |
|
|
|
typeof(OwnerClass), |
|
|
|
new FrameworkPropertyMetadata( |
|
|
|
default(PropertyType), |
|
|
|
FrameworkPropertyMetadataOptions.Inherits)); |
|
|
|
|
|
|
|
public PropertyType PropertyName |
|
|
|
{ |
|
|
|
get { return (PropertyType)this.GetValue(PropertyDeclaration); } |
|
|
|
set { this.SetValue(PropertyDeclaration, value); } |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
Eww! All that just to declare a single property. There's **A LOT** of boilerplate there. With |
|
|
|
generics and default parameters we can at least make it look a bit nicer: |
|
|
|
|
|
|
|
public static readonly PerspexProperty<PropertyType> PropertyDeclaration = |
|
|
|
PerspexProperty.Register<OwnerClass, PropertyType>("PropertyName", inherits: true); |
|
|
|
```csharp |
|
|
|
public static readonly PerspexProperty<PropertyType> PropertyDeclaration = |
|
|
|
PerspexProperty.Register<OwnerClass, PropertyType>("PropertyName", inherits: true); |
|
|
|
|
|
|
|
public PropertyType PropertyName |
|
|
|
{ |
|
|
|
get { return this.GetValue(PropertyDeclaration); } |
|
|
|
set { this.SetValue(PropertyDeclaration, value); } |
|
|
|
} |
|
|
|
public PropertyType PropertyName |
|
|
|
{ |
|
|
|
get { return this.GetValue(PropertyDeclaration); } |
|
|
|
set { this.SetValue(PropertyDeclaration, value); } |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
What can we see here? |
|
|
|
|
|
|
|
@ -77,34 +81,42 @@ write typeof() twice. |
|
|
|
Binding in Perspex uses Reactive Extensions' IObservable. To bind an IObservable to a property, |
|
|
|
use the Bind method: |
|
|
|
|
|
|
|
control.Bind(BorderProperty, someObject.SomeObservable()); |
|
|
|
```csharp |
|
|
|
control.Bind(BorderProperty, someObject.SomeObservable()); |
|
|
|
``` |
|
|
|
|
|
|
|
Note that because PerspexProperty is typed, we can check that the observable is of the correct type. |
|
|
|
|
|
|
|
To get the value of a property as an observable, call GetObservable(): |
|
|
|
|
|
|
|
var observable = control.GetObservable(Control.FooProperty); |
|
|
|
```csharp |
|
|
|
var observable = control.GetObservable(Control.FooProperty); |
|
|
|
``` |
|
|
|
|
|
|
|
## Attached Properties and Binding Pt 2 |
|
|
|
|
|
|
|
Attached properties are set just like in WPF, using SetValue. But what about the [] operator? C# 6 |
|
|
|
will allow us to use [] array subscripts in object initializers. So how does this look? |
|
|
|
|
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
} |
|
|
|
```csharp |
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
Nice... Lets take this further: |
|
|
|
|
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
[!Property2] = something.SomeObservable, |
|
|
|
} |
|
|
|
```csharp |
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
[!Property2] = something.SomeObservable, |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
Yep, by putting a bang in front of the property name you can **bind** to a property (attached or |
|
|
|
otherwise) from the object initializer. |
|
|
|
@ -120,12 +132,14 @@ Binding to a property on another control? Easy: |
|
|
|
|
|
|
|
Two way binding? Just add two bangs: |
|
|
|
|
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
[!!Property2] = anotherControl[!!Property1], |
|
|
|
} |
|
|
|
```csharp |
|
|
|
var control = new Control |
|
|
|
{ |
|
|
|
Property1 = "Foo", |
|
|
|
[Attached.Property] = "Bar", |
|
|
|
[!!Property2] = anotherControl[!!Property1], |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
## Visual and Logical trees |
|
|
|
|
|
|
|
@ -144,36 +158,42 @@ property, which is determined by... |
|
|
|
Styles in Perspex diverge from styles in WPF quite a lot, and move towards a more CSS-like system. |
|
|
|
It's probably easiest to show in an example. Here is the default style for the CheckBox control: |
|
|
|
|
|
|
|
new Style(x => x.OfType<CheckBox>()) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Button.TemplateProperty, ControlTemplate.Create<CheckBox>(this.Template)), |
|
|
|
}, |
|
|
|
}, |
|
|
|
new Style(x => x.OfType<CheckBox>().Template().Id("checkMark")) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Shape.IsVisibleProperty, false), |
|
|
|
}, |
|
|
|
}, |
|
|
|
new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark")) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Shape.IsVisibleProperty, true), |
|
|
|
}, |
|
|
|
}, |
|
|
|
```csharp |
|
|
|
new Style(x => x.OfType<CheckBox>()) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Button.TemplateProperty, ControlTemplate.Create<CheckBox>(this.Template)), |
|
|
|
}, |
|
|
|
}, |
|
|
|
new Style(x => x.OfType<CheckBox>().Template().Id("checkMark")) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Shape.IsVisibleProperty, false), |
|
|
|
}, |
|
|
|
}, |
|
|
|
new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark")) |
|
|
|
{ |
|
|
|
Setters = new[] |
|
|
|
{ |
|
|
|
new Setter(Shape.IsVisibleProperty, true), |
|
|
|
}, |
|
|
|
}, |
|
|
|
``` |
|
|
|
|
|
|
|
Let's see what's happening here: |
|
|
|
|
|
|
|
new Style(x => x.OfType<CheckBox>()) |
|
|
|
```csharp |
|
|
|
new Style(x => x.OfType<CheckBox>()) |
|
|
|
``` |
|
|
|
|
|
|
|
The constructor for the Style class defines the selector. Here we're saying "*this style applies to |
|
|
|
all controls in the the visual tree of type CheckBox*". A more complex selector: |
|
|
|
|
|
|
|
new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark")) |
|
|
|
```csharp |
|
|
|
new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark")) |
|
|
|
``` |
|
|
|
|
|
|
|
This selector matches "*all controls with Id == "checkMark" in the template of a CheckBox with the |
|
|
|
class ':checked'"*. Each control has an Id property, and Ids in templates are considered to be in a |
|
|
|
|