Forum Xamarin.Forms

Bindable property not being updated

BenjaminTooleBenjaminToole USMember ✭✭✭
edited January 2017 in Xamarin.Forms

I have a custom control that extends from content view:

<ContentView Opacity=".8" BackgroundColor="#fafafa" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TamarianApp.EmptyLabel">
    <StackLayout>
        <BoxView HorizontalOptions="FillAndExpand" HeightRequest="3" BackgroundColor="#f1f1f1" VerticalOptions="Start"/>
        <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
            <Label HorizontalOptions="Center" VerticalOptions="Center" TextColor="Gray" Text="{Binding Text}"/>
        </StackLayout>
    </StackLayout>
</ContentView>

Backend:

public partial class EmptyLabel : ContentView
    {
        public static BindableProperty TextProperty = BindableProperty.Create(
            "Text",
            typeof(string),
            typeof(EmptyLabel),
            defaultBindingMode: BindingMode.TwoWay,
            defaultValue: "",
            propertyChanged: TextPropertyChanged
        );


        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        public EmptyLabel()
        {
            InitializeComponent();

            BindingContext = this;
        }

        private static void TextPropertyChanged(BindableObject bindable, object oldVal, object newVal)
        {
            ((EmptyLabel)bindable).Text = newVal.ToString();
        }
    }

I want to control the visiblity of the EmptyLabel control from the view model by binding a property to it :

    ``<local:EmptyLabel IsVisible="{Binding HasNoImages}" Text="No Images"></local:EmptyLabel>``

For instance If I changed IsVisible="{Binding HasNoImages}" toIsVisible="false" it works.

Everything else on the page binds to there controls ( theres a lot more code ), but when binding to the custom control from the binding context, it ignores it. What is happening here?

Best Answer

