Using SQlite to bind GroupedItems Template but only getting GroupNames - sqlite

This code is from the datasource class. I am fetching the list of customers from the SQLite database and storing it in ObservableCollection. Using GetGroups() I am creating the groups based on some property:
public ObservableCollection<CustomerDetails> GetAllCustomers()
{
using (var con = new SQLiteConnection(app.DBPath))
{
ObservableCollection<CustomerDetails> newCol = new ObservableCollection<CustomerDetails>(con.Query<CustomerDetails>("Select * from CustomerDetails"));
return newCol;
}
}
public IEnumerable<IGrouping<int,CustomerDetails>> GetGroups()
{
return GetAllCustomers().OrderBy(x=>x.CustomerName).GroupBy(x=>x.CustomerPropertyType);
}
This is how I am binding the Grid View
CustomerImplementation objCustomerImp = new CustomerImplementation();
var all = objCustomerImp.GetGroups();
this.DefaultViewModel["Groups"] = all;
XAML File:
CustomerName, ContactNo1 and EmailId are properties inside DataSource. All are bound in the code above.
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"/>
<GridView
x:Name="itemGridView"
IsItemClickEnabled="True"
IsSwipeEnabled="True"
Grid.RowSpan="2"
Padding="116,136,116,46"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource groupedItemsViewSource}}"
SelectionMode="Single"
SelectedItem="0">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="320" Height="240">
<StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding CustomerName}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="48" Margin="15,0,15,0"/>
<TextBlock Text="{Binding ContactNo1}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="48" Margin="15,0,15,0"/>
<TextBlock Text="{Binding EmailId}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="48" Margin="15,0,15,0"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Style="{StaticResource TextPrimaryButtonStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}" Margin="3,-7,10,10" Style="{StaticResource GroupHeaderTextStyle}" />
<TextBlock Text="{StaticResource ChevronGlyph}" FontFamily="Segoe UI Symbol" Margin="0,-7,0,10" Style="{StaticResource GroupHeaderTextStyle}"/>
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>

I believe SQLite-net is implemented lazily, so the query doesn't actually give any results until you try to access the items in the collection. Try putting ToList() at the end of the Query call:
public ObservableCollection<CustomerDetails> GetAllCustomers()
{
using (var con = new SQLiteConnection(app.DBPath))
{
// add ToList() to query to instantiate the results
ObservableCollection<CustomerDetails> newCol = new ObservableCollection<CustomerDetails>(con.Query<CustomerDetails>("Select * from CustomerDetails").ToList());
return newCol;
}
}

I recreated your solution and found the problem in DefaultViewModel. Use your own implementation of DefaultViewModel, or call it MainViewModel, which implements INotifyPropertyChanged, e.g.:
public class MainViewModel : INotifyPropertyChanged
{
private IEnumerable<IGrouping<int, CustomerDetails>> groups = null;
public IEnumerable<IGrouping<int, CustomerDetails>> Groups
{
get { return groups; }
private set { Set(ref groups, value); }
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private bool Set<T>(ref T storage, object value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value))
return false;
storage = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
return true;
}
#endregion
}
Then set the DataContext of your Page to an instance of MainViewModel, and set the Groups property with data you want (should be in MainViewModel too, e.g., with some LoadGroups method). CollectionViewSource in page resources references to Groups property of your MainViewModel and you will see your data in the GridView.

Related

How to implement a listView "quick filtering" like week calendar view?

