0
votes

I’m trying to make a custom drop-down in xamarin forms with some bindable properties. I’m having a label and a list view below the label using relative layout so that listview will always below the label and its IsVisible property will be toggled.

I have created a custom view as below:

Dropdown.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
         x:Class="CustomViewXam.CustomViews.Dropdown">

  <StackLayout>
    <RelativeLayout>

        <Label x:Name="selectedLabel" TextColor="Red" Text="xcx"
                 BackgroundColor="Silver" FontSize="15" 
                 HeightRequest="50"
                 RelativeLayout.WidthConstraint =
                 "{ConstraintExpression
                 Type=RelativeToParent,
                 Property=Width,
                 Factor=0.5,
                 Constant=0}"/>

        <ListView x:Name="listView" BackgroundColor="Black" 

                           RelativeLayout.WidthConstraint =
                 "{ConstraintExpression
                 Type=RelativeToView,
                 ElementName=selectedLabel,
                 Property=Width,
                 Factor=1,
                 Constant=0}"

                           RelativeLayout.YConstraint=
                 "{ConstraintExpression
                 Type=RelativeToView,
                 ElementName=selectedLabel,
                 Property=Height,
                 Factor=1,
                 Constant=0}"
                 >
        </ListView>            
     </RelativeLayout>
   </StackLayout>
 </ContentView>

Dropdown.xaml.cs

 public partial class Dropdown : ContentView
{
    public Dropdown()
    {
        InitializeComponent();
        BindingContext = this;
    }

    public string TitleText
    {
        get { return base.GetValue(TitleTextProperty).ToString(); }
        set { base.SetValue(TitleTextProperty, value); }
    }

    private static BindableProperty TitleTextProperty = BindableProperty.Create(
        propertyName: "TitleText",
        returnType: typeof(string),
        declaringType: typeof(string),
        defaultValue: "",
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanged: TitleTextPropertyChanged);

    private static void TitleTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = (Dropdown)bindable;
        control.selectedLabel.Text = newValue.ToString();
    }

    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create<Dropdown, IEnumerable<object>>(p => p.ItemsSource,
                                                               null, BindingMode.OneWay, null, (bindable, oldValue, newValue) => { ((Dropdown)bindable).LoadItems(newValue); });


    public IEnumerable<object> ItemsSource
    {
        get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }


    public void LoadItems(IEnumerable<object> tiles)
    {
        try
        {
            var list = tiles;
        }
        catch (Exception e)
        { // can throw exceptions if binding upon disposal
        }

    }
}

And I'm using this custom view as below in my xaml page

  <local:Dropdown TitleText="dssdasd"  ItemsSource="{Binding TitleList}" />

Where TitleList is an ObservableCollection in ViewModel

private ObservableCollection<string> _titleList;  
    public ObservableCollection<string> TitleList
    {  
        get  
        {  
            return _titleList;  
        }  
        set  
        {  
            if (_titleList!= value)  
            {  
                _titleList= value;  
                NotifyPropertyChanged("TitleList");  
            }  
        }  
    }  

Problem:

The Text is visible on the UI and text is coming properly, but the listview below is empty and data is not coming. LoadItems method in Custom dropdown is not getting called even TitleList list is updated. Can anyone please guide me whats wrong i'm doing in the above code.

Note: I'm having BindingContext set to my view model in my view.

1
You should make a custom renderer of Picker insteadFabriBertani

1 Answers

1
votes

Note: I'm having BindingContext set to my view model in my view.

Do you mean you use the code in your Dropdown's construction method to set it?

public Dropdown()
{
    InitializeComponent();
    // Try to remove the code below
    BindingContext = this;
}

I recommend you to set the BindingContext in your page xaml and remove this code in the view:

public MainPage()
{
    InitializeComponent();

    MyViewModel viewModel = new MyViewModel();
    BindingContext = viewModel;
}

In this way the LoadItems() will fire at the first time we initialize the Dropdown's ItemsSource. But when you want to update the list, LoadItems() will not be called. because the instance of the list hasn't been changed. You can try to modify the method like:

public void LoadItems(IEnumerable<object> tiles)
{
    try
    {
        var list = tiles;
        listView.ItemsSource = tiles;
    }
    catch (Exception e)
    { // can throw exceptions if binding upon disposal
    }

}

Moreover if you want to see the listView's data on the view, you should set its ItemTemplate for instance:

<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell>
            <Grid>
                <Label Text="{Binding}"/>
            </Grid>
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>