Skip to content

Commit 2ae5a85

Browse files
committed
main
1 parent 4e51905 commit 2ae5a85

File tree

13 files changed

+727
-460
lines changed

13 files changed

+727
-460
lines changed

docs/document/Skill/Avalonia/docs/1. Fundamentals/2. XAML Essentials.md

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
11
# XAML Essentials
22

3-
Each tag in XAML file is of `AvaloniaObject`, it can be of `Control` or something else but inherits from `AvaloniaObject`.
4-
5-
::: code-group
6-
7-
```xml [App.axaml]
8-
<!-- Application is AvaloniaObject but not a Control --> <!-- [!code highlight] -->
9-
<Application xmlns="https://github.com/avaloniaui"
10-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
11-
x:Class="MyApp.App"
12-
xmlns:local="using:MyApp"
13-
RequestedThemeVariant="Default">
14-
<Application.DataTemplates>
15-
<local:ViewLocator/>
16-
</Application.DataTemplates>
17-
<Application.Styles>
18-
<FluentTheme />
19-
</Application.Styles>
20-
</Application>
21-
```
22-
:::
23-
243
## XAML Namespace
254

265
XAML namespace was a feature inherited from xml, it allows to import namespaces at any level of element, and all of its child elements within the scope can access the namespace.
@@ -64,14 +43,22 @@ Each child element within the scope can access it.
6443
- `x:DataType`: the `DataContext`(aka ViewModel in MVVM) for current control.
6544
- `x:Name`: name of the instance of control within the code-behind class.
6645
- XAML compiler would generate an instance by that name for your code-behind class.
67-
- avalonia has a `StyledElement.Name` property available, which is an equivalence of `x:Name`
68-
- `x:Static`: accessor for **static members in assembly**
46+
- avalonia has a `StyledElement.NameProperty` available, which is an equivalence of `x:Name`
47+
- `x:Type`: accessor for types, can be used as a **markup extension**. It's the equivalent of using the `typeof` operator.
48+
```xml
49+
<Window.DataTemplates>
50+
<DataTemplate DataType="{x:Type local:Student}">
51+
<!-- ... -->
52+
</DataTemplate>
53+
</Window.DataTemplates>
54+
```
55+
- `x:Static`: accessor for **static members in assembly**, can be used as a **markup extension**.
6956
```xml
7057
<Window.Resources>
7158
<SolidColorBrush Color="{x:Static Colors.Aqua}"></SolidColorBrush> <!-- [!code highlight] -->
7259
</Window.Resources>
7360
```
74-
- `x:Key`: similar to `x:Name` but for static identifier in resource dictionary
61+
- `x:Key`: similar to `x:Name` but for static identifier in resource **dictionary**
7562

