Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Styled Properties doc, Add User Control Examples #609

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions docs/guides/custom-controls/defining-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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<string> ExampleProperty =
AvaloniaProperty.Register<MyCustomControl, string>(nameof(Example), "Default value here");

public string Example
{
get => GetValue(ExampleProperty);
set => SetValue(ExampleProperty, value);
}
```
<MyCustomControl AttributeName="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
<MyCustomControl Example="value" ... >
```

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'
<Window xmlns="https://github.com/avaloniaui"
Expand Down Expand Up @@ -88,14 +106,12 @@ namespace AvaloniaCCExample.CustomControls
}
```

:::info
Note that the getter/setter of the property uses the special Avalonia UI `GetValue` and `SetValue` methods.
:::

The styled property will work both at run-time and in the preview panel.

<img src={DefiningPropertyPreviewScreenshot} alt=''/>

:::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)
:::
120 changes: 120 additions & 0 deletions docs/guides/custom-controls/how-to-create-a-user-control.md
Original file line number Diff line number Diff line change
@@ -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<GreetingCard>(UserNameProperty, UserPictureProperty);
}

public static readonly StyledProperty<string> UserNameProperty =
AvaloniaProperty.Register<GreetingCard,string>(nameof(UserName),"Unknown");

public string UserName
{
get => GetValue(UserNameProperty);
set => SetValue(UserNameProperty, value);
}

public static readonly StyledProperty<IImage?> UserPictureProperty =
AvaloniaProperty.Register<GreetingCard,IImage?>(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'
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="90"
MaxHeight="90"
x:Class="Example.UserControls.GreetingCard"
x:Name="UserControlXName">
<Border Background="White" Margin="10" Padding="10" CornerRadius="10" BoxShadow="0 5 10 0 DarkGray">
<StackPanel Orientation="Horizontal" ClipToBounds="True">
<Image Width="50" Height="50"
Source="{Binding #UserControlXName.UserPicture}"/>
<TextBlock Margin="10,0,0,0" VerticalAlignment="Center">
<Run Text="Logged in as:"/>
<Run Text="{Binding #UserControlXName.UserName}"/>
</TextBlock>
</StackPanel>
</Border>
</UserControl>
```

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'
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:uc="using:Example.UserControls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Example.MainWindow"
Title="Avalonia User Control">

<Panel Margin="40">
<uc:GreetingCard UserName="Avalonia" UserPicture="/Assets/avalonia-logo.ico"/>
</Panel>
</Window>
```

This is the result of the example:
<img src={ExampleUserControlScreenshot} alt=''/>
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/custom-controls/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
6 changes: 3 additions & 3 deletions docs/guides/custom-controls/types-of-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ 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

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.
Expand All @@ -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.
Expand Down
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.