Mark Wardell
Mark Wardell

Reputation: 555

Xamarin MultiBinding Needs BindingContext from higher ancestor level or named Items in XAML tree

I am Building a rectangular Grid of Homogeneous Buttons with Dynamic Rows and Columns to Be set at Runtime. The Code is in Xamarin. All the latest versions etc. Entire Project can be Downloaded here: https://github.com/BicycleMark/XamSweeper The Problem I am having is the MultiBinding Portion of the XAML. Any review or suggestions would be super-excellent

I used Nested StackPanels to create the Grid like this:

enter image description here

I succeeded at creating a MultiValueConverter for sizing the Buttons which are appearing correctly. I have Tested the Converter with a Unit Test and it functions as Expected. the [R,C] is printed in the Test of each Button. These are pulled from Model which is bound Correctly

The problem I am having is in Linking the MultiBinding to 3 Items I need to calculate Size for the Buttons:

The Width of Each Button is calculated with these three parameters

  1. ActualWidth of Frame Holding the Buttons
  2. Number of Columns Of Buttons Of Model
  3. Spacing or Padding between each Button
  4. If any [1..3] are null return 50 <--- This is What I return because my Binding is not working

The Height of Each Button is calculated with these three parameters

  1. ActualHeight of Frame Holding the Buttons
  2. Number of Rows Of Buttons Of Model
  3. Spacing or Padding between each Button
  4. If any [1..3] are null return 50 <--- This is What I return because my Binding is not working

Here is the Converter Described:

    public class SizeConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null || values.Length <3 || values[2] == null || values[1] == null || values[0] == null)
            return 50;
        int numItems = (int)values[2];
        int separatorSize = (int)values[1]; 
        double frameSize = System.Convert.ToDouble(values[0]) ;
        
        int totalSeparatorSize = (numItems - 1) * separatorSize;
        int remainingArea = System.Convert.ToInt32(frameSize) - totalSeparatorSize;
        return remainingArea / numItems;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And Its Unit Test Which Passes:

 [DataRow(100, 2, 4)]
    [DataTestMethod]
    public void Test_Piece_Sizing(double frameSize, int separatorSize, int numItems)
    {
        var sizeConverter = new SizeConverter();
        object[] values = new object[] { frameSize, separatorSize, numItems };
        Assert.AreNotEqual(0,sizeConverter.Convert(values, typeof(int), null, System.Globalization.CultureInfo.CurrentCulture));
    }

The issue at hand is when I bind My Values in Xamarin My Converter gets Null passed from my XAML Here is the XAML

    <?xml version="1.0" encoding="utf-8" ?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:prism="http://prismlibrary.com"
    prism:ViewModelLocator.AutowireViewModel="True"
    xmlns:local="clr-namespace:Sweeper.Controls;assembly=Sweeper"
    xmlns:cvt="clr-namespace:Sweeper.Views.Converters;assembly=Sweeper"
    x:Class="Sweeper.Views.GamePage"    
    Title="Game"
    x:Name="page">
    <ContentPage.Resources>
        <cvt:SizeConverter  x:Key="SizeConverter" />
        <cvt:CoordinateConverter x:Key="CoordinateConverter" />
    </ContentPage.Resources>
    <Frame BackgroundColor="DarkSeaGreen" CornerRadius="15" Padding="15">
        <Grid RowDefinitions="*,3*">
            <Frame Grid.Row="0" Padding="0">
                <Grid ColumnDefinitions="*,*,*" Padding="0" >
                    <Frame Grid.Column="0">
                        <Label HorizontalOptions="Center" VerticalOptions="Center">
                            <Label.Text>
                                <MultiBinding Converter="{StaticResource CoordinateConverter}">
                                    <Binding Path="Board.Rows" />
                                    <Binding Path="Board.Columns" />
                                </MultiBinding>
                            </Label.Text>
                        </Label>
                    </Frame>
                    <Frame Grid.Column="1">
                        <Button Text="Ok"></Button>
                    </Frame>
                    <Frame Grid.Column="2">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" Text="{Binding Game.Time}"/>
                    </Frame>
                </Grid>
            </Frame>
            <Frame Grid.Row="1" x:Name="frameButtons" Padding="4" HasShadow="True" >
                <StackLayout HorizontalOptions="FillAndExpand" Orientation="Vertical" Spacing="4" 
                             BindableLayout.ItemsSource="{Binding Board.RowItems}">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <StackLayout BindableLayout.ItemsSource="{Binding}" Orientation="Horizontal" HorizontalOptions="Center">
                                <BindableLayout.ItemTemplate>
                                    <DataTemplate>
                                        <Frame Padding="0"  HasShadow="False" BackgroundColor="LightBlue" >
                                            <Button  Text="{Binding Name}" FontSize="Body" HorizontalOptions="Center" TextColor="Navy"/>
                                            <Frame.WidthRequest>
                                                <MultiBinding Converter="{StaticResource SizeConverter}" >
                                                    <MultiBinding.Bindings>
                                                        <Binding Source="x:Reference frameButtons" Path="ActualWidth" />
                                                        <Binding Source="x:Reference frameButtons" Path="Padding" />
                                                        <Binding Source="x:Reference frameButtons" Path="BindingContext.Board.Columns" />
                                                    </MultiBinding.Bindings>
                                                </MultiBinding>
                                            </Frame.WidthRequest>
                                            <Frame.HeightRequest>
                                                <MultiBinding Converter="{StaticResource SizeConverter}" >
                                                    <MultiBinding.Bindings>
                                                        <Binding Source="x:Reference frameButtons" Path="ActualHeight"/>
                                                        <Binding Source="x:Reference frameButtons" Path="Padding" />
                                                        <Binding Source="x:Reference frameButtons" Path="BindingContext.Board.Rows" />
                                                    </MultiBinding.Bindings>
                                                </MultiBinding>
                                            </Frame.HeightRequest>
                                        </Frame >
                                    </DataTemplate>
                                </BindableLayout.ItemTemplate>
                            </StackLayout>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </StackLayout>
            </Frame>
        </Grid>
    </Frame>
</ContentPage>

The Converter is Getting Called but I am passing 3 nulls each time. This is the Binding Error I am trying to repair

Entire Project can be Downloaded here: https://github.com/BicycleMark/XamSweeper

Upvotes: 0

Views: 1408

Answers (1)

Leo Zhu
Leo Zhu

Reputation: 14956

The Converter is Getting Called but I am passing 3 nulls each time.

You could try to set the BindingContext to the Frame like:

<Frame Padding="0"  HasShadow="False" BackgroundColor="LightBlue" BindingContext="{x:Reference frameButtons}">
   <Button  Text="{Binding Name}" FontSize="Body" HorizontalOptions="Center" TextColor="Navy" Clicked="Button_Clicked"/>
   <Frame.WidthRequest>
      <MultiBinding Converter="{StaticResource SizeConverter}" >

         <MultiBinding.Bindings>
           <Binding Path="ActualWidth" />
           <Binding Path="Padding" />
           <Binding Path="BindingContext.Board.Columns" />
         </MultiBinding.Bindings>
      </MultiBinding>
   </Frame.WidthRequest>
   <Frame.HeightRequest>
      <MultiBinding Converter="{StaticResource SizeConverter}" >
         <MultiBinding.Bindings>
           <Binding Path="ActualHeight" />
           <Binding Path="Padding" />
           <Binding Path="BindingContext.Board.Rows" />
         </MultiBinding.Bindings>
      </MultiBinding>
    </Frame.HeightRequest>
 </Frame >

then you could get the value in your Converter.

But ActualWidth seems not be an existing bindable property.

Upvotes: 1

Related Questions