7663
::: code-group
7764
```xml [XAML]

docs/document/Skill/Avalonia/docs/1. Fundamentals/Avalonia Types.md

Lines changed: 240 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
# Avalonia Types
22

3-
One should know about avalonia types before learning XAML, each tag in XAML constructs a instance from corresponding type.
4-
XAML also has some implicit bindings for special types with certain properties, such as `ContentControl.Content` and `ItemsControl.Items`.
5-
There's two critical base classes in avalonia:
6-
- `AvaloniaObject`: a type containing a bunch of `AvaloniaProperty`
7-
- all components/controls are of `AvaloniaObject`, such as `Window`, `Button`.
8-
- `AvaloniaProperty`: how avalonia defined a **shared property metadata** for property **owners**
9-
- `StyledProperty`: property definition supports styling overriding and inheritance
10-
- `DirectProperty`: simple property for storing plain data
11-
123
## AvaloniaObject
134

145
All controls are derived from `AvaloniaObject` class.
156
There's some special and critical control base class you may encounter when using avalonia.
167
- `StyledElement`: a minimal useable control base which starts styling support, most of controls became applicable after this class
17-
- `ContentControl`: control for storing singular content
18-
- `ItemsControl`: control for storing multiple items
8+
- `ContentControl`: control for presenting singular content
9+
- `ItemsControl`: control for presenting multiple items
1910
- `UserControl`: TODO
2011
- `TemplatedControl`: TODO
2112

@@ -91,8 +82,8 @@ public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged {
9182
### Direct Property
9283

9384
`DirectProperty` is a minimal property type registered for **plain data**, ideal for **indicators** of a control, such as `Button.IsPressed` or a **statistic** such as `ItemsControl.ItemCount` or even an **collection**.
94-
Unlike `StyledProperty` which stores backing values in `ValueStore`, `DirectProperty` simply requires a true **backing field** within the class.
95-
`SetAndRaise` will raise `AvaloniaObject.PropertyChanged` event **when the new value is different than old one.**
85+
Unlike `StyledProperty` which stores backing values in `ValueStore`, `DirectProperty` simply requires a dedicated **backing field** within the class.
86+
`AvaloniaObject.SetAndRaise` will raise `AvaloniaObject.PropertyChanged` event **when the new value is different than the old.**
9687

9788
```cs
9889
public static readonly DirectProperty<Button, bool> IsPressedProperty =
@@ -106,9 +97,60 @@ public bool IsPressed {
10697
}
10798
```
10899

100+
> [!IMPORTANT]
101+
> It doesn't mean that a plain data can only be of `DirectProperty`, it can be a `StyledProperty` depending on its role, `Layoutable.HeightProperty` for example is a `StyledProperty`.
102+
109103
### Attached Property
110104

111-
TODO: what is that?
105+
`AttachedProperty` is a static property presentation that, it **does not require instance member as backing source**(like what `StyledProperty` and `DirectProperty` required) but two **conventional static getter/setter** methods to be invoked on runtime.
106+
107+
Besides `TOwner` and `TValue`, `AttachedProperty` has an extra type parameter
108+
109+
- `THost`: which kind of type the property can attach to
110+
111+
Static `Design` class is one of the great examples of attached property, what you have bind for `Design.DataContext` is exactly a property attached to your current control(`MainWindow` for example)
112+
You would notice that `AttachedProperty` would require two **static** methods with conventional name, those methods would be invoked by runtime by their special name.
113+
And the property value would be **still managed by `AvaloniaObject.GetValue` and `AvaloniaObject.SetValue`**, meaning that it has **same mechanism** as `StyledProperty`(the `ValueStore` was involved).
114+
115+
::: code-group
116+
117+
118+
```cs [Design.DataContext]
119+
public static readonly AttachedProperty<object> DataContextProperty = AvaloniaProperty
120+
.RegisterAttached<Control, object>("DataContext", typeof(Design));
121+
122+
public static void SetDataContext(Control host, object value) {
123+
host.SetValue(DataContextProperty, value); // [!code highlight]
124+
}
125+
126+
public static object GetDataContext(Control host) {
127+
return host.GetValue(DataContextProperty); // [!code highlight]
128+
}
129+
```
130+
131+
```xml [Attach DataContext for Control]
132+
<Window xmlns="https://github.com/avaloniaui"
133+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
134+
xmlns:vm="using:MyAva.ViewModels"
135+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
136+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
137+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
138+
x:Class="MyAva.Views.MainWindow"
139+
x:DataType="vm:MainWindowViewModel"
140+
Icon="/Assets/avalonia-logo.ico"
141+
Title="MyAva">
142+
143+
<Design.DataContext> <!-- [!code focus] -->
144+
<vm:MainWindowViewModel /> <!-- [!code focus] -->
145+
</Design.DataContext> <!-- [!code focus] -->
146+
</Window>
147+
```
148+
:::
149+
150+
> [!IMPORTANT]
151+
> Yes, XAML compiler would recognize the attached property and properly set the value at runtime.
152+
> Even though it was written in an instance construction syntax.
153+
112154

113155
### Reuse Property Definition
114156

@@ -161,11 +203,50 @@ public IBinding this[IndexerDescriptor binding] {
161203
}
162204
```
163205

164-
## Data Context
206+
## StyledElement.DataContext
165207

166208
`StyledElement.DataContext` is **the instance of view model** which allows you to access(or even mutate) the state of view model within a control(code-behind).
167209
Avalonia **searches upward hierarchically** for `DataContext` property until top level element, meaning that controls can share context with children.
168210

