Currently, I am refactoring an application, where we have lots of duplicated XAML code. I want to use custom UserControls instead. I did a tutorial, on how to create those, but now I am at a point, where I do not know how to solve it.
So, currently I am getting this error:
System.Windows.Markup.XamlParseException: '"Binding" cannot be set for the "PropertyMargin" property of type "TxtInput". "Binding" can only be set for a "DependencyProperty" of a "DependencyObject"
So, basically, I have a custom UserControl:
<UserControl ...>
<UserControl.Resources>
...
</UserControl.Resources>
<DockPanel Margin="{Binding PropertyMargin}">
<md:Card Style="{StaticResource ParamCard}">
<TextBlock Text="{Binding PropertyName}"/>
</md:Card>
</DockPanel>
</UserControl>
With its Code-Behind:
public partial class TxtInput : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
#region Properties
private string _propertyName = string.Empty;
public string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
OnPropertyChanged();
}
}
private string _propertyMargin = "0, 0, 0, 0";
public string PropertyMargin
{
get { return _propertyMargin; }
set
{
_propertyMargin = value;
OnPropertyChanged();
}
}
#endregion
public TxtInput()
{
DataContext = this;
InitializeComponent();
}
private void OnPropertyChanged([CallerMemberName] string? property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
I use this UserControl within another one - and I want to inject the Properties via the "Parent one"
So - for statically set properties, e.g. the PropertyName - that's no problem at all - but for dynamically changing values, for example the ProprtyMargin, it does not work as expected:
"Parent-UserControl"
<uc:TxtInput PropertyName="Developer Name" PropertyMargin="{Binding NameMargin}"/>
The Margin changes, depending, if the validation within the parent-userControl is successful or not (just an example here)
Parent-UserControlViewModel
...
private string _nameMargin;
public string NameMargin
{
get { return _nameMargin; }
set { SetProperty(ref _nameMargin, value); }
}
...
private void SetErrorTxts(List<ValidationFailure> errors)
{
RemoveErrorTxtsFromProperties();
foreach (var _ in errors.Select(error => error.ErrorMessage).Where(error => error.Contains("Name")).Select(error => new { }))
{
// ------------------------------
// Default values for properties
// if set not correctly
// ------------------------------
NameMargin = "0 0 0 16";
}
}
// ------------------------------
// Default values for properties
// if set correctly
// ------------------------------
private void RemoveErrorTxtsFromProperties()
{
NameMargin = "0 0 0 8";
}
I know, it's possible if I set the UserControl's DataContext to the ViewModel of the Control, where I am using it - but I want to use my custom control in different other views as well, so I cannot set the DataContext of this UserControl statically.
Basically, I want the custom UserControl represent the "style" that it gets via the properties from the parent-ViewModel. I hope it's clear, what I mean.
Any tip would be helpful!
As the error message says, to allow binding, you need to implement your properties has dependency property. Here is the documentation about that.
This look like this:
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register(
nameof(PropertyName),
typeof(string),
typeof(TxtInput),
new PropertyMetadata(default(string)));
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}