I have a problem binding data in a custom view in Xamarin forms to the view model of the containing page.
My Custom View is very simple, a pair of labels representing a key value pair:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="KeyValueView">
<Grid VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label x:Name="KeyLabel" Text="{Binding Key}" Grid.Column="0" HorizontalOptions="Start" />
<Label x:Name="ValueLabel" Text="{Binding Value}" Grid.Column="1" HorizontalOptions="EndAndExpand" />
</Grid>
</ContentView>
with the code behind:
public partial class KeyValueView : ContentView
{
public KeyValueView()
{
InitializeComponent();
this.VerticalOptions = LayoutOptions.Start;
this.BindingContext = this;
}
public static readonly BindableProperty ValueProperty =
BindableProperty.Create<KeyValueView, string>(w => w.Value, default(string));
public string Value
{
get {return (string)GetValue(ValueProperty);}
set {SetValue(ValueProperty, value);}
}
public static readonly BindableProperty KeyProperty =
BindableProperty.Create<KeyValueView, string>(w => w.Key, default(string));
public string Key
{
get {return (string)GetValue(KeyProperty);}
set {SetValue(KeyProperty, value);}
}
}
This is consumed in a page as follows:
<views:KeyValueView Key="Order Number" Value="{Binding document_number}" />
The problem is that the Key string is displayed as expected, but the value string is not. I have tried forcing a PropertyChanged event on the document_number property, this did not help. I have also tried explicitly setting the Text property on the Labels during the setter of the Key/Value properties of the custom view:
public string Key
{
get {return (string)GetValue(KeyProperty);}
set {
SetValue(KeyProperty, value);
KeyLabel.Text = value;
}
}
Again this did not help, the setter code never seems to get executed (I placed a breakpoint on it and it was not hit)
If I add an out of the box control such as a Label bound directly to the property in the page, this displays correctly:
<Label Text="{Binding document_number}"/>
<views:KeyValueView Key="Order Number" Value="{Binding document_number}" />
Can anyone explain why this is happening (or rather not happening)?
View-to-View Bindings You can define data bindings to link properties of two views on the same page. In this case, you set the BindingContext of the target object using the x:Reference markup extension.
The binding references the source object. To set the data binding, use the following two members of the target class: The BindingContext property specifies the source object. The SetBinding method specifies the target property and source property.
However, the default binding mode for the Value property of Slider is TwoWay . This means that when the Value property is a data-binding target, then the target is set from the source (as usual) but the source is also set from the target. This is what allows the Slider to be set from the initial Opacity value.
Custom Control Definition In the XAML file for your custom control, you can use any of the existing Xamarin. Forms controls or other custom controls previously defined. To create bindable properties, we need a static public property of type BindableProperty, like in the following definition of the Caption property.
Don't assign bindings internally inside custom controls. Use this:
public partial class KeyValueView : ContentView
{
public KeyValueView()
{
InitializeComponent();
this.VerticalOptions = LayoutOptions.Start;
}
public static readonly BindableProperty ValueProperty =
BindableProperty.Create<KeyValueView, string>(w => w.Value, default(string));
public string Value
{
get {return (string)GetValue(ValueProperty);}
set {SetValue(ValueProperty, value);}
}
public static readonly BindableProperty KeyProperty =
BindableProperty.Create<KeyValueView, string>(w => w.Key, default(string));
public string Key
{
get {return (string)GetValue(KeyProperty);}
set {SetValue(KeyProperty, value);}
}
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
if (propertyName == ValueProperty.PropertyName)
{
ValueLabel.Text = Value;
}
if (propertyName == KeyProperty.PropertyName)
{
KeyLabel.Text = Key;
}
}
}
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="KeyValueView">
<Grid VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label x:Name="KeyLabel" Grid.Column="0" HorizontalOptions="Start" />
<Label x:Name="ValueLabel" Grid.Column="1" HorizontalOptions="EndAndExpand" />
</Grid>
</ContentView>
Treat properties (eg. Value) as references to BindableProperty (ValueProperty). If you do something inside Value setter BindableProperty won't be notified about it AND opposite if you do something with BindableProperty (assign/change ValueProperty) - property Value setter won't be called.
If you want to handle PropertyChanged you have an override for that (OnPropertyChanged) or Actions (as additional parameters when creating BindableProperty).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With