I've created a customer specific task management app with tasks placed on specific dates (and sometime hours), but here the date is important.
I'm using a listView and have a DatePicker setting for selected other dates than today. So far so good.
I would like to implement a week quick-filter option so that e.g., the dates of the current week is displayed at the top of the list view and a click on a certain date would filter the listView accordingly. Kind of a standard outlook-like week view.
How would I do this in the best way?
CustomControl that I put above the listView?
ViewPager control?
Any ideas or suggestions much appreciated.
P.S. I need to be able to target both Android and iOS.
Set two Properties in the ViewModel one for containing all the Items EntireCollection and another to store the Filtered Items FilteredCollection. On button click derive the Filtered item from entire list using Where.
ViewModel
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<ListItem> filteredCollection;
public ObservableCollection<ListItem> FilteredCollection
{
get
{
return filteredCollection;
}
set
{
filteredCollection = value;
OnPropertyChanged();
}
}
private ObservableCollection<ListItem> entireCollection;
public ObservableCollection<ListItem> EntireCollection
{
get
{
return entireCollection;
}
set
{
entireCollection = value;
OnPropertyChanged();
}
}
public ViewModel()
{ ...
this.FilterCollection = this.EntireCollection;
...
}
}
Button clicked
void Button_Clicked(System.Object sender, System.EventArgs e)
{
DateTime selectedDate = ((DateTime)((sender as VisualElement).BindingContext)).Date;
viewModel.FilteredCollection = new ObservableCollection<ListItem>(viewModel.EntireCollection.Where(x =>
{
if (DateTime.Equals(x.DateAdded, selectedDate))
{
var asd = x.DateAdded.Day;
return true;
}
return false;
}));
}
XAML
<StackLayout>
<ScrollView
x:Name="calender"
Orientation="Horizontal">
<StackLayout
BackgroundColor="Blue"
BindableLayout.ItemsSource="{Binding Dates}"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Button
TextColor="White"
BackgroundColor="Blue"
Clicked="Button_Clicked"
Text="{Binding Day}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
<ListView
ItemsSource="{Binding FilteredCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding DateAdded}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Hope it helps!!

Xamarin Listview don't show the observable Collection

I'm using Xamarin.Forms MVVM to develop my app, and don't found what I'm doing wrong, I have an ObservableCollection with the values from web API, and when I set a break point all the values are good even in the view when I see the values of the binding source everything have the value, but the values are not showing up in my ListView.
Here is the ViewModel
class DatosMedicosViewModel : BaseViewModel
{
private ApiService apiService;
private ObservableCollection<Land> land;
private bool isRefreshing;
public ObservableCollection<Land> Lands
{
get { return this.land; }
set { SetValue(ref this.land, value); }
}
public bool IsRefreshing
{
get { return this.isRefreshing; }
set { SetValue(ref this.isRefreshing, value); }
}
public DatosMedicosViewModel()
{
this.apiService = new ApiService();
this.LoadLand();
}
private async void LoadLand()
{
this.IsRefreshing = true;
var connection = await this.apiService.CheckConnection();
if (!connection.IsSuccess)
{
this.IsRefreshing = false;
await Application.Current.MainPage.DisplayAlert(
"Error",
connection.Message,
"Accept");
await Application.Current.MainPage.Navigation.PopAsync();
return;
}
var response = await this.apiService.GetList<Land>(
"url Base",
"prefix",
"Controller");
if (!response.IsSuccess)
{
this.IsRefreshing = false;
await Application.Current.MainPage.DisplayAlert(
"Error",
response.Message,
"Accept"
);
return;
}
var list = (List<Land>)response.Result;
this.Lands = new ObservableCollection<Land>(list);
this.IsRefreshing = false;
}
public ICommand RefreshCommand
{
get
{
return new RelayCommand(LoadLand);
}
}
}
Here is the View
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ARLAPP.Views.ConsultaPage"
BackgroundColor="White"
BindingContext="{Binding Main, Source={StaticResource Locator}}"
Title="Lands">
<ContentPage.Content>
<StackLayout
BindingContext="{Binding Lands}"
Padding="5">
<StackLayout>
<Image
VerticalOptions="Center"
WidthRequest="300"
Source="UserIcon"
BackgroundColor="Transparent"/>
<Label Text="Mark"
VerticalOptions="Center"
HorizontalOptions="CenterAndExpand"
FontAttributes="Bold"
FontSize="Medium"/>
</StackLayout>
<StackLayout>
<ListView
SeparatorVisibility="Default"
FlowDirection="LeftToRight"
BackgroundColor="White"
ItemsSource="{Binding Lands}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label
Grid.Column="2"
VerticalOptions="Center"
TextColor="Black"
Text="{Binding Currency}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Here how I call the view
if (this.PageName == "Lands")
{
MainViewModel.GetInstance().Lands= new LandViewModel();
Application.Current.MainPage = new LandMasterPage();
}
Check your BindingContext. I think you are setting it wrong in your view.
In your top-level StackLayout you set the the BindingContext to your property: BindingContext="{Binding Lands}". And in your ListView you set the ItemsSource also to this property: ItemsSource="{Binding Lands}". That won't work because the ListView is trying to bind to a property Lands inside your BindingContext, which is also set to Lands.
Remove the BindingContext from your top-level StackLayout, because you don't need it.
Ensure the BindingContext of your page ConsultaPage is set to your view-model DatosMedicosViewModel.
Sample of setting the bindingcontext (abstract code):
var mypage = new ConsultaPage();
mypage.BindingContext = new DatosMedicosViewModel();
await Navigation.PushAsync(mypage);
// Load your data in OnAppearing() of the page-event
This should solve your binding-problem.
Side-Note: As Abdul Gani said in the comments: Ensure you implement the INotifyPropertyChanged interface, but I assume you do this already in your BaseViewModel and call the NotifyChanged-Event in your SetValue-Method.

