Convert Most `DirectProperty` Occurrences to `StyledProperty`
See original GitHub issueIs your feature request related to a problem? Please describe.
This one is going to cause some controversy. However, I believe it’s time to significantly narrow the use-case of DirectProperty
(ies) which are unable to be set by styles in most cases.
Some additional points for discussion:
- Using DirectProperty in styles will cause exceptions and crashes (I’ve seen this come up a few times recently). This is VERY unintuitive to use as developers and we have to carefully check each property type. Of course tooling and compiler errors will improve, but I think it’s more of a fundamental problem.
- It is somewhat confusing in Avalonia which property type to use and when. I think this should be documented clearly after this issue is closed (more on that below).
- DirectProperty is pretty neat but not something we had to worry about in WPF where all properties are fully supported in styles (except for special read-only ones).
- DirectProperty is still useful in some cases (read-only properties and lists) and must not go away
- There are good reasons to set some properties that are currently direct within styles. Since it isn’t supported there are some ugly hacks required.
- At the very minimum all direct property usage should be audited for 11.0 and converted to styled properties as required.
With the significant optimization done to the property value store over the past few years, I don’t expect a significant performance impact moving most properties over. I could be wrong though.
Describe the solution you’d like
Almost all current DirectProperty
occurrences should be converted to StyledProperty
so they can be used fully within styles. My original comment was:
direct properties should be reserved only for “data”… which includes content, read-only collections
@tomenscape had some good points and narrowed the scope again:
I would go even further than robloo’s suggestion: in light of these changes, DirectProperty should be restricted to read-only properties. This is the only safe way to proceed. Even “data” values can reasonably set in a style, if you have multiple distinct values that you want to switch between depending on UI state.
This means ALL direct properties such as TextBlock.Text
, Expander.IsExpanded
and SplitPane.IsPaneOpen
(which are UI state information) would be converted to styled properties.
Describe alternatives you’ve considered
There are two alternatives:
- Extend the styling system to allow settings
DirectProperty
as if it was aStyledProperty
. No idea how to do this. - No changes…
Additional context
Some recent discussions include:
- https://github.com/AvaloniaUI/Avalonia/issues/9800#issuecomment-1367480402 (see whole thread, there are some good comments)
- https://github.com/AvaloniaUI/Avalonia/discussions/9719#discussioncomment-4471230
- https://github.com/AvaloniaUI/Avalonia/issues/7982 TextBlock.Text detailed discussion and some details on why it was originally considered a direct property (lots of good info here).
@tomenscape Feel free to duplicate your past comments here. It will put everything in one spot for the future.
Current status is tracked below. Note that animations and certain drawing primitives are currently out-of-scope. There is a strong chance of performance regressions in these areas and the use-cases of changing such properties in styles is not yet clear. Therefore, the focus is primarily controls:
- AutoCompleteBox
- Completed in https://github.com/AvaloniaUI/Avalonia/pull/10712
- ~Border~
- No DirectProperty usage
- Button
- The only DirectProperty
IsPressed
is a read-only property so remains unchanged
- The only DirectProperty
- ~ButtonSpinner~
- No DirectProperty usage
- CalendarDatePicker
- Completed in #10370
- Calendar
- Completed in #10370
- ~Canvas~
- No DirectProperty usage
- ~Carousel~
- No DirectProperty usage
- ~CheckBox~
- No DirectProperty usage (derives directly from ToggleButton which was completed separately)
- ColorPicker / ColorView (and primitives)
- Verified to be correct
- ComboBox
- Completed in #10370
- ~ContentControl~
- No DirectProperty usage
- ContentPresenter
- Completed in #10370
- ~DataGrid~
- Does need updates but agreed as out-of-scope for 11.0: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1523655358. It is already a separate package.
- DatePicker
- Completed in #10370
- ~DatePickerPresenter~
- No DirectProperty usage
- ~Decorator~
- No DirectProperty usage
- ~DockPanel~
- No DirectProperty usage
- ~DropDownButton~
- No DirectProperty usage (currently derives directly from Button)
- Expander
- Completed in #9979
- Flyout
- Completed in #10370
- ~Grid~
- No DirectProperty usage
- ~GridSplitter~
- No DirectProperty usage
- ~Image~
- No DirectProperty usage
- ~ItemsControl~
- Has two direct properties (Items, ItemCount) but they are already handled after the Items/ItemsSource rewrite
- Items has an Obsolete setter (it is intended to be read-only)
- ItemCount is read-only and correct as a DirectProperty
- ItemsPresenter
- Completed in #10370
- ~ItemsRepeater~
- Does need updates but agreed as out-of-scope for 11.0: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1523655358. It is already a separate package.
- Label
- Completed in #10370
- [Partial] ListBox
- Has
SelectedItem
,SelectedIndex
orSelectedItems
properties which were not fully converted due to the complexity discussed in this issue: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1538012823. This may be separately addressed in a future version of Avalonia. - Relevant properties were converted
- Has
- ~Menu~
- No DirectProperty usage
- MenuBase
- ~MenuItem~
- No DirectProperty usage
- MenuFlyout
- ~ContextMenu~
- No DirectProperty usage
- NativeMenu
- Completed in #10370
- NativeMenuItem
- Completed in #10370
- NumericUpDown
- Completed in #10370
- ~Panel~
- No DirectProperty usage
- Popup
- Completed in #10370
- ProgressBar
- The only direct property
Percentage
is correct as a direct property. This is a read-only calculated value. - ProgressBarTemplateProperties has many DirectProperty members but these can likely be ignored for now (animation is out of scope).
- Other properties were fixed with
RangeBase
- The only direct property
- RadioButton
- Completed in #10370
- RangeBase
- Completed in #10803
- ~Rectangle~
- No DirectProperty usage
- ~RelativePanel~
- No DirectProperty usage
- ~RepeatButton~
- No DirectProperty usage
- ScrollBar
- Has a single read-only DirectProperty
IsExpanded
that is likely already correct
- Has a single read-only DirectProperty
- ScrollContentPresenter
- ScrollViewer
- [Partial] SelectingItemsControl
- Has
SelectedItem
,SelectedIndex
orSelectedItems
properties which were not fully converted due to the complexity discussed in this issue: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1538012823. This may be separately addressed in a future version of Avalonia. - Relevant properties were converted
- Has
- ~Separator~
- No DirectProperty usage
- ~Slider~
- No DirectProperty usage (inherited from RangeBase)
- ~Spinner~
- No DirectProperty usage
- ~SplitButton~
- No DirectProperty usage
- SplitView
- Completed in #10138
- ~StackPanel~
- No DirectProperty usage
- ~TabControl~
- No DirectProperty usage
- ~TabItem~
- No DirectProperty usage
- ~TabStrip~
- No DirectProperty usage
- TemplatedControl
- Completed in #10370
- TextBlock
- This must be done specially as its own PR and with benchmarking if possible. There is concern about performance regressions.
- Completed in #10617
- TextBox
- TextBox was originally of scope: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1383203339. However, it shares the TextBlock property using AddOwner so was decided to include and avoid breaking the link.
- Completed in #10617
- TimePicker
- Completed in #10370
- ~TimePickerPresenter~
- No DirectProperty usage
- ToggleButton
- Completed in #10370
- ~ToggleSplitButton~
- No DirectProperty usage
- ~ToggleSwitch~
- No DirectProperty usage
- ~ToolTip~
- No DirectProperty usage
- ~Track~
- No DirectProperty usage
- [Partial] TreeView
- Has
SelectedItem
,SelectedIndex
orSelectedItems
properties which were not fully converted due to the complexity discussed in this issue: https://github.com/AvaloniaUI/Avalonia/issues/9944#issuecomment-1538012823. This may be separately addressed in a future version of Avalonia. - Relevant properties were converted
- Has
- TreeViewItem
- Completed in #10370
- ~Viewbox~
- No DirectProperty usage
- ~WrapPanel~
- No DirectProperty usage
- Window
- Completed in #10370
- ~PathIcon~
- No DirectProperty usage
Issue Analytics
- State:
- Created 8 months ago
- Reactions:6
- Comments:49 (49 by maintainers)
Another option is a “property update batch” object which defers notifications for property changes made during its lifetime until disposed. This can be initiated from a control’s
OnPropertyChanged
, since it is executed first.Thanks for making this issue @robloo. I was looking at the new
ValueStore
last night and I agree that there doesn’t seem to be much of a performance difference between setting aStyledProperty
with local priority and setting aDirectProperty
. It seems that the only remaining benefit ofDirectProperty
is providing support for read-only properties.You have described the issue well, so I don’t need to replicate most of what I said. I’ll just add these details about the use case at my company:
I would implement this change by first removing
DirectProperty.Setter
andAvaloniaObject.SetDirectValueUnchecked
, and then fixing all the compile errors that arise. The fix for each error would be switching toStyledProperty
or removing the setter and making the property read-only.AvaloniaObject.SetAndRaise
(which is a protected method) would remain for use within private/protected C# property setters.Once the property is changed to
StyledProperty
, a coercion method could be executed before the value is written.