In my xamarin.forms application, I have a Listview.The Listview contains images which are binded to photoURL.I have implemented a searchbox at the top of listview.Everything worked fine.But the problem now I facing is whenever I search for anything in list,the item appears.But the image will be gets flicker on each character type.I am getting the data from API that binding to the listview.
Please refer the link : https://gfycat.com/WaterloggedBeneficialGlobefish
My Image binding
<Grid>
<ci1:CircleImage
HeightRequest="200"
Source="empavatar.png"
Aspect="AspectFit">
</ci1:CircleImage>
<ci1:CircleImage
HeightRequest="200"
Source="{Binding PhotoURL}"
Aspect="AspectFit">
</ci1:CircleImage>
</Grid>
Iam using circular imageview and a template image when ImageURL is null.
My Search
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(e.NewTextValue))
{
EmployeeListView.ItemsSource = resultObjForEmployee;
}
else
{
EmployeeListView.ItemsSource = resultObjForEmployee.Where(x => x.Name.ToLower().StartsWith(e.NewTextValue));
}
}
resultObjForEmployee is the result that I getting from json.
Please help me to recover this problem.
Try setting the CachingStrategy of Listview as "RecycleElement"
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<ci1:CircleImage
HeightRequest="200"
Source="empavatar.png"
Aspect="AspectFit"></ci1:CircleImage>
<ci1:CircleImage
HeightRequest="200"
Source="{Binding PhotoURL}"
Aspect="AspectFit"></ci1:CircleImage>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Related
I'm getting the error System.InvalidOperationException: 'Cannot convert "VerticalGrid, 2" into Xamarin.Forms.IItemsLayout' during InitalizeComponent() of my ContentPage containing a CollectionsView.
The code works properly on UWP, and the error occurs when running on Android. Being new to xamarin.forms, I'm not really sure what to start looking for.
EDIT: it works if I choose "VerticalList".
Here's a bit of my xaml:
<ContentPage ... >
<StackLayout>
<CollectionView
x:Name="DetailGrid"
ItemsLayout="VerticalGrid, 2" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="20" BackgroundColor="Crimson">
<Image Source= "{Binding Path}" WidthRequest="300"/>
<Label Text="{Binding FileName}" TextColor ="Bisque" LineBreakMode="WordWrap" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
And here's some code-behind:
namespace varlist
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CollectionPage : ContentPage
{
public ObservableCollection<NodeData> nodes = new ObservableCollection<NodeData>();
public CollectionPage ()
{
InitializeComponent ();
nodes.Add (new ImageData { FileName = "image_chair_pk.jpg" });
...
DetailGrid.ItemsSource = nodes;
}
}
}
I got the same error with you when I use a Xamarin.forms version 4.5.0.725.
After I update my Xamarin.forms to the latest version 4.8.0.1687, everything works well.
Please update your Xamarin.forms version to fix this problem:
I found a workaround. This appears to be a framework bug.
According to Xamarin.Forms CollectionView Layout the syntax I used should work - and it does work for UWP.
They also show an alternate declaration to specify ItemsLayout:
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" />
</CollectionView.ItemsLayout>
And, this syntax works on Android.
I filed a bug on github:[Bug] CollectionView with ItemsLayout=VerticalGrid crashes on Android #12920
You can omit this property if you wish it vertical, the default is set to VerticalList. If you want your collectionView to be Horizontal, just use HorizontalList.
See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout
I am using the scrollView and a stackLayout in it. Adding the View dynamically to the stackLayout. I am getting the following exception when I am trying to remove the View added:
The calling thread cannot access this object because a different
thread owns it.
My code is as following
<ScrollView Orientation="Vertical" VerticalOptions="FillAndExpand"
x:Name="MessagesScrollView">
<StackLayout Padding="7" x:Name="MessagesStackLayout"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
</StackLayout>
</ScrollView>
Add the code behind is:
MessagesStackLayout.Children.Clear();
foreach (var chat in messagesList)
{
MessagesStackLayout.Children.Add(new CustomViewCell(chat));
}
I have a feeling it is not running the code on MainThread try the following and see if that works for you:
Device.BeginInvokeOnMainThread(() =>
{
MessagesStackLayout.Children.Add(new CustomViewCell(chat));
});
I am currently working on a UWP project and I am trying to bind some command from the current page to an usercontrol.
While every other properties seems to be correctly bound, the command is not and thus is not working.
Do you have any idea from where it might come ?
Here is my code (main page):
<Grid Visibility="{Binding LoginStep, Converter={StaticResource LogConverter}, ConverterParameter='InputPin'}"
Height="450px" Width="280px">
<PIN:PinInput Pin="{Binding CurrentUser.Pin, Mode=TwoWay}" x:Uid="btnPin" SubmitCommand="{Binding AddPinCommand, Mode=TwoWay}"></PIN:PinInput>
</Grid>
View model:
private ICommand _addPinCommand;
public ICommand AddPinCommand
{
get
{
return _addPinCommand ?? (_addPinCommand = new CommandBase((o) =>
{
this.LoginStep = LoginStep.ChoseCompany;
}));
}
}
User control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Style="{StaticResource btnStyle}" Command="{Binding SubmitCommand, Mode=TwoWay}">
<StackPanel>
<Border Style="{StaticResource btnInnerStyle}" Width="100px" CornerRadius="10">
<TextBlock Text="{Binding SubmitButtonText}" Style="{StaticResource btnText}"></TextBlock>
</Border>
</StackPanel>
</Button>
</Grid>
public static readonly DependencyProperty submitCommandProperty = DependencyProperty.Register("SubmitCommand", typeof(ICommand), typeof(PinInput), null);
public ICommand SubmitCommand
{
get
{
return (ICommand)GetValue(submitCommandProperty);
}
set
{
SetValue(submitCommandProperty, value);
}
}
Note:
in the error log I have:
Error: BindingExpression path error: 'AddPinCommand' property not found on 'PAT.Mobile.UTrakk.UWP.Views.Components.PinInput'. BindingExpression: Path='AddPinCommand' DataItem='PAT.Mobile.UTrakk.UWP.Views.Components.PinInput'; target element is 'PAT.Mobile.UTrakk.UWP.Views.Components.PinInput' (Name='null'); target property is 'SubmitCommand' (type 'ICommand')
Thanks in advance,
Thomas K. T.
Ok this one was weird but here is what seems to do the trick:
<PIN:PinInput SubmitCommand="{Binding DataContext.AddPinCommand, ElementName=curPage}" Pin="{Binding CurrentUser.Pin, Mode=TwoWay}" x:Uid="btnPin" ></PIN:PinInput>
With "curPage" = name of the current page.
I do not fully understand why I have to do this, since CurrentUser.Pin is working perfectly fine without this work around.
Can someone explain ?
I have a ListView bind to list of BitmapImage.
I want to get the Index of current image in focus when I scroll thru this list.
But, I notice that ItemAppearing property is not there in UWP but it is there in Xamarin Forms.
How can I get the index of the current item in view?
Thanks!
<ScrollViewer Grid.Row="0" ZoomMode="{x:Bind ZoomMode, Mode=OneWay}" HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Auto">
<ListView HorizontalAlignment="Center" ItemsSource="{x:Bind ImagePages, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="BitmapImage">
<Image Source="{x:Bind }" Margin="0 2" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
</ScrollViewer>
For starters, the ItemAppearing property is not the behavior you are looking for. The ItemAppearing event for the ListView in Xamarin Forms is fired when the list item is rendered. For a small list this event will be fired for all items immediately. The equivalent event in UWP is ListView.ChoosingItemContainer event which like the ItemAppearing event, unless the ListView is virtualized is fired for all items in the list. Even for a large virtualized list, it is fired for several pages of items.
This is not what you want. As I understand it, you want to know the image that is visible at the top of the list view when the list is scrolled. Here is how to do that.
First of all. Get rid of the ScrollViewer. The ListView already has a ScrollViewer inside of it.
<ListView x:Name="listViewImage" Grid.Row="0" HorizontalAlignment="Center" ItemsSource="{x:Bind ImagePages, Mode=OneWay}"
Loaded="listViewImage_Loaded">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="BitmapImage">
<Image Source="{x:Bind }" Margin="0 2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
Note that I have named the ListView and I have added a Loaded event handler. In this handler, find the ScrollViewer inside the ListView and attach a handler to the ViewChanged event.
private void listViewImage_Loaded(object sender, RoutedEventArgs e)
{
Border b = VisualTreeHelper.GetChild(listViewImage, 0) as Border;
ScrollViewer sv = b.Child as ScrollViewer;
sv.ViewChanged += Sv_ViewChanged;
}
In the view changed handler, find the first visible ListViewItem and get its index in the collection. This is what you want.
private void Sv_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer sv = sender as ScrollViewer;
GeneralTransform gt = sv.TransformToVisual(this);
Point p = gt.TransformPoint(new Point(0, 0));
List<UIElement> list = new List<UIElement>(VisualTreeHelper.FindElementsInHostCoordinates(p, sv));
ListViewItem item = list.OfType<ListViewItem>().FirstOrDefault();
if(item != null)
{
int index = listViewImage.IndexFromContainer(item);
Debug.WriteLine("Visible item at top of list is " + index);
}
}
I have tried to detect the scrolling direction of the list view. My requirement is need to implement different functionality while list view scrolling up and scrolling down. Please suggest any idea for detecting list view scrolling direction. I have tried below syntax in my list view.
Sample code:
<StackLayout>
<Label x:Name="Direction" />
<ListView ItemsSource="{Binding Items}" HasUnevenRows = "true" ItemAppearing="Handle_ItemAppearing" IsPullToRefreshEnabled = "true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text = "{Binding}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
I don't think you can do it by default, you can only act on a item that is appearing or disappearing. So, you either need to work with that by creating some code which gets the index of (dis)appearing items and and see if the indexes are getting higher or lower to determine whether someone is scrolling up or down. Or you need to hook up a custom renderer, but I'm not sure the native controls have anything to detect this either.
I've whipped up a very basic example for you, you can find the full code here.
Basically hook into the event available, keep track of the last index in a class variable and compare it to the current index of the item that is appearing.
private void Handle_ItemAppearing (object sender, Xamarin.Forms.ItemVisibilityEventArgs e)
{
var currentIdx = Items.IndexOf ((string)e.Item);
if (currentIdx > _lastItemAppearedIdx)
Direction.Text = "Up";
else
Direction.Text = "Down";
_lastItemAppearedIdx = Items.IndexOf ((string)e.Item);
}
In this code I simply show it in a Label, but of course you can create some enum to return or fire an event or something to make the code some more reusable. Here is the code in action:
Recently came through this problem and fixed it this way:
<StackLayout>
<Label x:Name="Direction" />
<ListView ItemsSource="{Binding Items}" HasUnevenRows = "true" ItemAppearing="Handle_ItemAppearing" ItemDisappearing="Handle_ItemDisappearing" IsPullToRefreshEnabled = "true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text = "{Binding}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
string ScrollingDirection;
int visibleTabIndex;
int disappearingTabIndex;
public async void Handle_ItemAppearing(object sender, ItemVisibilityEventArgs e)
{
var visibleTab = e.Item;
visibleTabIndex = MyItemsList.IndexOf(visibleTab);
if (disappearingTabIndex > visibleTabIndex) ScrollingDirection = "DOWN";
else ScrollingDirection = "UP";
}
public async void Handle_ItemDisappearing(object sender, ItemVisibilityEventArgs e)
{
var invisibleTab = e.Item as TicketsList;
disappearingTabIndex = tvm.Tickets.IndexOf(invisibleTab);
}