From 61783340a7e3e20824764ce48edea4400664d478 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Mon, 13 Jan 2025 20:04:01 -0300 Subject: [PATCH 1/3] Improve Styled properties doc --- .../custom-controls/defining-properties.md | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/guides/custom-controls/defining-properties.md b/docs/guides/custom-controls/defining-properties.md index 6e1ce5074..f6b268ad0 100644 --- a/docs/guides/custom-controls/defining-properties.md +++ b/docs/guides/custom-controls/defining-properties.md @@ -20,21 +20,39 @@ On this page, you will see how to implement a property so that it can be changed ### Register a Styled Property -You register a styled property by defining a static read-only field and using the `AvaloniaProperty.Register` method. +You register a styled property by defining a `public static read-only` field of type `StyledProperty` and set it's value using the `AvaloniaProperty.Register` method. -There is a convention for the name of a property. It must follow the pattern: +:::warning +The name of this static field **MUST** be the same name as the public attribute, followed by "`Property`" at the end. +Failure to follow this naming convention may result in "*Unable to find suitable setter or adder for property*" errors during compilation. +::: -``` -[AttributeName]Property -``` +### Styled Property Register Example -This means that _Avalonia UI_ will look for an attribute in the XAML, like this: +```csharp +public static readonly StyledProperty ExampleProperty = + AvaloniaProperty.Register(nameof(Example), "Default value here"); +public string Example +{ + get => GetValue(ExampleProperty); + set => SetValue(ExampleProperty, value); +} ``` - + +:::info +Note that the getter/setter of the public property uses the special Avalonia UI `GetValue` and `SetValue` methods and should not be changed to something else nor do any extra work inside of the get/set. +::: + +Then, _Avalonia UI_ will look for an attribute in the XAML, like this: + +```xml + ``` -For example, with a styled property in place, you can control the background color of the custom control from the window styles collection: +### Styled property in a custom control example + +With a styled property in place, you can control the background color of the custom control from the window styles collection: ```xml title='MainWindow.axaml' From 39198902e4003bc747ca9941c7a4f0f1ec651f68 Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Mon, 13 Jan 2025 20:16:03 -0300 Subject: [PATCH 2/3] Improve Control Types links --- .../custom-controls/how-to-create-templated-controls.md | 2 ++ docs/guides/custom-controls/index.md | 2 ++ docs/guides/custom-controls/types-of-control.md | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/guides/custom-controls/how-to-create-templated-controls.md b/docs/guides/custom-controls/how-to-create-templated-controls.md index b6f29c542..2f016d46a 100644 --- a/docs/guides/custom-controls/how-to-create-templated-controls.md +++ b/docs/guides/custom-controls/how-to-create-templated-controls.md @@ -6,6 +6,8 @@ title: How To Create Templated Controls # How To Create Templated Controls +Templated Controls are one of the [types of controls](./types-of-control.md) available for authoring custom controls.They are lookless controls, meaning that they can be restyled for different themes and applications. + ## Data Binding When you're creating a control template and you want to bind to the templated parent you can use: diff --git a/docs/guides/custom-controls/index.md b/docs/guides/custom-controls/index.md index 3f3144cc6..2f191d28c 100644 --- a/docs/guides/custom-controls/index.md +++ b/docs/guides/custom-controls/index.md @@ -14,6 +14,8 @@ Before you start to create your own control, you must decide which type of custo ### Custom Control +Basic Controls are one of the [types of controls](./types-of-control.md) available for authoring custom controls. + A custom control draws itself using the _Avalonia UI_ graphics system, using basic methods for shapes, lines, fills, text, and many others. You can define your own properties, events and pseudo classes. Some of the _Avalonia UI_ built-in controls are like this. For example, the text block control (`TextBlock` class) and the image control (`Image` class). diff --git a/docs/guides/custom-controls/types-of-control.md b/docs/guides/custom-controls/types-of-control.md index f4d8c265b..e9224876d 100644 --- a/docs/guides/custom-controls/types-of-control.md +++ b/docs/guides/custom-controls/types-of-control.md @@ -11,7 +11,7 @@ UserControls are the simplest way to author controls. This type of control is be ### Templated Controls -TemplatedControls are best used for generic controls that can be shared among various applications. They are lookless controls, meaning that they can be restyled for different themes and applications. The majority of standard controls defined by Avalonia fit into this category. +[TemplatedControls](./how-to-create-templated-controls.md) are best used for generic controls that can be shared among various applications. They are lookless controls, meaning that they can be restyled for different themes and applications. The majority of standard controls defined by Avalonia fit into this category. :::info In WPF/UWP you would inherit from the Control class to create a new templated control, but in Avalonia you should inherit from TemplatedControl. @@ -23,7 +23,7 @@ If you want to provide a Style for your TemplatedControl in a separate file, rem ### Basic Controls -Basic Controls are the foundation of user interfaces - they draw themselves using geometry by overriding the Visual.Render method. Controls such as TextBlock and Image fall into this category. +[Basic Controls](./index.md) are the foundation of user interfaces - they draw themselves using geometry by overriding the Visual.Render method. Controls such as TextBlock and Image fall into this category. :::info In WPF/UWP you would inherit from the FrameworkElement class to create a new basic control, but in Avalonia you should inherit from Control. From bb810f9493c02c144063cecfd7f4c11344f394df Mon Sep 17 00:00:00 2001 From: Leo Bottaro Date: Mon, 13 Jan 2025 20:16:16 -0300 Subject: [PATCH 3/3] Add How to create a user control with example --- .../custom-controls/defining-properties.md | 2 + .../how-to-create-a-user-control.md | 120 ++++++++++++++++++ .../custom-controls/types-of-control.md | 2 +- sidebars.js | 1 + .../custom-controls/how-to-uc-example.png | Bin 0 -> 13655 bytes 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 docs/guides/custom-controls/how-to-create-a-user-control.md create mode 100644 static/img/guides/custom-controls/how-to-uc-example.png diff --git a/docs/guides/custom-controls/defining-properties.md b/docs/guides/custom-controls/defining-properties.md index f6b268ad0..76bf92562 100644 --- a/docs/guides/custom-controls/defining-properties.md +++ b/docs/guides/custom-controls/defining-properties.md @@ -112,4 +112,6 @@ The styled property will work both at run-time and in the preview panel. :::info For more advanced information about how to create a custom control, see [here](../custom-controls/how-to-create-advanced-custom-controls.md). + +For info on how to bind to these properties on a user control, see [here](./how-to-create-a-user-control.md) ::: diff --git a/docs/guides/custom-controls/how-to-create-a-user-control.md b/docs/guides/custom-controls/how-to-create-a-user-control.md new file mode 100644 index 000000000..516e19164 --- /dev/null +++ b/docs/guides/custom-controls/how-to-create-a-user-control.md @@ -0,0 +1,120 @@ +--- +id: how-to-create-a-user-control +title: How To Create a User Control +--- + +import ExampleUserControlScreenshot from '/img/guides/custom-controls/how-to-uc-example.png'; + +# How To Create a User Control + +UserControls are one of the [types of controls](./types-of-control.md) available for authoring custom controls, User Controls being the simplest way to do so. This type of control is best for big "views" or "pages", but also very good for creating smaller modular controls that can be sprinkled throughout the UI. + +UserControls are authored in the same way as you would author a Window: by creating a new UserControl from a template and then adding controls to it. + +### Binding data + +Then there are two common ways to bind data on your User Control: + +For "big" User Controls that need business logic, like a full "view" for a tab, it's recommended to follow the [MVVM pattern](../../concepts/the-mvvm-pattern/index.md) and create a ViewModel for that specific User Control. + +For smaller User Controls that does not require any business logic, like a "welcome badge" for the user, a simple way to bind data is to create some custom [Styled Properties](./defining-properties.md) on the code-behind and Bind the controls attributes to it. This makes the User Control "modular" and able to bind to many different use cases as needed. + +### Example User Control + +Here's an example on how to make a small UserControl and Bind the Attributes to the Styled Properties defined on the code-behind: + +1. Create a `UserControls` folder +2. Inside of it add a new *Avalonia User Control*, call it `GreetingCard` +3. In the `GreetingCard.axaml.cs` (code-behind), define the Styled Properties needed. + +:::info +It is **NOT** recommended to put *any* business logic in here, if needed use a ViewModel instead. These should be kept as simple "data pass-through" properties. +::: + +:::info +Note that the getter/setter of the public property uses the special Avalonia UI `GetValue` and `SetValue` methods and should not be changed to something else nor do any extra work inside of the get/set. +::: + +```csharp title='UserGreetingCard.axaml.cs (Code Behind)' +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace Example.UserControls; + +public partial class GreetingCard : UserControl +{ + public GreetingCard() + { + InitializeComponent(); + AffectsRender(UserNameProperty, UserPictureProperty); + } + + public static readonly StyledProperty UserNameProperty = + AvaloniaProperty.Register(nameof(UserName),"Unknown"); + + public string UserName + { + get => GetValue(UserNameProperty); + set => SetValue(UserNameProperty, value); + } + + public static readonly StyledProperty UserPictureProperty = + AvaloniaProperty.Register(nameof(UserPicture)); + + public IImage? UserPicture + { + get => GetValue(UserPictureProperty); + set => SetValue(UserPictureProperty, value); + } +} +``` + +4. In the `GreetingCard.axaml`, define how the User Control will look. +5. Inside the `UserControl` main tag, define a name in the `x:Name` attribute. This name will be used for the Data Binding. +6. In the controls that need access to the code-behind styled properties, Bind them using the [Data Binding Short Hand Syntax](../../basics/data/data-binding/data-binding-syntax.md#data-binding-sources) `#ParentXName.Property` + +```xml title='UserGreetingCard.axaml' + + + + + + + + + + + +``` + +8. In the `MainWindow.axaml` file, Import the User Controls [xml namespace](../../basics/user-interface/introduction-to-xaml.md#xml-namespaces) as `xmlns:uc` +9. Add the new GreetingCard User Control tag by specifying the namespace:ControlName, in this case it's `uc:GreetingCard`. +10. Now the Attributes defined previously can be set directly or be defined by a Binding. + +```xml title='MainWindow.axaml' + + + + + + +``` + +This is the result of the example: + \ No newline at end of file diff --git a/docs/guides/custom-controls/types-of-control.md b/docs/guides/custom-controls/types-of-control.md index e9224876d..8c4dfade0 100644 --- a/docs/guides/custom-controls/types-of-control.md +++ b/docs/guides/custom-controls/types-of-control.md @@ -7,7 +7,7 @@ If you want to create your own controls, there are three main categories of cont ### User Controls -UserControls are the simplest way to author controls. This type of control is best for "views" or "pages" that are specific to an application. UserControls are authored in the same way as you would author a Window: by creating a new UserControl from a template and adding controls to it. +[UserControls](./how-to-create-a-user-control.md) are the simplest way to author controls. This type of control is best for big "views" and "pages" or smaller simple controls, like "badges" or "notifications". UserControls are authored in the same way as you would author a Window: by creating a new UserControl from a template and adding controls to it. ### Templated Controls diff --git a/sidebars.js b/sidebars.js index 0d9f424fe..d55b9a78c 100644 --- a/sidebars.js +++ b/sidebars.js @@ -326,6 +326,7 @@ const sidebars = { 'guides/custom-controls/create-a-custom-panel', 'guides/custom-controls/defining-properties', 'guides/custom-controls/draw-with-a-property', + 'guides/custom-controls/how-to-create-a-user-control', 'guides/custom-controls/how-to-create-a-custom-controls-library', 'guides/custom-controls/how-to-create-a-custom-flyout', 'guides/custom-controls/how-to-create-advanced-custom-controls', diff --git a/static/img/guides/custom-controls/how-to-uc-example.png b/static/img/guides/custom-controls/how-to-uc-example.png new file mode 100644 index 0000000000000000000000000000000000000000..5098eb69e4b25d692558549a7df4aae32a203383 GIT binary patch literal 13655 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