Search code examples
c#avaloniauiavalonia

AvaloniaUI: DirectProperty in UserControl


I'm trying to learn AvaloniaUI. I would like to know how DirectProperties work.

So: I created a UserControl: TestUC I added a DirectProperty in it: Value I added 4 TextBoxes. 2 of them in the MainView and 2 in the TestUC. I added bindings to the "greeting" field in the MainViewModel.

I try to sync all 4 TextBoxes. Just to learn how DirectoProperty works. The 2 TextBoxes in the TestUC are not synced with the others. So It looks like I miss something with DirectProperties.

I have a github repo here to show this.

What do I miss?

Here is the relevant part in MainView.axaml

<StackPanel>
            <Label Content="First in MainView"/>
            <TextBox Text="{Binding Greeting}" HorizontalAlignment="Stretch"/>

            <Label Content="Second in MainView"/>
            <TextBox Text="{Binding Greeting}" HorizontalAlignment="Stretch"/>

            <Label Content="UserControl"/>          
            <uc:TestUC Value="{Binding Greeting}"/>     
</StackPanel>

TestUC.axaml

<StackPanel>
    <TextBox Text="{Binding Value,Mode=TwoWay}"/>
    <TextBox Text="{Binding Value,Mode=TwoWay}"/>
</StackPanel>

TestUC.axaml.cs

public static readonly DirectProperty<TestUC, string> ValueProperty =
   AvaloniaProperty.RegisterDirect<TestUC, string>(
       nameof(Value),
       o => o.Value,
       (o, v) => o.Value = v,
       defaultBindingMode: BindingMode.TwoWay);

private string _value;

public string Value
{
    get { return _value; }
    set { SetAndRaise(ValueProperty, ref _value, value); }
}

Solution

  • User Controls is not design for this scenarios ...look at Types of Custom Controls Templated control is what I will choose

    StyledProperty is more suited than DirectProperty, because your property will use the Avalonia Style system

    the following is what you want create a Templated control called MyContorl in Controls namespace

    public class MyControl : TemplatedControl
    {
        public static readonly StyledProperty<string> ValueProperty =
           AvaloniaProperty.Register<MyControl, string>(
               nameof(Value),
               defaultBindingMode: BindingMode.TwoWay);
    
        public string Value
        {
            get => GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }
    }
    

    and create a template for it

    <Styles xmlns="https://github.com/avaloniaui"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:controls="using:TestAvalonia.Controls">
      <Design.PreviewWith>
        <controls:MyControl />
      </Design.PreviewWith>
    
      <Style Selector="controls|MyControl">
        <!-- Set Defaults -->
        <Setter Property="Template">
          <ControlTemplate>
            <StackPanel>
              <Label Content="one way binding"/>
              <TextBox Text="{TemplateBinding Value}"/>
              <Label Content="Two way binndign"/>
              <TextBox Text="{Binding Value,Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
            </StackPanel>
          </ControlTemplate>
        </Setter>
      </Style>
    </Styles>
    

    and include this style in your Application style

    <StyleInclude Source="/Controls/MyControl.axaml"/>
    

    finally use it as you expect

    xmlns:controls ="using:TestAvalonia.Controls"
    ....
    <Label Content="controls"/>
    <controls:MyControl Value="{Binding Greeting}"/>
    
    

    I have create Pull request in your GitHub with this changes