Bindableproperty gets set but doesn't update view in custom control

ChrisVardonChrisVardon GBMember ✭✭

I have a custom control with an activityIndicator using in a view, which I need to show and hide from the view model. I have a bindableproperty which is setting the visibility to true which shows the indicator, however after setting the visibility to true in a method in the viewmodel the indicator doesn't hide even though the bindable property is set to false. Any ideas?

CONTROL XAML
<ActivityIndicator x:Name="testnam" Color="White" IsRunning="true" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="{Binding PhoneContactsLoading, Mode=TwoWay}"/>

CONTROL CS FILE
`public static readonly BindableProperty PhoneContactsLoadingProperty = BindableProperty.Create("PhoneContactsLoading", typeof(bool), typeof(PhoneContactPickerControl), false);

    public bool PhoneContactsLoading
    {
        get
        {
            return (bool)GetValue(PhoneContactsLoadingProperty);
        }
        set
        {
            SetValue(PhoneContactsLoadingProperty, value);
        }
    }`

VIEW USING CONTROL
<local:PhoneContactPickerControl x:Name="grdPhoneContactsPicker" AbsoluteLayout.LayoutFlags="None" CancelClicked="Handle_CancelClicked" lstPhoneContacts="{Binding PhoneContacts}" ContactSearchText="{Binding ContactsSearchText, Mode=TwoWay}" PhoneContactsLoading="{Binding IsContactsLoading, Mode=TwoWay}" SelectedItem="{Binding SelectedContact, Mode=TwoWay}" ContactSelected="Handle_ContactSelected"/>

VIEWMODEL CODE

`private bool isContactsLoading;

public bool IsContactsLoading
{
get
{
return isContactsLoading;
}
set
{
isContactsLoading = value;
OnPropertyChanged("IsContactsLoading");
}
}`

public async void LoadPhoneContacts() { IsContactsLoading = true; LoadContacts(); IsContactsLoading = false; }

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    99% of the time this question always boils down to the same thing: BindinContext doesn't evaluate down to what the develoepr thinks it is. Either it was inherited from the parent and we forget that, or something similar.

    One of the lessons on http://www.RedPillXamarin.com is for BindableProperties on Custom Controls. Maybe it can help.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    owever after setting the visibility to true in a method in the viewmodel the indicator doesn't hide even though the bindable property is set to false.

    Also... Don't work with both IsRunning and IsVisisble. After a lot of hand-on playing I've found these must be acting on the same state deep within the ActvityIndicator and working both properties just screws it up.

    Based on hands-on I'd say the ActivityIndicator is always running. When you set IsRunning to false all that really happens is that it gets hidden. After all, what would a non-running activity indicator look like? Nothing. Which is what you get when it is hidden.

    Personally I'd say just work with IsRunning. Ignore IsVisible. Because when its not running there shouldn't be any visible aspects to it anyway.

  • ChrisVardonChrisVardon GBMember ✭✭

    Thanks for the help @ClintStLaurent , but I'm still no closer to solving this. Other bindings in the control are working except the activityIndicator which is weird. I am currently setting BindingContext="{x:Reference Name=userControl}" for a searchbar but this doesn't help my situation unfortunately. No idea what to do next

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Other bindings in the control are working except the activityIndicator which is weird.

    Then its not the binding. Its the ActivityIndicator.

    Its a weird, freaky little control that has spawned many threads here. It has to be called on its own thread... it has to be marshaled back to the UI thread/View... Its just plain twitchy. We have one in our page 'header', and then have a 'BusyPopup' for things like logging in where we want the popup to block the UI until some process is done. In both cases I had to just fight it and d!ck around with the threading and pushing it to the UI thread until it worked. **Then don't touch it again. ** Its a special little child that wears a helmet but doesn't play on a team; if you get my drift.

    I wrote our own custom NavigationVM rather than use the built in Navigation, and all the IsBusy, ActivityIndicator and popup handlers are in it. Then any other feature VM just calls an inherited "PushPopup" method (etc.). invoking the code on that navigation VM - rather than making every VM responsible for knowing how to get it all straight.

    Once you have it on a Page or View and have it working from a single property you want to continue to funnel everything through that one property. Don't get fancy with having 10 different VM's trying to call it. Have them all go back to that one property.

  • NMackayNMackay GBInsider, University mod
    edited May 2017

    @ChrisVardon

    You don't need two way binding on an activity indicator, the control doesn't allow user input.

    @ClintStLaurent

    Interesting what you say about setting both, a while back on or the other was unreliable so by (maybe bad habit) I always set both but I'd never given me any problems. I'll revisit it though as this was back in Forms 1.4.x I was having problems I think.

                <ActivityIndicator IsRunning="{Binding Busy}"
                                       IsVisible="{Binding Busy}"
                                       VerticalOptions="CenterAndExpand"
                                       HorizontalOptions="CenterAndExpand">
                        <ActivityIndicator.WidthRequest>
                            <OnPlatform x:TypeArguments="x:Double">
                                <On Platform="iOS,Android">100</On>
                                <On Platform="Windows">400</On>
                            </OnPlatform>
                        </ActivityIndicator.WidthRequest>
                        <ActivityIndicator.Color>
                            <OnPlatform x:TypeArguments="Color">
                                <On Platform="iOS,Windows">#2499CE</On>
                            </OnPlatform>
                        </ActivityIndicator.Color>
                    </ActivityIndicator>
    
    
  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    I didn't even notice the two way binding. That could very much be part of the problem. Sure you can set both. But in the two-ways situation what happens?

    Set IsVisible to true, while IsRunning is still false... That updates makes a circular path through the ViewModel, back the ActivityControl, causing a change in state that then changes the _other_property... that circles around... {repeat}

    You may have hit the nail on the head there. @NMackay

  • tuyenvtuyenv VNUniversity ✭✭✭

    @ChrisVardon,

    As I experienced so far, you should please use Nullable type for value type then you will get your bindable property having changed notifications.

    Cheers.

  • BehroozTorkiBehroozTorki HUMember ✭✭
    edited December 2018

    @ChrisVardon

    To resolve the issue you have to notify Xaml that the property has changed.

    When "PhoneContactsLoading" value changes. it will update "PhoneContactsLoadingProperty" through a binding.
    *** BUT : Xaml does not know when this change has happen so you have to notify it to call the getter on "PhoneContactsLoading"

    use the following code for your "PhoneContactsLoading"

    public static readonly BindableProperty PhoneContactsLoadingProperty = 
            BindableProperty.Create("PhoneContactsLoading", typeof(bool), typeof(PhoneContactPickerControl), false, 
                propertyChanged: (BindableObject bindable, object oldValue, object newValue) => {
                    (bindable as PhoneContactPickerControl).OnPropertyChanged("PhoneContactsLoading"); 
                    } );
    

    the important part is to set "sourceChanged" and in that call "OnPropertyChanged" on the property you want xaml to update

Sign In or Register to comment.