diff --git a/src/Perspex.Base/DirectProperty.cs b/src/Perspex.Base/DirectProperty.cs
index 7ae19ec520..82d47e7c1d 100644
--- a/src/Perspex.Base/DirectProperty.cs
+++ b/src/Perspex.Base/DirectProperty.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.Data;
namespace Perspex
{
@@ -44,11 +45,13 @@ namespace Perspex
/// The property to copy.
/// Gets the current value of the property.
/// Sets the value of the property. May be null.
+ /// Optional overridden metadata.
private DirectProperty(
PerspexProperty source,
Func getter,
- Action setter)
- : base(source, typeof(TOwner))
+ Action setter,
+ PropertyMetadata metadata)
+ : base(source, typeof(TOwner), metadata)
{
Contract.Requires(getter != null);
@@ -76,16 +79,25 @@ namespace Perspex
/// Registers the direct property on another type.
///
/// The type of the additional owner.
+ /// Gets the current value of the property.
+ /// Sets the value of the property.
+ ///
+ /// The value to use when the property is set to
+ ///
+ /// The default binding mode for the property.
/// The property.
public DirectProperty AddOwner(
Func getter,
- Action setter = null)
+ Action setter = null,
+ TValue unsetValue = default(TValue),
+ BindingMode defaultBindingMode = BindingMode.OneWay)
where TNewOwner : PerspexObject
{
var result = new DirectProperty(
this,
getter,
- setter);
+ setter,
+ new DirectPropertyMetadata(unsetValue, defaultBindingMode));
PerspexPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result;
diff --git a/src/Perspex.Base/PerspexProperty.cs b/src/Perspex.Base/PerspexProperty.cs
index e88c7f4418..36c534266e 100644
--- a/src/Perspex.Base/PerspexProperty.cs
+++ b/src/Perspex.Base/PerspexProperty.cs
@@ -71,7 +71,11 @@ namespace Perspex
///
/// The direct property to copy.
/// The new owner type.
- protected PerspexProperty(PerspexProperty source, Type ownerType)
+ /// Optional overridden metadata.
+ protected PerspexProperty(
+ PerspexProperty source,
+ Type ownerType,
+ PropertyMetadata metadata)
{
Contract.Requires(source != null);
Contract.Requires(ownerType != null);
@@ -86,6 +90,11 @@ namespace Perspex
Notifying = source.Notifying;
Id = source.Id;
_defaultMetadata = source._defaultMetadata;
+
+ if (metadata != null)
+ {
+ _metadata.Add(ownerType, metadata);
+ }
}
///
diff --git a/src/Perspex.Base/PerspexProperty`1.cs b/src/Perspex.Base/PerspexProperty`1.cs
index 101aeaed22..1ad1cef2e1 100644
--- a/src/Perspex.Base/PerspexProperty`1.cs
+++ b/src/Perspex.Base/PerspexProperty`1.cs
@@ -32,8 +32,12 @@ namespace Perspex
///
/// The property to copy.
/// The new owner type.
- protected PerspexProperty(PerspexProperty source, Type ownerType)
- : base(source, ownerType)
+ /// Optional overridden metadata.
+ protected PerspexProperty(
+ PerspexProperty source,
+ Type ownerType,
+ PropertyMetadata metadata)
+ : base(source, ownerType, metadata)
{
}
}
diff --git a/src/Perspex.Base/StyledPropertyBase.cs b/src/Perspex.Base/StyledPropertyBase.cs
index 2f5311d706..e77c5704eb 100644
--- a/src/Perspex.Base/StyledPropertyBase.cs
+++ b/src/Perspex.Base/StyledPropertyBase.cs
@@ -46,7 +46,7 @@ namespace Perspex
/// The property to add the owner to.
/// The type of the class that registers the property.
protected StyledPropertyBase(StyledPropertyBase source, Type ownerType)
- : base(source, ownerType)
+ : base(source, ownerType, null)
{
_inherits = source.Inherits;
}
diff --git a/src/Perspex.Controls/Presenters/TextPresenter.cs b/src/Perspex.Controls/Presenters/TextPresenter.cs
index a08c1c7b5e..3bcc07a9e8 100644
--- a/src/Perspex.Controls/Presenters/TextPresenter.cs
+++ b/src/Perspex.Controls/Presenters/TextPresenter.cs
@@ -12,24 +12,28 @@ namespace Perspex.Controls.Presenters
{
public class TextPresenter : TextBlock
{
- public static readonly StyledProperty CaretIndexProperty =
- TextBox.CaretIndexProperty.AddOwner();
+ public static readonly DirectProperty CaretIndexProperty =
+ TextBox.CaretIndexProperty.AddOwner(
+ o => o.CaretIndex,
+ (o, v) => o.CaretIndex = v);
- public static readonly StyledProperty SelectionStartProperty =
- TextBox.SelectionStartProperty.AddOwner();
+ public static readonly DirectProperty SelectionStartProperty =
+ TextBox.SelectionStartProperty.AddOwner(
+ o => o.SelectionStart,
+ (o, v) => o.SelectionStart = v);
- public static readonly StyledProperty SelectionEndProperty =
- TextBox.SelectionEndProperty.AddOwner();
+ public static readonly DirectProperty SelectionEndProperty =
+ TextBox.SelectionEndProperty.AddOwner(
+ o => o.SelectionEnd,
+ (o, v) => o.SelectionEnd = v);
private readonly DispatcherTimer _caretTimer;
+ private int _caretIndex;
+ private int _selectionStart;
+ private int _selectionEnd;
private bool _caretBlink;
private IBrush _highlightBrush;
- static TextPresenter()
- {
- CaretIndexProperty.OverrideValidation((o, v) => v);
- }
-
public TextPresenter()
{
_caretTimer = new DispatcherTimer();
@@ -47,20 +51,44 @@ namespace Perspex.Controls.Presenters
public int CaretIndex
{
- get { return GetValue(CaretIndexProperty); }
- set { SetValue(CaretIndexProperty, value); }
+ get
+ {
+ return _caretIndex;
+ }
+
+ set
+ {
+ value = CoerceCaretIndex(value);
+ SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
+ }
}
public int SelectionStart
{
- get { return GetValue(SelectionStartProperty); }
- set { SetValue(SelectionStartProperty, value); }
+ get
+ {
+ return _selectionStart;
+ }
+
+ set
+ {
+ value = CoerceCaretIndex(value);
+ SetAndRaise(SelectionStartProperty, ref _selectionStart, value);
+ }
}
public int SelectionEnd
{
- get { return GetValue(SelectionEndProperty); }
- set { SetValue(SelectionEndProperty, value); }
+ get
+ {
+ return _selectionEnd;
+ }
+
+ set
+ {
+ value = CoerceCaretIndex(value);
+ SetAndRaise(SelectionEndProperty, ref _selectionEnd, value);
+ }
}
public int GetCaretIndex(Point point)
@@ -206,6 +234,13 @@ namespace Perspex.Controls.Presenters
}
}
+ private int CoerceCaretIndex(int value)
+ {
+ var text = Text;
+ var length = text?.Length ?? 0;
+ return Math.Max(0, Math.Min(length, value));
+ }
+
private void CaretTimerTick(object sender, EventArgs e)
{
_caretBlink = !_caretBlink;
diff --git a/src/Perspex.Controls/TextBlock.cs b/src/Perspex.Controls/TextBlock.cs
index 0ddc3c30ee..162774e732 100644
--- a/src/Perspex.Controls/TextBlock.cs
+++ b/src/Perspex.Controls/TextBlock.cs
@@ -72,8 +72,11 @@ namespace Perspex.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty TextProperty =
- PerspexProperty.Register(nameof(Text));
+ public static readonly DirectProperty TextProperty =
+ PerspexProperty.RegisterDirect(
+ nameof(Text),
+ o => o.Text,
+ (o, v) => o.Text = v);
///
/// Defines the property.
@@ -87,14 +90,8 @@ namespace Perspex.Controls
public static readonly StyledProperty TextWrappingProperty =
PerspexProperty.Register(nameof(TextWrapping));
- ///
- /// The formatted text used for rendering.
- ///
+ private string _text;
private FormattedText _formattedText;
-
- ///
- /// Stores the last constraint passed to MeasureOverride.
- ///
private Size _constraint;
///
@@ -140,8 +137,8 @@ namespace Perspex.Controls
[Content]
public string Text
{
- get { return GetValue(TextProperty); }
- set { SetValue(TextProperty, value); }
+ get { return _text; }
+ set { SetAndRaise(TextProperty, ref _text, value); }
}
///
diff --git a/src/Perspex.Controls/TextBox.cs b/src/Perspex.Controls/TextBox.cs
index d44401b1a3..3985dbef60 100644
--- a/src/Perspex.Controls/TextBox.cs
+++ b/src/Perspex.Controls/TextBox.cs
@@ -29,18 +29,29 @@ namespace Perspex.Controls
public static readonly DirectProperty CanScrollHorizontallyProperty =
PerspexProperty.RegisterDirect("CanScrollHorizontally", o => o.CanScrollHorizontally);
- // TODO: Should CaretIndex, SelectionStart/End and Text be direct properties?
- public static readonly StyledProperty CaretIndexProperty =
- PerspexProperty.Register("CaretIndex", validate: ValidateCaretIndex);
-
- public static readonly StyledProperty SelectionStartProperty =
- PerspexProperty.Register("SelectionStart", validate: ValidateCaretIndex);
-
- public static readonly StyledProperty SelectionEndProperty =
- PerspexProperty.Register("SelectionEnd", validate: ValidateCaretIndex);
-
- public static readonly StyledProperty TextProperty =
- TextBlock.TextProperty.AddOwner();
+ public static readonly DirectProperty CaretIndexProperty =
+ PerspexProperty.RegisterDirect(
+ nameof(CaretIndex),
+ o => o.CaretIndex,
+ (o, v) => o.CaretIndex = v);
+
+ public static readonly DirectProperty SelectionStartProperty =
+ PerspexProperty.RegisterDirect(
+ nameof(SelectionStart),
+ o => o.SelectionStart,
+ (o, v) => o.SelectionStart = v);
+
+ public static readonly DirectProperty SelectionEndProperty =
+ PerspexProperty.RegisterDirect(
+ nameof(SelectionEnd),
+ o => o.SelectionEnd,
+ (o, v) => o.SelectionEnd = v);
+
+ public static readonly DirectProperty TextProperty =
+ TextBlock.TextProperty.AddOwner(
+ o => o.Text,
+ (o, v) => o.Text = v,
+ defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty TextAlignmentProperty =
TextBlock.TextAlignmentProperty.AddOwner();
@@ -71,6 +82,10 @@ namespace Perspex.Controls
public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text);
}
+ private string _text;
+ private int _caretIndex;
+ private int _selectionStart;
+ private int _selectionEnd;
private bool _canScrollHorizontally;
private TextPresenter _presenter;
private UndoRedoHelper _undoRedoHelper;
@@ -78,7 +93,6 @@ namespace Perspex.Controls
static TextBox()
{
FocusableProperty.OverrideDefaultValue(typeof(TextBox), true);
- TextProperty.OverrideMetadata(new StyledPropertyMetadata(defaultBindingMode: BindingMode.TwoWay));
}
public TextBox()
@@ -117,10 +131,15 @@ namespace Perspex.Controls
public int CaretIndex
{
- get { return GetValue(CaretIndexProperty); }
+ get
+ {
+ return _caretIndex;
+ }
+
set
{
- SetValue(CaretIndexProperty, value);
+ value = CoerceCaretIndex(value);
+ SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text)
_undoRedoHelper.UpdateLastState();
}
@@ -128,21 +147,37 @@ namespace Perspex.Controls
public int SelectionStart
{
- get { return GetValue(SelectionStartProperty); }
- set { SetValue(SelectionStartProperty, value); }
+ get
+ {
+ return _selectionStart;
+ }
+
+ set
+ {
+ value = CoerceCaretIndex(value);
+ SetAndRaise(SelectionStartProperty, ref _selectionStart, value);
+ }
}
public int SelectionEnd
{
- get { return GetValue(SelectionEndProperty); }
- set { SetValue(SelectionEndProperty, value); }
+ get
+ {
+ return _selectionEnd;
+ }
+
+ set
+ {
+ value = CoerceCaretIndex(value);
+ SetAndRaise(SelectionEndProperty, ref _selectionEnd, value);
+ }
}
[Content]
public string Text
{
- get { return GetValue(TextProperty); }
- set { SetValue(TextProperty, value); }
+ get { return _text; }
+ set { SetAndRaise(TextProperty, ref _text, value); }
}
public TextAlignment TextAlignment
@@ -426,9 +461,9 @@ namespace Perspex.Controls
}
}
- private static int ValidateCaretIndex(PerspexObject o, int value)
+ private int CoerceCaretIndex(int value)
{
- var text = o.GetValue(TextProperty);
+ var text = Text;
var length = text?.Length ?? 0;
return Math.Max(0, Math.Min(length, value));
}