Bound property not assigned - xamarin.forms

My view...
public partial class TableRow : ViewCell
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(TableRow), null);
public string Text
{
get => (string)GetValue(TextProperty);
set
{
SetValue(TextProperty, value);
lblValue.Text = value;
}
}
Used like this...
<Custom:TableRow NameText="Name" Text="{Binding Name}" />
But when I set the binding context...
BindingContext = candidate;
... you've guessed it! Nothing happens!
What's the missing step?
It's the page's BindingContext that candidate is set to.

Related

Xamarin.Forms Bindable Properties

It may sound like a dumb question but why are bindable properties static?
public static readonly BindableProperty MapSpanProperty = BindableProperty.Create
(
propertyName: "MapSpan",
returnType: typeof(MapSpan),
declaringType: typeof(BindableMap),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: MapSpanPropertyChanged
);
public MapSpan MapSpan
{
get
{
return (MapSpan)GetValue(MapSpanProperty);
}
set
{
SetValue(MapSpanProperty, value);
}
}
I have this code and it works just fine if I make the bindable property static, otherwise, it doesn't work. If I make this bindable property static it means, let's say if I have 2 maps opened at the same time, that this value will be the same on both if I set it on one of them?
Each bindable property has a corresponding public static readonly field of type BindableProperty that is exposed on the same class and that is the identifier of the bindable property.
You could check the source code of the control which provide the binable property.
I use the contentview for example. The code is from the link below. Xamarin forms update viewmodel field from a bindable property
public partial class MyCustomControl : ContentView
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged();
}
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(MyCustomControl),
string.Empty,
propertyChanged: (bindable, oldValue, newValue) =>
{
var control = bindable as MyCustomControl;
//var changingFrom = oldValue as string;
//var changingTo = newValue as string;
control.Title.Text = newValue.ToString();
});
public MyCustomControl()
{
InitializeComponent();
}
The ContentView provide the public static readonly BindableProperty.
//
// Summary:
// An element that contains a single child element.
//
// Remarks:
// The following example shows how to construct a new ContentView with a Label inside.
[ContentProperty("Content")]
public class ContentView : TemplatedView
{
//
// Summary:
// Backing store for the Xamarin.Forms.ContentView.Content property..
//
// Remarks:
// To be added.
public static readonly BindableProperty ContentProperty;
public ContentView();
//
// Summary:
// Gets or sets the content of the ContentView.
public View Content { get; set; }
//
// Summary:
// Method that is called when the binding context changes.
//
// Remarks:
// To be added.
protected override void OnBindingContextChanged();
}
You could check the MS docs about more for the static in binable property. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/bindable-properties

bindableProperty can not get value?

I created a bindableproperty in a customview but it can not get value.
here is the code:
public string BackgroundImage
{
get
{
var image = (image)GetValue(BackgroundImageProperty);
return image;
}
set => SetValue(BackgroundImageProperty, value);
}
the image is null.
public static readonly BindableProperty BackgroundImageProperty = BindableProperty.Create(nameof(BackgroundImage), typeof(string), typeof(MyView), null);
the xaml code:
<local:MyView BackgroundImage="back.png" />
I do want give a string value and give it to a Image.
I have give the BackgroundImage a string value in the xaml.
Solution1:
You can get the value from the event propertyChanged
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(MyView),
null,
propertyChanged: (bindable, oldValue, newValue) =>
{
var value = newValue;
// do some thing you want .
}
);
Solution 2:
You can set the BindingContext in CustomView .
public MyView
{
//...
BindingContext = this;
//...
}

Xamarin.Forms: XAML property pass-through