Answers

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    The BindingContext of your EmptyLabel control is to whatever View Model object is created.

    Thus, your Label control inside of it is bound to the Text Property of the View Model object, when you want it to be bound to the EmptyLabel's property Text.

    You can change the BindingContext of the Label, or the Source of the binding to achieve your goal.

  • BenjaminTooleBenjaminToole USMember ✭✭✭

    The label is not the problem though, If you notice its a contentview with a stack layout and label contained within it. I'm trying to make the entire contentview dissapear.

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    Can we see your View Model object then?

  • BenjaminTooleBenjaminToole USMember ✭✭✭
    public class PhotoModelView : ViewModel
        {
    
            private bool hasImages { get; set; } = false; 
    
            private bool hasNoImages { get; set; } = true;
    
    
            public bool HasImages
            {
                get
                {
                    return hasImages;
                }
                set
                {
                    if (hasImages != value)
                    {
                        hasImages = value;
                        HasNoImages = !value;
                        OnPropertyChanged();
                    }
                }
            }
    
            public bool HasNoImages
            {
                get
                {
                    return hasNoImages;
                }
                set
                {
                    if (hasNoImages != value)
                    {
    
                        hasNoImages = value;
                        OnPropertyChanged();
                    }
                }
            }
        }
    }
    
  • BenjaminTooleBenjaminToole USMember ✭✭✭
        public abstract class ViewModel : INotifyPropertyChanged
        {
            public Page CurrentPage { get; set; } 
    
            public event PropertyChangedEventHandler PropertyChanged;
    
    
            public void OnPropertyChanged([CallerMemberName] string name="")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
            }
    
            public void NotifyPropertyChanged()
            {
    
            }
        }
    
  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    And an instance of the PhotoModelView is set to the BindingContext of the EmptyLabel?

    Do you hit the getter in a breakpoint?

  • BenjaminTooleBenjaminToole USMember ✭✭✭

    So I followed the instructions and still not working.

    The breakpoint for the backend of the control never gets hit when the BindableProperty is bound to another BindableProperty. Actually the breakpoint for the OnPropertyChanged function never gets hit at all:

    <local:MyCustomControl IsVisible={Binding someval}/>

    but does when there is not a BindableProperty. The OnPropertyChanged function gets hit here:

    <local:MyCustomControl IsVisible="false"/>

    Why is that happening?

  • CarlosAyalaCarlosAyala Member ✭✭
    edited January 2018

    Hi!,

    I have the same problem,

    Not fires the "propertyChanged", but if change this to:

    Fires normally.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @CarlosAyala said:
    Hi!,

    I have the same problem,

    <Helpers:NoResults x:Name="NoResults" Count="{Binding UsersListQuantity }"></Helpers:NoResults>
    

    Not fires the "propertyChanged", but if change this to:

    <Helpers:NoResults x:Name="NoResults" Count="100"></Helpers:NoResults>
    

    Dude you have to use proper markdown for your XAML if you don't want the site parser to eat it.
    https://redpillxamarin.com/2016/12/13/faq-frequently-asked-questions/

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @CarlosAyala said:

    but if change this to:

    <Helpers:NoResults x:Name="NoResults" Count="100"></Helpers:NoResults>
    

    Fires normally.

    How can that fire anything? - there is no binding in that markup.

    99% of binding issues turn out to be the same thing: Your binding context at runtime does not evaluate to what you think it is. After that its a toss up. I'm going to guess that UsersListQuantity is not on the object you are using as context. Or it is a plain CLR property and not a BindableProperty.

  • CarlosAyalaCarlosAyala Member ✭✭
    edited January 2018

    Yup,

    The ContentView have the BindableProperty.

    ``` public static readonly BindableProperty CountProperty = BindableProperty.Create(

            propertyName: "Count",
            returnType: typeof(int),
            declaringType: typeof(NoResults),
            defaultValue: 0,
            defaultBindingMode: BindingMode.OneWay,
            validateValue: (bindable, value) =>
            {
                return true;
            },
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                ((NoResultsViewModel)bindable.BindingContext).CheckCount(Int32.Parse(newValue.ToString()));
            }
    

    );
    ```

    The propertyChanged only fires when you wrote Count="100" and not when you wrote Count="{Binding UsersListQuantity }"

    The UsersListQuantity is in ViewModel, is public and bindable.

    public int UsersListQuantity { get { return _userListQuantity; } set { SetProperty(ref _userListQuantity, value); } }

    If I wrote this,

    <Helpers:NoResults x:Name="NoResults" Count="{Binding UsersListQuantity }"></Helpers:NoResults> <Label Text="{Binding UsersListQuantity}"/>

    The Label show the UserListQuantity value with no problems, but the Count="{Binding UsersListQuantity }" not work.

  • CarlosAyalaCarlosAyala Member ✭✭

    @BenjaminToole said:
    So I followed the instructions and still not working.

    The breakpoint for the backend of the control never gets hit when the BindableProperty is bound to another BindableProperty. Actually the breakpoint for the OnPropertyChanged function never gets hit at all:

    <local:MyCustomControl IsVisible={Binding someval}/>

    but does when there is not a BindableProperty. The OnPropertyChanged function gets hit here:

    <local:MyCustomControl IsVisible="false"/>

    Why is that happening?

    Did you find a solution?

  • aichamaghrebiaichamaghrebi Member ✭✭

    Hi !!Me too I have this problem now !! can any give us some ideas of how to resolve it !! thank you :)

  • @BenjaminToole did you ever get a working solution for the problem? Thanks!

  • cris_mcris_m USMember ✭✭

    this problem persist. It work for other value except boolean values

  • cris_mcris_m USMember ✭✭
    edited January 10

    After our of research I found the solution. If in your custom control c# class the binding context is like this BindingContext = this It won't work if you are binding the view that call the customer control to another view model or itself. The solution is to specify the source so that your customer control can get the proper value for the property.

    customer control before the fix

     <controls:CheckBoxControl IsChecked="check}"/>
    

    IsChecked doesnot get the value since the customer control doesnot know which class is bind to.

    customer control after the fix

     <controls:CheckBoxControl IsChecked="{Binding Source={x:Reference MyDay}, Path=BindingContext.check}"/>
    

    IsChecked get the value since I rename my Content Page x:Name="MyDay" the on my customer control I specied the source to my Content Page and the Path to it binding context and the value which I am binding to. The same technique works for command.
    For more explanation please read on this blog post rosengren.me/blog/xamarin-usercontrol-bindingcontext/

  • xamarishxamarish Member
    edited April 10

    @cris_m thanks for the idea, I was setting the BindingContext of my custom ContentView with the view model, but I solved it by instead setting the BindingContext of the main StackLayout inside the custom control and that way I can do "regular" bindings when using the custom control.

Sign In or Register to comment.