Quantcast
Channel: Xamarin.Forms — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 89864

ViewCell Databinding One-time only

$
0
0

Here is a post I've had up on Stack Overflow for a little while and I've got no response on it:

I have created a ViewCell that can be reused throughout my project:

 <?xml version="1.0" encoding="UTF-8"?>
 <ViewCell xmlns="http://xamarin.com/schemas/2014/forms" 
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
          xmlns:C="using:****.Converters"
          x:Class="****.Views.Settings.SettingCell">
 <Grid BackgroundColor="{StaticResource BlueGray}">
     <Grid.Resources>
         <C:DebugConverter x:Key="debugConverter"/>
     </Grid.Resources>
     <Grid.ColumnDefinitions>
         <ColumnDefinition Width="1*"/>
         <ColumnDefinition Width="2*"/>
     </Grid.ColumnDefinitions>
     <Image Grid.Column="0" Source="{Binding ImageSource}"/>
     <Label Grid.Column="1" 
            Text="{Binding Path=Title,
                           Converter={StaticResource debugConverter}, 
                           FallbackValue=Title}" 
            HorizontalOptions="Start" 
            VerticalOptions="Center"/>
     </Grid>
 </ViewCell>

Here is ViewModel:

public partial class SettingCell : ViewCell
{
    public SettingModel Model
    {
        get
        {
            return model;
        }
        set
        {
            model = value;
            BindingContext = value;
        }
    }
    public SettingModel model;

    public static readonly BindableProperty SettingTypeProperty = BindableProperty.Create(nameof(SettingType), typeof(Setting), typeof(SettingModel));
    public Setting SettingType
    {
        get
        {
            return (Setting)GetValue(SettingTypeProperty);
        }
        set
        {
            SetValue(SettingTypeProperty, value);
        }
    }

    public SettingCell()
    {
        InitializeComponent();

        switch (SettingType)
        {
            case Setting.Name:
                Model = new NameSettingModel();
                break;
            default:
                throw new NotImplementedException("Unknown Setting Type");
        }
    }
}

The Model is a subtype of an abstract class that is selected through a class discriminator. The class is very simple at this point since I'm just getting it wired up, but here is the base class along with on sub-type:

public class NameSettingModel : SettingModel
{
    public NameSettingModel()
    {
        Title = "Name";
        IconSource = "";
    }

    public override void ClickCommand()
    {
        Debug.WriteLine("Setting Command Run");
    }
}

public abstract class SettingModel : BindableBase
{
    public string Title
    {
        get
        {
            return GetProperty<string>();
        }
        set
        {
            SetProperty(value);
        }
    }
    public string IconSource
    {
        get
        {
            return GetProperty<string>();
        }
        set
        {
            SetProperty(value);
        }
    }

    public abstract void ClickCommand();
}

BindableBase is just a base binding class that implements INotifyPropertyChanged and stores property values in a dictionary so I don't have to make a field for each property.

When I run the project; the fallback value shows up each time. The thing that strikes me as odd is that when I put a break in my debugConverter which is as follows:

    public class DebugConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }

I see the value "Name" coming through the converter but the UI only ever shows the fallback value. Any ideas? Any reason that the binding would get the correct value but just not refresh to display it? I'm at a loss here. I'm happy to post any more code that could be helpful, just leave me a comment.

Thanks!

=========================== Edit =============================

I gave up on the code above and redid a lot of my work. So, I am no longer trying to get the Name property to databind. Now I am trying to databaind the IsEnabled property on a ViewCell to a property "IsConnected" in the abstract base class. Please find the new pertinent code below:

I now have a viewcell that is directly in my listview and the template I have inserted is just a Grid.

    <?xml version="1.0" encoding="UTF-8"?>
    <Grid xmlns="http://xamarin.com/schemas/2014/forms" 
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
      xmlns:Images="clr-namespace:MyApp.Images;assembly=MyApp"        
      xmlns:Converters="clr-namespace:MyApp.Views.Converters"
      x:Class="MyApp.Views.Templates.SettingTemplate" 
      BackgroundColor="White" VerticalOptions="Center">
    <Grid.Resources>
         <Converters:SettingToIconConverter x:Key="SettingToIconConverter"/>
         <Converters:InverseBoolConverter x:Key="InverseBoolConverter"/>
    </Grid.Resources>
    <StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="Center">
        <Images:VectorImage ResourceId="{Binding Converter={StaticResource SettingToIconConverter}}"
                            WidthRequest="30" HeightRequest="30" Margin="2"  VerticalOptions="Center"/>
        <Label Text="{Binding Name, FallbackValue=Name}" FontAttributes="Bold" FontSize="Medium" Margin="2" 
               VerticalOptions="Center"/>
        <ContentPresenter Content="{Binding BonusContent}" VerticalOptions="Center" Margin="2"/>
    </StackLayout>
    <Frame BackgroundColor="{StaticResource Slate}" Opacity="0.25" IsVisible="{Binding IsEnabled, UpdateSourceEventName=ValueChanged, Converter={StaticResource InverseBoolConverter}, FallbackValue=false}"/>

Here's the containing listpage that has two templates in it:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="75"/>
        <RowDefinition Height="1"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Templates:PeripheralDetailsTemplate/>
    <Frame BackgroundColor="{StaticResource Slate}" Grid.Row="1"/>
    <Frame BackgroundColor="White" Grid.Row="2">
        <ListView x:Name="lvPeripheralSettings" ItemsSource="{Binding Settings}" 
                  SelectionMode="None" ItemTapped="Setting_Tapped" RowHeight="40"
                  ios:ListView.SeparatorStyle="FullWidth">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell IsEnabled="{Binding IsEnabled, UpdateSourceEventName=ValueChanged, Mode=TwoWay, Converter={StaticResource DebugConverter}}">
                        <Templates:SettingTemplate/>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Frame>
</Grid>

Take note of the PeripheralDetailsTemplate:

       <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="75"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="75"/>
            </Grid.ColumnDefinitions>
            <!--Icon--> 
            <Frame Padding="5">
                 <Images:VectorImage ResourceId="{Binding HardwareType, Converter={StaticResource hardwareTypeToSVGPathConverter}}"
                                     WidthRequest="150" HeightRequest="150" 
                                     HorizontalOptions="Center" VerticalOptions="Center"/>
            </Frame>
            <Grid Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                </Grid.RowDefinitions>
                <Frame>
                    <Label Text="{Binding Name, FallbackValue=Peripheral Name}" FontSize="Medium" FontAttributes="Bold" HorizontalOptions="Start" VerticalOptions="Center"/>
                </Frame>
                <Frame Grid.Row="1">
                    <Label Text="{Binding HardwareType, FallbackValue=Peripheral Type}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
                </Frame>
                <Frame Grid.Row="2">
                    <Label Text="{Binding MacAddress, StringFormat='Mac: {0}', FallbackValue=000111222333}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
                </Frame>
                <Frame Grid.Row="3">
                    <Label Text="{Binding FirmwareRevision, StringFormat='Firmware: {0}', FallbackValue=01AB}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
                </Frame>
            </Grid>
            <Frame Grid.Column="2">
                <Label Text="Conn" HorizontalOptions="Center" VerticalOptions="Center" TextColor="{Binding IsConnected, Converter={StaticResource BoolToColorConverter}, FallbackValue={StaticResource LynkdBlue}}"/>
            </Frame>
        </Grid>

It is bound to EXACTLY THE SAME PROPERTY AS THE ViewCell!!! and it works fine. I'm looking for the difference and haven't found it yet. I just know that I have properly set the bindingcontext because it databinds once and I know I'm properly firing NotifyPropertyChanged because the other template which is bound to the same property updates as I would expect. I have attached a debugconverter and it is never run. I think it's interesting that in both situations I was working with a ViewCell. These are two separate code bases with the same issue. :/


Viewing all articles
Browse latest Browse all 89864

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>