How do I pass property values to custom control children without writing code?
Content page:
<views:ButtonView Image="newViolationSmall.png" Text="{Binding ParentsText}/>
Then ButtonView.xaml
<StackLayout
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SmartwebsCrossPlatform.Portable.Views.ButtonView">
<Button Image="{Binding Image}"/>
<Label Text="{Binding Text}" />
</StackLayout>
And ButtonView.cs
public partial class ButtonView : StackLayout {
...
//text
public static readonly BindableProperty TextProperty = BindableProperty.Create( "Text", typeof( string ), typeof( ButtonView ), null, BindingMode.Default, null, null, null, null );
public string Text {
get {
return (string)GetValue( TextProperty );
}
set {
SetValue( TextProperty, value );
}
}
//image
public static readonly BindableProperty ImageProperty = BindableProperty.Create( "Image", typeof( string ), typeof( ButtonView ) );
public string Image {
get {
return (string)GetValue( ImageProperty );
}
set {
SetValue( ImageProperty, value );
}
}
This does not show any text or image. A rather annoying alternative is to name both Button and Label, override ButtonView.OnPropertyChanged, and explicitly set their properties from code but it causes a System.ObjectDisposedException (Object name: 'Android.Graphics.Bitmap') when I navigate around this page.
#yyou's solution works for const values, but not for parent's properties.
The only thing missing is to set the BindingContext of this custom layout.
ie.
//ButtonView.xaml.cs
public ButtonView ()
{
InitializeComponent ();
BindingContext = this;
}
//to use this ButtonView component in a page
//ie. DetailPage.xaml
<ContentPage ...>
<app2:ButtonView Text="{Binding ParentText}" Image="{Binding ButtonImage }"/>
</ContentPage>
//in DetailPage.xaml.cs
public DetailPage() {
InitializeComponent ();
BindingContext = new DetailViewModel() {
ParentText = "sjldfdkfdjd",
ButtonImage = "my-button-image.png"
};
}
//in the file DetailViewModel.cs
namespace App2.ViewModels
{
public class DetailViewModel : BaseViewModel {
private string _parentText;
public string ParentText {
get { return _parentText; }
set { SetProperty<string>(ref _parentText, value); }
}
private string _buttonImage;
public string ButtonImage {
get { return _buttonImage; }
set { SetProperty<string>(ref _buttonImage, value); }
}
//other stuff
}
}

BindableProperty getter returns null

Below is the BindableProperty definition on a custom control:
public static BindableProperty ItemsSourceProperty =
BindableProperty.Create<NodeListView, IEnumerable<Node>> (ctrl =>
ctrl.ItemsSource,defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanging: (bindable, oldValue, newValue) => {
var ctrl = (NodeListView)bindable;
ctrl.ItemsSource = newValue;
});
public IEnumerable<Node> ItemsSource {
get {
var itemsSource = (IEnumerable<Node>)GetValue (ItemsSourceProperty);
return itemsSource;
}
set {
SetValue (ItemsSourceProperty, value);
BindNodeIcons ();
}
}
When I set the BindingContext on the control I can see the newValue in propertyChanging is set to a correct object. Also in the setter of ItemsSource property the value variable takes the correct value. In the BindNodeIcons() method, I access ItemsSource property and it returns null. I can't see anything wrong in the code but still.
Try the following:
public class NodeListView : BindableObject
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable<Node>), typeof(NodeListView), propertyChanged: OnItemSourceChanged);
public IEnumerable<Node> ItemsSource
{
get { return (IEnumerable<Node>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
private static void OnItemSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var nodeListView = bindable as NodeListView;
nodeListView.BindNodeIcons();
}
}

Create a tag as a bindable property

Is there an alternative for a Tag Bindable Property?
A tag property is available in WPF. However, it does not exist in Xamarin.Forms.
I would like to create a bindable context between two elements.
Specifically, I would like to bind a label's Tag property to an entry's Text property.
I have attempted the following:
Tag Property
public class TagBehavior
{
public static readonly BindableProperty TagProperty = BindableProperty.CreateAttached<TagBehavior, BindableObject>(
bindableObject => TagBehavior.GetTag(bindableObject),
null, /* default value */
BindingMode.OneWay,
null,
(bo, old, #new) => TagBehavior.OnCompletedChanged(bo, old, #new),
null,
null);
public static BindableObject GetTag(BindableObject bindableObject)
{
return (BindableObject)bindableObject.GetValue(TagBehavior.TagProperty);
}
public static void SetTag(BindableObject bindingObject, BindableObject value)
{
bindingObject.SetValue(TagBehavior.TagProperty, value);
}
public static void OnCompletedChanged(BindableObject bindableObject, BindableObject oldValue, BindableObject newValue)
{
//var tag = TagBehavior.GetTag(entry);
//if (tag != null)
//{
// Debug.WriteLine("TODO - Handle tag's value change event");
//}
}
}
XAML
xmlns:Behaviors="clr-namespace:ViewMenu.Behaviors;assembly=ViewMenu"
. . .
<Entry x:Name="textBox1" BindingContext="{StaticResource ViewModel}" Text="{Binding Path=Content1}" Grid.Row="0" Grid.Column="0" >
<Entry.Behaviors>
<Behaviors:TextBoxFocusBehavior />
</Entry.Behaviors>
</Entry>
<Label x:Name="label1" Grid.Row="0" Grid.Column="0"
Behaviors:TagBehavior.Tag="{Binding Source={x:Reference textBox1}, Path=Text}">
<Label.Behaviors>
<Behaviors:LabelDisplayBehavior />
</Label.Behaviors>
</Label>
However, I get an error in the output window saying:
SetValue: Can not convert to type
'Xamarin.Forms.BindableObject'
Any suggestions?
You're creating an attached property of type BindableObject which you try to bind to the property Text of the TextBox which is of type string. Obviously, string cannot be casted to BindableObject hence the error.
Instead, declare your attached property of type string, or if you want to stay more general, of type object:
public class TagBehavior
{
public static readonly BindableProperty TagProperty = BindableProperty.CreateAttached<TagBehavior, string>(
bindableObject => TagBehavior.GetTag(bindableObject),
null, /* default value */
BindingMode.OneWay,
null,
(bo, old, #new) => TagBehavior.OnCompletedChanged(bo, old, #new),
null,
null);
public static string GetTag(BindableObject bindableObject)
{
return (string)bindableObject.GetValue(TagBehavior.TagProperty);
}
public static void SetTag(BindableObject bindingObject, string value)
{
bindingObject.SetValue(TagBehavior.TagProperty, value);
}
public static void OnCompletedChanged(BindableObject bindableObject, string oldValue, string newValue)
{
//var tag = TagBehavior.GetTag(entry);
//if (tag != null)
//{
// Debug.WriteLine("TODO - Handle tag's value change event");
//}
}
}

Resources