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

Customised StackLayout variant not binding

$
0
0

I hope someone can help me here. I'm completely new to app development and Xamarin, but I've been working my way through some bits and pieces over the last few weeks. I have an issue with my data binding I hope you can help with. I've created a custom variant of a Stack Layout to provide list items that looks like this...

    public class StackedList : StackLayout
    {
        public static readonly BindableProperty ModelProperty =
            BindableProperty.Create<StackedList, StackedListViewModel> (p => p.Model, StackedListViewModel.Empty);

        public StackedListViewModel Model 
        {
            get { return (StackedListViewModel)GetValue (ModelProperty); }
            set 
            { 
                SetValue (ModelProperty, value); 
                BindChildren ();
                OnPropertyChanged ("Model");
            }
        }

        #region | Events |

        public event EventHandler ItemSelected;

        protected virtual void OnItemSelected(EventArgs e)
        {
            EventHandler handler = ItemSelected;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region | Event Handlers |

        protected override void OnAdded (View view)
        {
            base.OnAdded (view);

            if (!(view is StackedListItem)) {
                Children.Remove (view);
            } else {
                var sli = (StackedListItem)view;
                sli.GestureRecognizers.Add (new TapGestureRecognizer {
                        Command = TapCommand(sli)
                    });
            }
        }

        protected override void OnBindingContextChanged ()
        {
            base.OnBindingContextChanged ();
            BindChildren ();
        }

        #endregion

        public void ClearSelection()
        {
            foreach (var childItem in Children) {
                childItem.BackgroundColor = Styles.ListBackgroundColor;
            }
            Model.SelectedItem = null;
        }

        #region | Private Methods |

        private void BindChildren()
        {
            Children.Clear ();

            foreach (var item in Model.Items) {
                Children.Add (new StackedListItem { BindingContext = item });
            }
        }

        private Command TapCommand(StackedListItem item)
        {
            return new Command ((parameter) => { 
                foreach (var childItem in Children) {
                    childItem.BackgroundColor = Styles.ListBackgroundColor;
                }

                item.BackgroundColor = Styles.SelectedItemHighlightColor; 
                Model.SelectedItem = (StackedListItemViewModel)item.BindingContext;
                OnItemSelected(new EventArgs());
            });
        }

This is used in XAML like so...

                    <v:StackedList x:Name="activeClientsList"
                                   VerticalOptions="Start"
                                   Spacing="1"
                                   Model="{Binding ActiveClients}"
                                   ItemSelected="ActiveClientsItemSelected"
                                   BackgroundColor="{x:Static st:Styles.BackgroundColor}"
                                   IsVisible="{Binding ActiveClients.Any}" />

If I change the underlying value of ActiveClients (or InactiveClients or UserClients) in the containing Page's ViewModel (Below) the list content doesn't update, so when I execute the search code nothing changes. What am I missing here?

Here's the code for the ViewModel that the XAML above uses...

    public class HomeViewModel : NavigableViewModelBase (Provides an INavigation, State Manager and implements INotifyPropertyChanged) 
    {
        private ClientSet _currentClientSetState;
        private string _searchQuery;
        private IClientSetManager _clientSetManager;

        #region | Construction |

        public HomeViewModel(INavigation navigation, IAppState appState)
            : base(navigation, appState)
        { 
            _clientSetManager = new ClientSetManager(appState.Repos, appState.CurrentUser);
            BindClientSets();
        }

        #endregion

        public StackedListViewModel ActiveClients { get; private set; }

        public StackedListViewModel InactiveClients { get; private set; }

        public StackedListViewModel UserClients { get; private set; }

        public ClientSet CurrentClientSetState
        {
            get { return _currentClientSetState; }
            set 
            {
                _currentClientSetState = value;
                OnPropertyChanged("CurrentClientSetState");
            }
        }

        public string SearchQuery
        {
            get { return _searchQuery; }
            set 
            {
                _searchQuery = value;
                OnPropertyChanged("SearchQuery");
            }
        }

        #region | Events |

        public event EventHandler<SearchEventArgs> SearchClientsCompleted;

        protected virtual void OnSearchClientsCompleted(SearchEventArgs e)
        {
            var handler = SearchClientsCompleted;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region | Commands |

        public ICommand SearchClientsCommand
        {
            get 
            { 
                return new Command (async () => {
                    ExecuteSearch();
                    OnSearchClientsCompleted(new SearchEventArgs(SearchQuery));
                }); 
            }
        }

        #endregion

        #region | Private Methods |

        private void BindClientSets()
        {
            ActiveClients = new StackedListViewModel(
                from client in _clientSetManager.ActiveClients
                select client);

            InactiveClients = new StackedListViewModel(
                from client in _clientSetManager.InactiveClients
                select client);

            UserClients = new StackedListViewModel(
                from client in _clientSetManager.UserClients
                select client);

            OnPropertyChanged("ActiveClients");
            OnPropertyChanged("InactiveClients");
            OnPropertyChanged("UserClients");
        }

        private void ExecuteSearch()
        {
            if (String.IsNullOrWhiteSpace(SearchQuery))
                _clientSetManager.ClearFilter();
            else
                _clientSetManager.Filter(SearchQuery);

            BindClientSets();
        }

        #endregion
    }

And the code behind file for the XAML...

    public partial class Home : ContentPage
    {
        #region | Constructor |

        public Home ()
        {
            InitializeComponent();
            Model = new HomeViewModel(this.Navigation, new AppState());

            BindingContext = Model;

            InitUi();
            Model.SearchClientsCompleted += SearchClientsCompleted;
        }

        #endregion

        protected HomeViewModel Model { get; private set; }

        #region | Event Handlers |

        protected void SearchClientsCompleted(object sender, SearchEventArgs args)
        {
            DisplayAlert ("Alert", "You searched for " + args.Query, "OK");
        }

        #endregion

        #region | Private Methods |

        private void InitUi()
        {
            signOutButton.SetActive();
        }

        #endregion
    }

(code reduced for brevity)


Viewing all articles
Browse latest Browse all 58056

Trending Articles



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