I'm using AvaloniaUI and making a UserControl with a StyledProperty using. I'm using MVVM. The problem is that when I have a Binding to my styledproperty but it doesn't update.
I want to use the UserControl like this <Comp:ValueTextBlock VariableName="{Binding RobotSettingsModel.Robot_SP}"/> where VariableName uses a binding to a model that is created in the ViewModel.
The problem is that I can't seem to get the StyledProperty to work when I use a binding. When I set VariableName directly in the view it does work
// this works
<Comp:ValueTextBlock VariableName="PLC_U1_Robot_SP"/>
// this doesn't
<Comp:ValueTextBlock VariableName="{Binding RobotSettingsModel.Robot_SP}" DescriptionLocation="Left"/>
What am I doing wrong here?
The code-behind for my UserControl ValueTextBlock looks something like this:
public class ValueTextBlock : UserControl
{
private ValueTextBlockVm _viewModel;
#region --- Variable name properties ---
public static readonly StyledProperty<string> VariableNameProperty = AvaloniaProperty.Register<ValueTextBlock, string>(nameof(VariableName), defaultBindingMode: BindingMode.TwoWay, defaultValue: "UNKNOWN DProperty");
public string VariableName
{
get { return _viewModel.vmVariableName; } // the property is used in the ViewModel
set { _viewModel.vmVariableName = value; }
}
#endregion
#region constructor
public ValueTextBlock()
{
this.InitializeComponent();
// create new instance of viewmodel and attach it as DataContext
_viewModel = new ValueTextBlockVm();
this.DataContext = _viewModel;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
#endregion
}
In the ViewModel for ValueTextBlock vmVariableName is done like this:
private string _vmVariableName = "UKN";
public string vmVariableName
{
get => _vmVariableName;
set => this.RaiseAndSetIfChanged(ref _vmVariableName, value);
}
When i use this UserControl and directly set VariableName in view it works, but when I use Binding it doesn't work. This is my view:
<StackPanel>
<Comp:ValueTextBlock VariableName="PLC_U1_Robot_SP"/> <!-- directly setting VariableName works -->
<Comp:ValueTextBlock VariableName="{Binding RobotSettingsModel.Robot_SP}" DescriptionLocation="Left"/> <!-- binding VariableName to a model doesn't work -->
<TextBlock Text="{Binding RobotSettingsModel.Robot_SP}"/> <!-- binding normal text to a model does work-->
</StackPanel>
Code-behind for the view
public class ucRobotSettings : UserControl
{
private ucRobotSettingsVm _viewModel;
#region properties
public string Prefix
{
get { return _viewModel.vmPrefix; }
set { _viewModel.vmPrefix = value; }
}
public static readonly StyledProperty<string> PrefixProperty = AvaloniaProperty.Register<ucRobotSettings, string>(nameof(Prefix));
#endregion
#region constructor
public ucRobotSettings()
{
this.InitializeComponent();
// create new instance of viewmodel and attach it as DataContext
_viewModel = new ucRobotSettingsVm();
this.DataContext = _viewModel;
this.AttachedToVisualTree += ucRobotSettings_AttachedToVisualTree;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
#endregion
private void ucRobotSettings_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
_viewModel.OnAttachedToVisualTree();
}
}
In the ViewModel a new RobotSettingsModel is made, this is what I want to bind to in the View
public class ucRobotSettingsVm : ViewModelBase
{
#region --- properties ---
private string _vmPrefix;
public string vmPrefix
{
get => _vmPrefix;
set => this.RaiseAndSetIfChanged(ref _vmPrefix, value);
}
private RobotSettingsModel _RobotSettingsModel = new RobotSettingsModel();
public RobotSettingsModel RobotSettingsModel
{
get => _RobotSettingsModel;
set => this.RaiseAndSetIfChanged(ref _RobotSettingsModel, value);
}
#endregion
public ucRobotSettingsVm() { }
public void OnAttachedToVisualTree()
{
// don't update if the prefix hasn't changed
if (RobotSettingsModel.Prefix != vmPrefix) RobotSettingsModel.Prefix = vmPrefix;
}
}
The model that is used in ucRobotSettings looks like this:
public class RobotSettingsModel : ReactiveObject
{
// unit prefix.
private string _Prefix;
public string Prefix { get => _Prefix; set { this.RaiseAndSetIfChanged(ref _Prefix, value); NotifyPropertyChanged(); } }
// values
private string _Robot_SP = "UKN";
public string Robot_SP { get => _Robot_SP; set => this.RaiseAndSetIfChanged(ref _Robot_SP, value); }
public RobotSettingsModel()
{ }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
if (propertyName == "Prefix") // only update when property "Prefix changes"
{
Robot_SP = Prefix + "_" + nameof(Robot_SP);
// inform outside outside world the complete class has PropertyChanged
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}