Data template not updating cell - xamarin

I'm trying to insert a new row (different cell) between two items on a list. The problem is when the new item is inserted, it does not insert the desired cell.
For example, I want to insert item5(ItemCell2) between item 1 and 2:
item1(ItemCell1)
item2(ItemCell1)
item3(ItemCell1)
item4(ItemCell1)
When I insert item5 the cell takes the form of ItemCell1 not of ItemCell2.
Also, I notice that OnSelectTemplate in DemoTemplate is only called once. Thus if the first item of the list is Visible = true it applies ItemCell2 to all the rows even thought the list contains items with Visible = false
using System;
using System.Collections.ObjectModel;
using MvvmHelpers;
using Xamarin.Forms;
namespace Demo.ViewModels
{
public class ItemViewModel : BaseViewModel
{
public ItemViewModel()
{
var item1 = new Item { ItemName = "Name 1", Visible = false };
var item2 = new Item { ItemName = "Name 2", Visible = true };
var item3 = new Item { ItemName = "Name 3", Visible = false };
var item4 = new Item { ItemName = "Name 4", Visible = false };
var item5 = new Item { ItemName = "Name 5", Visible = false };
List = new ObservableCollection<Item>();
List.Add(item1);
List.Add(item2);
List.Add(item3);
List.Add(item4);
List.Add(item5);
MessagingCenter.Subscribe<Item, Item>(this, "msg", (sender, arg) =>
{
DoSomething(arg);
});
void DoSomething(Item item)
{
var index = List.IndexOf(item);
List.Insert(index, new Item { ItemName = "Name 6", Visible = true} );
}
}
ObservableCollection<Item> list;
public ObservableCollection<Item> List
{
get { return list; }
set { SetProperty(ref list, value); }
}
}
}
Item:
using System;
using System.Windows.Input;
using MvvmHelpers;
using Xamarin.Forms;
namespace Demo.Models
{
public class Item : ObservableObject
{
string itemName;
public string ItemName
{
get { return itemName; }
set { SetProperty(ref itemName, value); }
}
bool visible;
public bool Visible
{
get { return visible; }
set { SetProperty(ref visible, value); }
}
ICommand testCommand;
public ICommand TestCommand => testCommand ??
(
testCommand = new Command(DoSomething)
);
void DoSomething()
{
MessagingCenter.Send(this, "msg", this);
System.Diagnostics.Debug.WriteLine("test");
}
}
}
Data Template:
using System;
using Xamarin.Forms;
namespace Demo.Templates
{
public class DemoTemplate : DataTemplateSelector
{
readonly DataTemplate dataTemplate1;
readonly DataTemplate dataTemplate2;
public DemoTemplate()
{
dataTemplate1 = new DataTemplate(typeof(ItemCell1));
dataTemplate2 = new DataTemplate(typeof(ItemCell2));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var dataTemplate = dataTemplate1;
var myItem = item as Item;
if(myItem.Visible)
{
dataTemplate = dataTemplate1;
}
else
{
dataTemplate = dataTemplate2;
}
return dataTemplate;
}
}
}
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"
x:Class="Demo.Pages.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<template:DmoTemplate
x:Key="dataTemplate">
</template:DemoTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ListView
CachingStrategy="RecycleElementAndDataTemplate"
ItemTemplate="{StaticResource dataTemplate}"
ItemsSource="{Binding List}"
HasUnevenRows="true"
SeparatorVisibility="None">
</ListView>
</ContentPage.Content>
</ContentPage>

Changing CachingStrategy to "CachingStrategy="RecycleElement"' fixed the issue.

Related

Highlight URL using label span - xamarin.forms