211+
::: details definition
212+
213+
```cs
214+
public class StyledElement : Animatable,
215+
IDataContextProvider,
216+
ILogical,
217+
IThemeVariantHost,
218+
IResourceHost2,
219+
IStyleHost,
220+
ISetLogicalParent,
221+
ISetInheritanceParent,
222+
ISupportInitialize,
223+
INamed,
224+
IAvaloniaListItemValidator<ILogical>,
225+
#pragma warning disable CS0618 // Type or member is obsolete
226+
IStyleable
227+
#pragma warning restore CS0618 // Type or member is obsolete
228+
{
229+
/** ... **/
230+
public static readonly StyledProperty<object?> DataContextProperty =
231+
AvaloniaProperty.Register<StyledElement, object?>(
232+
nameof(DataContext),
233+
defaultValue: null,
234+
inherits: true,
235+
defaultBindingMode: BindingMode.OneWay,
236+
validate: null,
237+
coerce: null,
238+
enableDataValidation: false,
239+
notifying: DataContextNotifying);
240+
241+
public object? DataContext {
242+
get { return GetValue(DataContextProperty); }
243+
set { SetValue(DataContextProperty, value); }
244+
}
245+
/** ... **/
246+
}
247+
```
248+
:::
249+
169250
```cs
170251
using Avalonia.Controls;
171252
using MyApp.ViewModels;
@@ -187,8 +268,7 @@ public partial class MainWindow : Window {
187268

188269
Views beside `MainWindow`(which is the default window generated by avalonia project Template) needs explicit set for `DataContext`, because `MainWindow.DataContext` was assigned in `App.axaml.cs` so you don't need to worry about it.
189270

190-
<details>
191-
<summary>App.axaml.cs</summary>
271+
::: details App.axaml.cs
192272

193273
```cs
194274
public override void OnFrameworkInitializationCompleted() {
@@ -203,7 +283,7 @@ public override void OnFrameworkInitializationCompleted() {
203283
}
204284
```
205285

206-
</details>
286+
:::
207287

208288
```cs
209289
namespace MyApp.Views;
@@ -225,16 +305,14 @@ You can create a dedicated view model class that inherits from a formal one, and
225305
public class MyDesignTimeViewModel: MyViewModel { // [!code highlight]
226306
public MyDesignTimeViewModel() {
227307
// properties are inherited from MyViewModel
228-
ServerName = "John Price";
229-
ServiceTitle = "Hair Cut and Beard Trim";
230-
ServicePrice = (decimal)25.5;
231-
ServiceDateTime = new DateTime(2023, 1, 3, 11, 15, 0);
232-
Description = "Please allow 30 minutes.";
308+
Name = "John Smith";
309+
Age = 28;
310+
Title = "Manager";
233311
}
234312
}
235313
```
236314

237-
Then you assign the mock view model class for `Design.DataContext`.
315+
Then you may specify the mock view model class for `Design.DataContext`.
238316

239317
```xml
240318
<UserControl
@@ -246,13 +324,146 @@ Then you assign the mock view model class for `Design.DataContext`.
246324
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="250"
247325
x:Class="MyApp.Views.MyCustomView"
248326
xmlns:vm="using:MyApp.ViewModels"
249-
x:DataType="vm:MyDesignTimeViewModel"> <!-- [!code highlight] -->
327+
x:DataType="vm:MyDesignTimeViewModel"> <!-- [!code focus] -->
250328

251-
<Design.DataContext> <!-- [!code highlight] -->
252-
<vm:MyDesignTimeViewModel/> <!-- [!code highlight] -->
253-
</Design.DataContext> <!-- [!code highlight] -->
329+
<Design.DataContext> <!-- [!code focus] -->
330+
<vm:MyDesignTimeViewModel/> <!-- [!code focus] -->
331+
</Design.DataContext> <!-- [!code focus] -->
254332

255333
</UserControl>
256334
```
257335

258-
## Data Template
336+
## Template Types & Properties
337+
338+
- Template Types:
339+
- `ITemplate<TParam, TControl>`: the base template interface that requires implementer to know how to `Build`(instantiate) the template.
340+
- `IDataTemplate`: inherits from `ITemplate<object?, Control?>`, requiring to know how to validate the `TParam` source using extra `Match` method.
341+
- `DataTemplate`: for defining template for specific data type
342+
- `IControlTemplate`: effectively `ITemplate<TemplatedControl, TemplateResult<Control>?>`
343+
- `ControlTemplate`: common template type for `TemplatedControl.TemplateProperty`
344+
345+
- Template Properties:
346+
- `Control.DataTemplates`: a direct collection of `IDataTemplate`(not a `AvaloniaProperty`)
347+
- provides a way to **present any type(such as a model) as a control**
348+
- `TemplatedControl.TemplateProperty`: how a control was presented, you may override this property for controls to alter its default appearance
349+
- `ContentControl.ContentTemplateProperty`: how `Content` was presented
350+
- `ItemsControl.ItemTemplateProperty`:
351+
352+
353+
### IDataTemplate
354+
355+
The common builtin presenter for displaying objects other than `AvaloniaObject` with certain format, implements `IDataTemplate`.
356+
A typical usage of `IDataTemplate` is `ViewLocator` which determines what kind of control can
357+
**Yes, controls are not specially treated by XAML compiler,** they were only allowed when you specify `.DataTemplates` for `Application` so that the application could load controls by the context object at runtime(**using reflection**).
358+
359+
> [!NOTE]
360+
> If one were to compile ahead-of-time which could not use reflection, should use `CompiledBinding` markup and `DataTemplate.DataType` to inform XAML compiler explicitly.
361+
362+
```xml
363+
<Application.DataTemplates>
364+
<DataTemplate DataType="{x:Type local:Person}"> <!-- [!code focus] -->
365+
<TextBlock>First Name:</TextBlock>
366+
<TextBlock Text="{Binding FirstName}"/>
367+
<TextBlock>Last Name:</TextBlock>
368+
<TextBlock Text="{Binding LastName}"/>
369+
</DataTemplate>
370+
</Application.DataTemplates>
371+
```
372+
373+
- `Match(obj)`: determine whether current `DataContext` could embody using the given template.
374+
- `Build(obj)`: instantiate the template using current `DataContext`
375+
376+
::: code-group
377+
```cs [ViewLocator]
378+
public class ViewLocator : IDataTemplate {
379+
public Control? Build(object? datacontext) {
380+
if (datacontext is null)
381+
return null;
382+
383+
var name = datacontext.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
384+
var type = Type.GetType(name);
385+
386+
if (type != null) {
387+
return (Control)Activator.CreateInstance(type)!;
388+
}
389+
390+
return new TextBlock { Text = "Not Found: " + name };
391+
}
392+
393+
public bool Match(object? datacontext) {
394+
return datacontext is ViewModelBase;
395+
}
396+
}
397+
```
398+
```xml [App.axaml]
399+
<Application xmlns="https://github.com/avaloniaui"
400+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
401+
x:Class="MyAva.App"
402+
xmlns:local="using:MyAva"
403+
RequestedThemeVariant="Default">
404+
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
405+
406+
<Application.DataTemplates> <!-- [!code focus] -->
407+
<local:ViewLocator/> <!-- [!code focus] -->
408+
</Application.DataTemplates> <!-- [!code focus] -->
409+
410+
<Application.Styles>
411+
<FluentTheme />
412+
</Application.Styles>
413+
</Application>
414+
```
415+
:::
416+
417+
418+
### ContentControl.ContentTemplate
419+
420+
`ContentControl.ContentTemplate` is of type `IDataTemplate` that is commonly overridden by `DataTemplate`, the default **presentation of custom data formatting** type.
421+
The whole point of `DataTemplate` is to provide a way to format `ContentProperty` using given format.
422+
423+
<!-- TODO: add example -->
424+
425+
### ItemsControl.ItemTemplate
426+
427+
Similar to `ContentControl.ContentTemplate` but for presenting items
428+
429+
## ControlTemplate
430+
431+
A common placeholder type to present how to re-structure a control's template looking(which you don't have access to modify the control's source definition).
432+
This is typically used to override `TemplatedControl.Template` and with `TemplateBinding` markup extension to read context from containing control.
433+
434+
```xml
435+
<Setter Property="Template">
436+
<ControlTemplate> <!-- [!code highlight] -->
437+
<TextBlock Text="Templated Control" /> <!-- [!code highlight] -->
438+
</ControlTemplate> <!-- [!code highlight] -->
439+
</Setter>
440+
```
441+
442+
## Presenter Controls
443+
444+
Presenter controls are control scaffold for representing certain content as a **wrapper** itself, so that it can bind **any source** of content from context within it.
445+
446+
### ContentPresenter
447+
448+
`ContentPresenter` is a type of placeholder for singular content. It's a lightweight element whose sole purpose is to **define a binding declaration** for displaying the content of a `ContentControl`
449+
So it's typically seen in declaration of `TemplatedControl.Template` and using with `TemplateBinding` markup extension to read context from containing control.
450+
451+
```xml
452+
<ToggleButton.Template>
453+
<ControlTemplate>
454+
<Grid ColumnDefinitions="*,Auto">
455+
<ContentPresenter <!-- [!code highlight] -->
456+
Grid.Column="0" <!-- [!code highlight] -->
457+
Content="{TemplateBinding Content}"> <!-- [!code ++] -->
458+
</ContentPresenter> <!-- [!code highlight] -->
459+
</Grid>
460+
</ControlTemplate>
461+
</ToggleButton.Template>
462+
```
463+
464+
### ItemsPresenter
465+
466+
A place holder type presenting items specifically with `TemplateBinding`.
467+
468+
Similar to `ContentControl.ContentTemplate` but for presenting items
469+

0 commit comments

Comments
 (0)