Xamarin Forms: Dynamically creating a Listview item -> Problem with BindingContext

as I was already describing in another post here on Stackoverflow, I was trying to get a different layout (one frame spanning multiple listview items). Now I decided to try the following approach: My ViewModel is a List of Lists (just like for a grouped listview). However instead of using a grouped listview, I have a normal ListView in which the single Items of the child list will be created in Code-behind as soon as the bindingContext of the ParentViewCell is available:
private void CommentViewCell_BindingContextChanged(object sender, EventArgs e)
{
if (this.BindingContext == null) return;
var model = this.BindingContext as CommentViewModel;
DateCommentViewCell dateCell = new DateCommentViewCell
{
BindingContext = model
};
ParentCommentViewCell parentCell = new ParentCommentViewCell
{
BindingContext = model
};
ContentStackView.Children.Add(dateCell.View);
ContentStackView.Children.Add(parentCell.View);
foreach (CommentBaseViewModel cbvm in model)
{
if (cbvm is CommentViewModel)
{
ChildCommentViewCell childCell = new ChildCommentViewCell
{
BindingContext = cbvm
};
ContentStackView.Children.Add(childCell.View);
}
}
}
When I run this, the visuals are actually ok and look how I intended them to.
However the BindingContext is wrong: The ChildCommentViewCell BindingContext does not reference the CommentViewModel of the child, but that of the parent when being displayed. I checked the BindingContext of the ChildCommentViewCell like this
public ChildCommentViewCell ()
{
InitializeComponent ();
BindingContextChanged += ChildCommentViewCell_BindingContextChanged;
}
private void ChildCommentViewCell_BindingContextChanged(object sender, EventArgs e)
{
Debug.WriteLine("### ChildCommentViewCell BindingContext Changed");
test();
}
public void test()
{
var context = this.BindingContext as CommentViewModel;
Debug.WriteLine("### Instance: " + this.GetHashCode());
Debug.WriteLine("### \tBinding Context: " + context.CommentModel.Text);
Debug.WriteLine("### \tLabel: " + ChildCommentText.Text);
}
and the output on the console is just fine. However when running on my phone, the actual content is (as written above) that of the ParentCommentViewModel. Any ideas?
The XAML code of the ChildCommentViewCell element is the following:
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.View.ViewCell.ChildCommentViewCell">
<StackLayout Padding="10,0" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<StackLayout Grid.Column="0" VerticalOptions="FillAndExpand" Orientation="Vertical" Spacing="0">
<Label Text="{Binding CommentModel.AuthorName}" Style="{StaticResource CommentAuthor}"/>
</StackLayout>
<Frame IsClippedToBounds="True" HasShadow="False" Margin="5" Padding="3" BackgroundColor="LightGray" CornerRadius="3.0">
<StackLayout Grid.Column="1" VerticalOptions="FillAndExpand" Orientation="Vertical" Spacing="0">
<Label x:Name="ChildCommentText" Text="{Binding Path=CommentModel.Text, StringFormat=' {0}'}" Style="{StaticResource CommentContent}"/>
<Label Text="{Binding CommentTimeAgo}" Style="{StaticResource CommentTime}" HorizontalOptions="Start"/>
</StackLayout>
</Frame>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
One additional thing: I tried to debug the "Appearing"-Event, however this does not even get called once...?!
Thank you very much in advance!
Found my problem in the BindingContextChanged method: I had to explicitly bind the BindingContext to the view, not only to the ViewCell:
foreach (CommentBaseViewModel cbvm in model)
{
if (cbvm is CommentViewModel)
{
ChildCommentViewCell childCell = new ChildCommentViewCell
{
BindingContext = cbvm
};
childCell.View.BindingContext = cbvm;
ContentStackView.Children.Add(childCell.View);
}
}

