BindableProperty trouble (I know people ask about these a lot; I've tried many things already...)

BenAtWorkBenAtWork USMember ✭✭
edited October 2017 in Xamarin.Forms

I'm trying to make a reusable donut chart XAML component that I can drop in wherever, ex:

    <ListView x:Name="TopStocks">
                                <ColumnDefinition Width="1*" />
                                <ColumnDefinition Width="1*" />
                                <ColumnDefinition Width="1*" />
                            <Label Grid.Column="0" Text="{Binding symbol}" />

and here, finally, is the "call" to the custom component (HTML-style comments broke my post layout!)

                            <local:StockScore Grid.Column="1" Score="{Binding score}" Text="Hi" Radius="25" />

                            <Label Grid.Column="2" Text="{Binding score}" /> <!-- P.S. "score" comes in here looking fine -->

where StockScore is a XAML/CS element I've created. however, I cannot for the life of me get Score to bind; the application keeps crashing. if I used a hard-coded value (say, Score="80"), that works (which I take as evidence that my xmlns:local stuff is set up properly...); the binding, however, refuses to work. by setting up HockeyApp, I was able to learn that the exception is:

android.runtime.JavaProxyThrowable: Xamarin.Forms.Xaml.XamlParseException: Position 20:67. Cannot assign property "Score": Property does not exists, or is not assignable, or mismatching type between value and property

which set me on the path of finding out how to set up bindable properties... however, it's still not working!

(P.S. if there's a way to see these more-useful exceptions without using an online service, that'd be great to know, too...)

the code I have for Score's BindableProperty is...

public partial class StockScore : StackLayout
    public static readonly BindableProperty ScoreProperty = BindableProperty.Create("Score", typeof(float), typeof(StockScore), 0);

    public float Score
        get { return (float)GetValue(ScoreProperty); }
        set { SetValue(ScoreProperty, value); }

other things I've tried:

  • using a "normal" property get/set for Score. this is what I had before setting up the BindableProperty stuff. this also didn't work; from what I've read online, no one expects it to
  • changing Score to be of type "string", and using get { return GetValue(ScoreProperty).ToString(); } for the Score getter. my thinking here was "I'm super-sure that {Binding score} is a float (it shows up in that Label just fine...), but maybe it's not; maybe it's somehow coming in as something else..." if true, using a string and casting to string would have solved that problem, if it were a problem, but it didn't solve anything, so I assume that's not the problem :P
  • updating various Xamarin NuGet packages. unfortunately, a separate issue prevents me from updating several packages from 23.3.0 to 26.0.2 ("Could not install package 'Xamarin.Android.Support.Animated.Vector.Drawable 26.0.2'. You are trying to install this package into a project that targets 'MonoAndroid,Version=v7.1', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author."). I'm not sure that this is relevant - it seems unlikely? - but it is an oddity, and I can't help but worry "what if these two problems ARE related??"
  • running on both emulated Android, and a for-realsies Android, over USB. not sure why this would help, but I'm at my wit's end...
  • running in Release, rather than Debug... seems even LESS likely, but wits are rapidly decreasing...
  • I read somewhere that using [assembly: XamlCompilation(XamlCompilationOptions.Compile)] might yield some insightful compiler errors, so I threw that into my AssemblyInfo.cs, but that hasn't seemed to have changed anything

if seeing other bits of my code would be helpful, let me know; I can post more, though I'd hesitate to, like, upload the entire project, or anything...


  • AdamMeaneyAdamMeaney USMember ✭✭✭✭

    A good way to see what is going on is to add this to your code:

        public static readonly BindableProperty ScoreProperty = BindableProperty.Create("Score", typeof(float), typeof(StockScore), 0, propertyChanged: OnScoreChanged);
        private static void OnScoreChanged(BindableObject bindable, object oldvalue, object newvalue)
            // Check value of newvalue here.
        public float Score
            get { return (float)GetValue(ScoreProperty); }
            set { SetValue(ScoreProperty, value); }

    Where that comment is, you can see if you get the value to your ScoreProperty from the binding correctly.

    If it does appear there, I would say the issue is in something else in your StockScore class, which you may need to post more of.

  • BenAtWorkBenAtWork USMember ✭✭

    oh! did not expect a reply so quickly! thanks :)

    I added the method method you suggested (and the propertyChanged var in the bindable), and put in a breakpoint, so I could inspect the values. however: it apparently crashes before reaching this point :|

  • BenAtWorkBenAtWork USMember ✭✭

    oh! so I checked the exceptions on HockeyApp again (this is hard to do, since the app crashes on startup, so it never gets to send the reports, unless I make a ton of changes to remove all the bindable stuff so that it DOESN'T crash on startup... so I mostly wasn't bothering)... BUT: it looks like the exception is different now: "android.runtime.JavaProxyThrowable: System.TypeInitializationException: The type initializer for 'MobileTestApp.StockScore' threw an exception. ---> System.ArgumentException: Default value did not match return type".

    apparently "0" isn't good enough as a default value for floats: "0f" is needed. changing that, everything now works >_>

    sorry for the trouble, although if you don't mind a totally-separate question: do you know a better way to get exception details than to rely on something like HockeyApp?

  • ChaseFlorellChaseFlorell CAInsider, University mod

    yeah, I've run into that issue too. Because the default value is "object", you need to specify the type explicitly. That's a tough one to find, and would be nice if there was better error handling around it.

  • BenAtWorkBenAtWork USMember ✭✭

    right. I can imagine the comparison they're making under the hood ("you said it's typeof(float), but the typeof the default value you provided is an int!?! THOSE AREN'T THE SAME!" :P). being required - as developers - to do all this bindable stuff in the first place is a little weird... but that's a separate issue >_>

Sign In or Register to comment.