I am creating a chat application in xamarin.forms.What I am trying to achieve is whenever user typed message contains a URL, that should be highlighted and provide click to it.For this feature I found Span in Label text.When user click on send button of chat , I will check for URL and make it as another span.I got this idea from Lucas Zhang - MSFT form this question here.
The problem is I am trying to do the spanning in view model and the individual chat bubble is in another view cell which will call as ItemTemplate in my chat listview. Anyway the spanning is not working as I intended ie; it doesn't highlight .
My view Model.
public Queue<Message> DelayedMessages { get; set; } = new Queue<Message>();
public ObservableCollection<Message> Messages { get; set; } = new ObservableCollection<Message>();
public string TextToSend { get; set; }
public ChatPageViewModel()
{
OnSendCommand = new Command(() =>
{
if (!string.IsNullOrEmpty(TextToSend))
{
var urlStr = TextToSend;
int startIndex = 0, endIndex = 0;
if (urlStr.Contains("www."))
{
startIndex = urlStr.IndexOf("www.");
}
if (urlStr.Contains(".com"))
{
endIndex = urlStr.IndexOf(".com") + 3;
}
if (startIndex != 0 || endIndex != 0)
{
var formattedString = new FormattedString();
Span span1 = new Span() { Text = urlStr.Substring(0, startIndex), TextColor = Color.Black };
formattedString.Spans.Add(span1);
Span span2 = new Span() { Text = urlStr.Substring(startIndex, endIndex - startIndex + 1), TextColor = Color.LightBlue };
span2.GestureRecognizers.Add(new TapGestureRecognizer()
{
NumberOfTapsRequired = 1,
Command = new Command(() => {
})
});
formattedString.Spans.Add(span2);
Span span3 = new Span() { Text = urlStr.Substring(endIndex, urlStr.Length - 1 - endIndex), TextColor = Color.Black };
formattedString.Spans.Add(span3);
var message = new Message
{
Text = formattedString.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
else
{
var message = new Message
{
Text = urlStr.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
}
});
}
Single chat Bubble XAML
<Label x:Name="OutgoingMessage" TextColor="White" FormattedText="{Binding Text}" HorizontalOptions="End" >
</Label>
My Chat page XAML
<Grid RowSpacing="0" Margin="0,20,0,0"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages,Mode=OneWay}"
Margin="0"
SelectionMode="None"
FlowDirection="RightToLeft"
HasUnevenRows="True" x:Name="ChatList"
VerticalOptions="FillAndExpand"
SeparatorColor="Transparent"
>
</ListView>
<BoxView HorizontalOptions="FillAndExpand"
HeightRequest="1"
BackgroundColor="#F2F3F5"
Grid.Row="1"/>
<partials:ChatInputBarView Grid.Row="2"
Margin="0,0,0,0"
x:Name="chatInput"/>
</Grid>
ChatPage.xaml.cs
public partial class ChatPage : ContentPage
{
ChatPageViewModel vm;
public ChatPage()
{
InitializeComponent();
this.BindingContext = vm= new ChatPageViewModel();
}
}
Messages class
public class Message : ObservableObject
{
string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}
Any Help is appreciated.
EDIT:
This question was actually continuation of question. Previously I used AwesomeHyperLinkLabel fromlink. The problem was I cant manage the click event of that label.Thats why I moved with label span.Thanks to Leo Zhu - MSFT For the render changes.
For Android:
[assembly: ExportRenderer(typeof(AwesomeHyperLinkLabel), typeof(AwesomeHyperLinkLabelRenderer))]
namespace App18.Droid
{
public class AwesomeHyperLinkLabelRenderer : LabelRenderer
{
public AwesomeHyperLinkLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (AwesomeHyperLinkLabel)Element;
if (view == null) return;
TextView textView = new TextView(Forms.Context);
textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
textView.SetTextColor(view.TextColor.ToAndroid());
// Setting the auto link mask to capture all types of link-able data
textView.AutoLinkMask = MatchOptions.All;
// Make sure to set text after setting the mask
textView.Text = view.Text;
AddHyperlinksManually(textView);
//textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
// overriding Xamarin Forms Label and replace with our native control
SetNativeControl(textView);
}
public static void AddHyperlinksManually(TextView _tv)
{
SpannableStringBuilder currentSpan = new SpannableStringBuilder(_tv.Text);
Linkify.AddLinks(currentSpan, MatchOptions.WebUrls);
var objects = currentSpan.GetSpans(0, currentSpan.Length(), Java.Lang.Class.FromType(typeof(URLSpan)));
var urlSpans = new URLSpan[objects.Length];
for (var i = 0; i < urlSpans.Length; i++)
{
urlSpans[i] = objects[i] as URLSpan;
}
foreach (URLSpan _url in urlSpans)
{
int iStart = currentSpan.GetSpanStart(_url);
int iEnd = currentSpan.GetSpanEnd(_url);
currentSpan.RemoveSpan(_url);
currentSpan.SetSpan(new CustomURLSpan(_url.URL), iStart, iEnd, SpanTypes.InclusiveInclusive);
_tv.SetText(currentSpan, TextView.BufferType.Normal);
_tv.MovementMethod = LinkMovementMethod.Instance;
}
}
public class CustomURLSpan : ClickableSpan
{
string mTargetURL;
public CustomURLSpan(string _url) {
mTargetURL =_url;
}
public override void OnClick(Android.Views.View widget)
{
//here you could handle the click event,and you could use MessagingCenter to send mTargetURL to your Page.
Console.WriteLine("Click");
}
}
}
The mistake was with my model.Changed string to FormattedString and also changed in the viewmodel
public class Message : ObservableObject
{
FormattedString text;
public FormattedString Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}

Telerik UI for Xamarin: Property not found for RadListView Binding

I am working on an Android app with Xamarin, using Telerik UI.
The following error is raised when trying to bind a property to a Telerik ListViewTextCell in a RadListView:
[0:] Binding: 'Author' property not found on 'Book', target property: 'Telerik.XamarinForms.DataControls.ListView.ListViewTextCell.Detail'
This happens in even the most minimal cases. Below is an example, drawn largely from the ListView documentation itself.
PageTest.cs:
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;
using Telerik.XamarinForms.DataControls;
using Telerik.XamarinForms.DataControls.ListView;
namespace MyTelerikApp
{
[DesignTimeVisible(false)]
public partial class PageTest : ContentPage
{
public PageTest()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
var listView = new RadListView
{
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 }
};
MainPageContent.Children.Add(listView);
}
}
}
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class ViewModel
{
public ViewModel()
{
this.Source = new List<Book>{
new Book{ Title = "The Fault in Our Stars ", Author = "John Green"},
new Book{ Title = "Divergent", Author = "Veronica Roth"},
new Book{ Title = "Gone Girl", Author = "Gillian Flynn"},
new Book{ Title = "Clockwork Angel", Author = "Cassandra Clare"},
new Book{ Title = "The Martian", Author = "Andy Weir"},
new Book{ Title = "Ready Player One", Author = "Ernest Cline"},
new Book{ Title = "The Lost Hero", Author = "Rick Riordan"},
new Book{ Title = "All the Light We Cannot See", Author = "Anthony Doerr"},
new Book{ Title = "Cinder", Author = "Marissa Meyer"},
new Book{ Title = "Me Before You", Author = "Jojo Moyes"},
new Book{ Title = "The Night Circus", Author = "Erin Morgenstern"},
};
}
public List<Book> Source { get; set; }
}
PageText.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="GeoGIS.views.PageTest">
<StackLayout x:Name="MainPageContent">
</StackLayout>
</ContentPage>
After some searching, it seems that a BindingContext is necessary, but I couldn't get that to work either.
I didn't found BindingContext from your code.And I guess you confused the two usages of ContentPage(XAML and C# ).
When we created a contentpage,we have two choices(XAML and C#) as follows:
1.When we choose the ContentPage(c#),in this case, there is no xaml.And we can do like this:
public class TestPage1 : ContentPage
{
public TestPage1 ()
{
var listView = new RadListView
{
BackgroundColor = Color.White,
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 },
};
Content = new StackLayout {
Children = {
listView
}
};
}
}
2.When we choose the ContentPage,in this case, code has xaml.We can do like this.
Put the followinging code in your xaml
<StackLayout>
<telerikDataControls:RadListView ItemsSource="{Binding Source}" BackgroundColor="White" x:Name="listView">
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
<telerikDataControls:RadListView.ItemTemplate>
<DataTemplate>
<telerikListView:ListViewTextCell Text="{Binding Title}" Detail="{Binding Author}" TextColor="Black" DetailColor="Gray" />
</DataTemplate>
</telerikDataControls:RadListView.ItemTemplate>
<telerikDataControls:RadListView.LayoutDefinition>
<telerikListView:ListViewLinearLayout ItemLength="70" />
</telerikDataControls:RadListView.LayoutDefinition>
</telerikDataControls:RadListView>
</StackLayout>
And remove the method OnAppearing() from your code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
//BindingContext = new ViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
From above code,we can found the BindingContext,it is necessary.
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
And we can also BindingContext like this(Any one is ok.):
BindingContext = new ViewModel();
The result is the same:

Xamarin Forms Data Binding

I am just starting out using Xamarin so I decided to make a project so I could get the hang of things. For my project, I have these details pages which based on which page you select will display a chart and some text with data specific to that page. I was able to display the text quite easily with bindingcontext but I am lost on how I would be able to pass the data for the charts. I am using microcharts to generate the charts.
ItemDetailPage.xaml.cs
using System;
using Xamarin.Forms;
using System.Collections.Generic;
using Microcharts;
using Entry = Microcharts.Entry;
using SkiaSharp;
namespace TestApp
{
public partial class ItemDetailPage : ContentPage
{
List<Microcharts.Entry> entries = new List<Microcharts.Entry>
{
new Entry(200)
{
Label = "January",
ValueLabel = "200",
Color = SKColor.Parse("#266489")
},
new Entry(400)
{
Label = "February",
ValueLabel = "400",
Color = SKColor.Parse("#68B9C0")
},
new Entry(-100)
{
Label = "March",
ValueLabel = "-100",
Color = SKColor.Parse("#90D585")
}
};
ItemDetailViewModel viewModel;
// Note - The Xamarin.Forms Previewer requires a default, parameterless constructor to render a page.
public ItemDetailPage()
{
Initialize(null);
}
public ItemDetailPage(ItemDetailViewModel viewModel)
{
Initialize(viewModel);
}
public void Initialize(ItemDetailViewModel viewModel) {
InitializeComponent();
Chart1.Chart = new RadialGaugeChart { Entries = entries };
if (viewModel == null) {
var item = new Item
{
Text = "Item 1",
Description = "This is an item description."
};
viewModel = new ItemDetailViewModel(item);
Console.WriteLine(item.Text);
}
BindingContext = viewModel;
}
}
}
ItemDetailPage.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:forms = "clr-namespace:Microcharts.Forms;assembly=Microcharts.Forms" x:Class="TestApp.ItemDetailPage" Title="{Binding Title}">
<ContentPage.Content>
<StackLayout>
<Label TextColor="#77d065" FontSize = "20" Text="{Binding Item.Text}" />
<Label TextColor="#77d065" FontSize = "20" Text="{Binding Item.Info}" />
<forms:ChartView x:Name="Chart1" HeightRequest = "150"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ItemDetailViewModel.cs
using System;
namespace TestApp
{
public class ItemDetailViewModel : BaseViewModel
{
public Item Item { get; set; }
public ItemDetailViewModel(Item item = null)
{
Title = item?.Text;
Item = item;
}
}
}
ItemsPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TestApp
{
public partial class ItemsPage : ContentPage
{
ItemsViewModel viewModel;
public ItemsPage()
{
InitializeComponent();
BindingContext = viewModel = new ItemsViewModel();
}
async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
var item = args.SelectedItem as Item;
if (item == null)
return;
await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));
// Manually deselect item
ItemsListView.SelectedItem = null;
}
async void AddItem_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new NewItemPage());
}
protected override void OnAppearing()
{
base.OnAppearing();
if (viewModel.Items.Count == 0)
viewModel.LoadItemsCommand.Execute(null);
}
}
}
Items List
items = new List<Item>();
var mockItems = new List<Item>
{
new Item { Id = Guid.NewGuid().ToString(), Text = "First item", Description="This is an item description.", Info = "Some More Info", label1 = "value1" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Second item", Description="This is an item description.",label1 = "value2" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Third item", Description="This is an item description.", label1 = "value3" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Fourth item", Description="This is an item description." },
new Item { Id = Guid.NewGuid().ToString(), Text = "Fifth item", Description="This is an item description." },
new Item { Id = Guid.NewGuid().ToString(), Text = "Sixth item", Description="This is an item description." },
};
So like my goal for example would be to have the label for one of the entries in the chart correspond to the label1 value of item.
If anybody could point me in the right direction on how to accomplish this, that would be very nice.
You have got the ItemDetailViewModel in the method Initialize(...), so you can use your item passed from ItemsPage to customize your charts like:
// Do not use the default entries
Chart1.Chart = new RadialGaugeChart(); //{ Entries = entries };
// Modify the entry in entries(for instance the first one)
var singleEntry = entries[0];
Microcharts.Entry entry = new Microcharts.Entry(float.Parse(viewModel.Item.label1))
{
Label = singleEntry.Label,
ValueLabel = viewModel.Item.label1,
Color = singleEntry.Color
};
entries[0] = entry;
// At last initialize the chart's Entries
Chart1.Chart.Entries = entries;
just add this line and set the value the size to say in this example is 20f, but it can be anyone who serves you
Chart1.Chart.LabelTextSize = 30f;

Use search command of search bar on list view with mvvm pattern

I'm a beginner on xamarin mvvm patter. Currently I'm trying to create a search bar that searches the word from a list of names. I tried to write some codes on comman function on my view model and bind it on the SearchCommand of search bar on view. But it didn't work. Here's my code
namespace HelloWorld.ViewModel
{
public class CustViewModel : INotifyPropertyChanged
{
private custmodel _custmodel;
public custmodel custmodel
{
get { return _custmodel; }
set
{
_custmodel = value;
NotifyPropertyChanged();
}
}
private string _message;
public string message
{
get { return _message; }
set
{
_message = value;
NotifyPropertyChanged();
}
}
private ObservableCollection<string> _list;
public ObservableCollection<string> Items
{
get
{
return _list;
}
set
{
_list = value;
NotifyPropertyChanged();
}
}
public Command SaveCommand
{
get
{
return new Command(() =>
{
message = "Your task : " + custmodel.name + ", " + custmodel.surname + " was successfully saved!";
});
}
}
private string _bar;
public string Bar
{
get { return _bar; }
set { _bar = value;
}
}
public Command SearchCommand
{
get
{
return new Command(() =>
{
string keyword = _bar;
IEnumerable<String> searchresult = _list.Where(name => name.Contains(keyword));
_list = new ObservableCollection<string>(searchresult);
NotifyPropertyChanged();
}
);
}
}
public CustViewModel()
{
custmodel = new custmodel
{
name = "Aasish",
surname = "Gurung",
email = "iamaaceez#yahoo.com"
};
_list = new ObservableCollection<string>();
_list.Add("Saurab");
_list.Add("Basanta");
_list.Add("Abhishek");
_list.Add("Surace");
_list.Add("Amir");
}
public event PropertyChangedEventHandler PropertyChanged;
//public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is my xaml file
<?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="HelloWorld.Styling"
BackgroundColor="AntiqueWhite" Title="Hello"
xmlns:converters="clr-namespace:HelloWorld.Converters; assembly=HelloWorld">
<StackLayout>
<SearchBar x:Name="MainSearchBar" Text="{Binding Bar}"
SearchCommand="{Binding SearchCommand}"/>
<ListView ItemsSource="{Binding Items}"/>
</StackLayout>
First, make sure you are setting your ContentPage's BindingContext to your CustViewModel.
Also, you should stop assigning and adding things to _list and instead assign and add things to your public Items property. Items is the one that will trigger the NotifyPropertyChanged() method when it has been assigned to.
So change your SearchCommand to this:
return new Command(() => {
string keyword = _bar;
IEnumerable<String> searchresult = _list.Where(name => name.Contains(keyword));
Items = new ObservableCollection<string>(searchresult);
//NotifyPropertyChanged(); //There is no reason to trigger NotifyPropertyChanged on this command each time the getter is run, I would image that this could cause an infinite loop
});

Where to set bindings for a custom cell with customer renderer if I am using a DataTemplateSelector?

I have a DataTemplateSelector that selects between two different cells. On Android, this template picks cells that are defined as Android xml files. I can confirm that the template selector is working because I have two different color circles showing, and the colors are correct. But my data is not being bound and I am not sure why. I think I am not setting the binding somewhere, but I am not sure where/how to do that.
Here is my page that includes the ListViewwith the DataTemplateSelector. I set the ItemsSourcehere, but I never set the bindings for the different parts of the list items. That is where I do not know what to do.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Pages.Routines.TopLevelRoutinesPage"
xmlns:statics="clr-namespace:MyApp.Statics;assembly=MyApp"
xmlns:controls="clr-namespace:MyApp.Controls;assembly=MyApp">
<ContentPage.Resources>
<ResourceDictionary>
<controls:RoutinesDataTemplateSelector x:Key="RoutinesDataTemplateSelector"></controls:RoutinesDataTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Orientation="Vertical"
Spacing="0">
<ListView ItemsSource="{Binding SelectedRoutineTree}"
ItemTemplate="{StaticResource RoutinesDataTemplateSelector}"
x:Name="RoutinesView"
ItemSelected="RoutineClicked"
Margin ="0, 8, 0, 0">
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The code-behind:
using MyApp.ViewModels;
using MyCloudContracts.DTOs;
using System;
using System.Linq;
using Xamarin.Forms;
namespace MyApp.Pages.Routines
{
public partial class TopLevelRoutinesPage : ContentPage
{
private TopLevelRoutinesViewModel _viewModel;
private string _projCompName;
public TopLevelRoutinesPage(Guid docId, bool fromCompany, string projCompName)
{
InitializeComponent();
_projCompName = projCompName;
Title = _projCompName;
_viewModel = new TopLevelRoutinesViewModel(docId, fromCompany);
BindingContext = _viewModel;
if (Device.OS == TargetPlatform.Android)
RoutinesView.SeparatorVisibility = SeparatorVisibility.None;
}
private async void RoutineClicked(object sender, SelectedItemChangedEventArgs e)
{
//since this is also called when an item is deselected, return if set to null
if (e.SelectedItem == null)
return;
var selectedRoutine = (PublishedDocumentFragmentDTO)e.SelectedItem;
var fragId = selectedRoutine.FragmentId;
var title = selectedRoutine.Title;
var blobIdStr = selectedRoutine.BlobId;
var blobId = new Guid(blobIdStr);
if (selectedRoutine.Children.Any())
{
var routineTree = _viewModel.SelectedRoutineTree;
var subroutinesPage = new SubroutinesPage(routineTree, fragId, title, blobId, _projCompName);
await Navigation.PushAsync(subroutinesPage);
}
else
{
var routinePage = new RoutinePage(title, blobId);
await Navigation.PushAsync(routinePage);
}
//take away selected background
((ListView)sender).SelectedItem = null;
}
}
}
The DataTemplateSelector
using MyApp.Pages.Routines.CustomCells;
using MyCloudContracts.DTOs;
using Xamarin.Forms;
namespace MyApp.Controls
{
class RoutinesDataTemplateSelector : DataTemplateSelector
{
private readonly DataTemplate _folderDataTemplate;
private readonly DataTemplate _routineDataTemplate;
public RoutinesDataTemplateSelector()
{
_folderDataTemplate = new DataTemplate(typeof(FolderViewCell));
_routineDataTemplate = new DataTemplate(typeof(RoutineViewCell));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var chooser = item as PublishedDocumentFragmentDTO;
if (chooser == null)
return null;
else if (chooser.Children.Length == 0)
{
return _routineDataTemplate;
}
else
{
return _folderDataTemplate;
}
}
}
}
And an example of one of my custom ViewCells. I think this is where I am wrong, but I am not sure why. I make the properties, but I do not know how to set them properly.
using Xamarin.Forms;
namespace MyApp.Pages.Routines.CustomCells
{
public class RoutineViewCell : ViewCell
{
public static readonly BindableProperty TitleProperty =
BindableProperty.Create("Title", typeof(string), typeof(RoutineViewCell), "");
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
}
}
Thanks for the help :)
I found the answer. I needed to override OnBindingContextChanged() in the custom cell file. My working code looks like this now:
using Xamarin.Forms;
namespace MyApp.Pages.Routines.CustomCells
{
public class RoutineViewCell : ViewCell
{
public static readonly BindableProperty TitleProperty =
BindableProperty.Create("Title", typeof(string), typeof(RoutineViewCell), "");
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
protected override void OnBindingContextChanged()
{
this.SetBinding(TitleProperty, "Title");
base.OnBindingContextChanged();
}
}
}

Resources