What's wrong with my Windows Phone 7 Databinding(no viewmodels used)?

I am having difficulty in databinding. I can successfully get the results but it just won't display. here is my code:
private List<FacebookFriend> friendList;
public List<FacebookFriend> FriendList
{
get { return friendList; }
set
{
friendList = value;
NotifyPropertyChanged("FriendList");
}
}
private void GetFbFriends()
{
var fb = new FacebookClient(_accessToken);
friendList = new List<FacebookFriend>();
fb.GetCompleted += (o, e) =>
{
if (e.Error != null)
{
return;
}
var result = (JsonObject)e.GetResultData();
foreach (var friend in (JsonArray)result["data"])
friendList.Add(new FacebookFriend()
{
Id = (string)(((JsonObject)friend)["id"]),
Name = (string)(((JsonObject)friend)["name"])
});
FriendList = friendList;
};
fb.GetAsync("me/friends");
}
then in the page's xaml:
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding FriendList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="Red" Height="100" Width="300" Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=Id}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It seems correct but still, it does not display anything. Any help is appreciated. Thanks so much!
Try using ObservableCollection<> instead of list<>. For more info please see this
Note: ObservableCollection is a generic dynamic data collection that provides notifications (using an interface "INotifyCollectionChanged") when items get added, removed, or when the whole collection is refreshed.

Bind an element in an ItemTemplate to a value outside the ItemSource in Windows Phone 7

I have a custom UserControl that consists of a ListBox with a DataTemplate.
The ListBox gets it's source set in XAML and the elements in the DataTemplate gets it's values from Binding.
My UserControl XAML looks like this:
<UserControl x:Class="Test.UserControls.TracksListBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="clr-namespace:Test.Converters"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
mc:Ignorable="d"
d:DesignHeight="480" d:DesignWidth="480"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Name="this">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="transparent">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" IsChecked="{Binding Show}"/>
<ListBox Grid.Row="1" Name="List">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Top"
Margin="0 5 0 5">
<TextBlock Text="{Binding Title}"/>
<CheckBox IsChecked="{Binding ElementName=this, Path=Show}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And the code-behind:
namespace Test.UserControls
{
public partial class TracksListBox : UserControl
{
public static readonly DependencyProperty TracksListProperty =
DependencyProperty.Register("TracksList",
typeof(List<Track>),
typeof(TracksListBox),
new PropertyMetadata(null, OnTracksListChanged));
public static readonly DependencyProperty ShowProperty =
DependencyProperty.Register("Show",
typeof(bool),
typeof(TracksListBox),
new PropertyMetadata(false));
public TracksListBox()
{
InitializeComponent();
}
public List<Track> TracksList
{
get
{
return (List<Track>)GetValue(TracksListProperty);
}
set
{
SetValue(TracksListProperty, value);
}
}
public bool Show
{
get
{
return (bool)GetValue(ShowProperty);
}
set
{
SetValue(ShowProperty, value);
}
}
private static void OnTracksListChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
(obj as TracksListBox).OnTracksListChanged((List<Track>)args.OldValue, (List<Track>)args.NewValue);
}
protected virtual void OnTracksListChanged(List<Track> oldValue, List<Track> newValue)
{
List.ItemsSource = newValue;
}
}
}
In my MainPage.xaml I use it like this:
<userControls:TracksListBox x:Name="TopTracksListBox"
TracksList="{Binding ElementName=this, Path=TopTracks}"
Show="True"/>
My problem here is that the CheckBox inside the ListBox DataTemplate won't get the value from Show. The other CheckBox in Grid.Row="0" though, gets the correct value... How do I bind a value from my UserControl inside the DataTemplate of the ListBox?
This must be a bug, no mather what I tried the DataContext of the CheckBox always ended up being null or a Track. If you want to work around it you can add this until this gets fixed
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Top"
Margin="0 5 0 5">
<TextBlock Text="{Binding Title}"/>
<CheckBox Loaded="CheckBox_Loaded"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
and then set the Binding in code behind once it is Loaded
private void CheckBox_Loaded(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
Binding isCheckedBinding = new Binding("Show");
isCheckedBinding.Source = this;
checkBox.SetBinding(CheckBox.IsCheckedProperty, isCheckedBinding);
}
I dropped your code into a user control in an app, changed Track to string and didn't bind anything to the list but I still saw a checkbox displayed and the binding of Show to the IsChecked worked for me.

Resources