There are two listviews, one of them is grouped and other is not. I have to drag items from one list and drop into other. The drag and drop should be visual like item should actually be seen dragging and dropping on the UI. After some search, I found this tutorial TouchTrackingEffect Demos and thought to apply the same logic to Listview controls. I have somehow managed to apply the touch effects to the ListViews and it is firing events such as pressed, moved and released. I also managed to add items to the grouped listview. However, it is not showing dragging and dropping on the screen. I am assuming, I am doing something wrong but I am new to Xamarin so trying my best to get it working. The attached images show the UI, 2 shows the startup screen, 3 shows the X and Y coordinates in "Moved" effect, and 4 shows "New Word" is added in Grouped ListView on Released event. Below is the code, I would appreciate if someone can help me in this regard.
Page2Grid.Xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TouchTrackingEffect.Models;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TouchTracking;
using SkiaSharp;
using SkiaSharp.Views.Forms;
namespace TouchTrackingEffect
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page2Grid : ContentPage
{
//public ObservableCollection<GroupedStructureModel> grouped { get; set; }
ObservableCollection<GroupedStructureModel> grouped = new ObservableCollection<GroupedStructureModel>();
public ObservableCollection<GroupedStructureModel> grouped1 { get { return grouped; } }
// Drag and Drop Class
class DragInfo
{
public DragInfo(long id, Point pressPoint, AbsoluteLayout absoluteLayout)
{
Id = id;
PressPoint = pressPoint;
absoluteLayoutID = absoluteLayout;
}
public long Id { private set; get; }
public AbsoluteLayout absoluteLayoutID { set; get; }
public Point PressPoint { private set; get; }
}
// dictionary for ListViews
Dictionary<ListView, DragInfo> lstStructDragDictionary = new Dictionary<ListView, DragInfo>();
Dictionary<ListView, DragInfo> lstWordsDragDictionary = new Dictionary<ListView, DragInfo>();
Random random = new Random();
public Page2Grid()
{
InitializeComponent();
populateRhymeListView();
// adding effects to ListViews
// Main List
TouchEffect touchEffect1 = new TouchEffect();
touchEffect1.TouchAction += OnTouchEffectAction;
lstViewMain.Effects.Add(touchEffect1);
// Word List
TouchEffect touchEffect2 = new TouchEffect();
touchEffect2.TouchAction += OnTouchEffectAction;
lstViewWords.Effects.Add(touchEffect2);
}
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
lstViewWords = sender as ListView;
switch (args.Type)
{
case TouchActionType.Pressed:
// don't allow a second touch on an already touched boxview
if (!lstWordsDragDictionary.ContainsKey(lstViewWords))
{
lstWordsDragDictionary.Add(lstViewWords, new DragInfo(args.Id, args.Location, absLayout));
// set capture property to true
TouchEffect toucheffect = (TouchEffect)lstViewWords.Effects.FirstOrDefault(e => e is TouchEffect);
toucheffect.Capture = true;
}
break;
case TouchActionType.Moved:
if (lstWordsDragDictionary.ContainsKey(lstViewWords) && lstWordsDragDictionary[lstViewWords].Id == args.Id)
{
//lstViewMain.TranslateTo
headerLbl.Text = " listWords is moved";
Rectangle rect = AbsoluteLayout.GetLayoutBounds(lstViewWords);
Point initialLocation = lstWordsDragDictionary[lstViewWords].PressPoint;
rect.X += args.Location.X - initialLocation.X;
rect.Y += args.Location.Y - initialLocation.Y;
AbsoluteLayout.SetLayoutBounds(lstViewWords, rect);
headerLbl.Text = "X = " + args.Location.X + " Y = " + args.Location.Y;
}
break;
case TouchActionType.Released:
if (lstWordsDragDictionary.ContainsKey(lstViewWords) && lstWordsDragDictionary[lstViewWords].Id == args.Id)
{
grouped1.FirstOrDefault().Add(new StructureModel { word = "New Word" });
}
break;
}
}
// Create a Grouped ListView from a Rhyme Description
void populateRhymeListView()
{
// defining two lines
GroupedStructureModel list1Group = new GroupedStructureModel() { LongName = "Line1" };
GroupedStructureModel list2Group = new GroupedStructureModel() { LongName = "Line2" };
// words for List 1
//List<WordList> wordLst1 = new List<WordList>();
list1Group.Add(new StructureModel { word = "first"});
// words for List
list2Group.Add(new StructureModel { word = "second1" });
grouped1.Add(list1Group);
grouped1.Add(list2Group);
// binding list itemsource
lstViewMain.ItemsSource = grouped1;
}
async void BtnNext_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new Page3());
}
}
}
****Page2Grid.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="TouchTrackingEffect.Page2Grid">
<ContentPage.Content>
<AbsoluteLayout x:Name="absLayout">
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
<Grid x:Name="gridLayout">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="300"></ColumnDefinition>
<ColumnDefinition Width="300"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label x:Name="headerLbl" Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" />
<Button x:Name="btnNext" Grid.Row="0" Grid.Column="2" Text="Next" Clicked="BtnNext_Clicked"></Button>
<!--Main ListView containing structure -->
<ListView x:Name ="lstViewMain" Grid.Row="1" Grid.Column="0" IsGroupingEnabled="true" GroupDisplayBinding="{Binding LongName}" WidthRequest="200" BackgroundColor="Azure">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label x:Name="lblItem" Text="{Binding word}"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--2nd Column-->
<BoxView x:Name="test" Grid.Row="1" Grid.Column="1"></BoxView>
<!-- Word List Views-->
<ListView x:Name="lstViewWords" Grid.Row="1" Grid.Column="2" BackgroundColor="AliceBlue">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</Grid>
</StackLayout>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
Related
I cannot get the image buttons to move the carousel image to the next in the list. when I click the imagebutton and step through the code the Position is changing properly, but does not move the carousel in the view. Any help would be greatly appreciated. Here is the code for both my View and View Model:
<?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="Das_Local.Views.MainPage"
xmlns:vm="clr-namespace:Das_Local.ViewModels"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:MainViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<StackLayout VerticalOptions="Start">
<CarouselView ItemsSource="{Binding Images}" HorizontalOptions="FillAndExpand" HeightRequest="375">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageUrl}" Aspect="AspectFill" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</StackLayout>
<StackLayout>
<Label Text="Whats New in the Local" FontSize="Title" HorizontalTextAlignment="Center" Margin="0,10,0,-10"/>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<CarouselView Grid.Row="1" HorizontalOptions="FillAndExpand" ItemsSource="{Binding Articles}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True">
<StackLayout Margin="0,0">
<Label Text="{Binding ArticleTitle}" FontAttributes="Bold" FontSize="Title"/>
<Label Text="{Binding ArticleTextBody}" FontSize="Body" />
</StackLayout>
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<ImageButton Grid.Row="1" Source="left.png" Aspect="Fill" HorizontalOptions="Start" VerticalOptions="Center" BackgroundColor="Transparent"
HeightRequest="35" WidthRequest="35" Command="{Binding ChangePositionCommand}" CommandParameter="L"/>
<ImageButton Grid.Row="1" Source="right.png" Aspect="Fill" HorizontalOptions="End" VerticalOptions="Center" BackgroundColor="Transparent"
HeightRequest="35" WidthRequest="35" Command="{Binding ChangePositionCommand}" CommandParameter="R"/>
</Grid>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
--------------------------------------------------------------------------------------------------
using System.Collections.ObjectModel;
using System.Windows.Input;
using Das_Local.Models;
using Xamarin.Forms;
namespace Das_Local.ViewModels
{
class MainViewModel : BaseViewModel
{
public MainViewModel()
{
Title = "SMART 29";
images = GetCarouselImages();
articles = GetCarouselArticles();
ChangePositionCommand = new Command(ChangePosition);
}
private ObservableCollection<CarouselImages> images;
public ObservableCollection<CarouselImages> Images
{
get { return images; }
set
{
images = value;
OnPropertyChange();
}
}
private ObservableCollection<CarouselImages> GetCarouselImages()
{
return new ObservableCollection<CarouselImages>
{
new CarouselImages { ImageUrl= "anglewrappedduct.jpg" },
new CarouselImages { ImageUrl = "ductrun.jpg" },
new CarouselImages { ImageUrl = "offset.jpg" },
new CarouselImages { ImageUrl = "panels.jpg" }
};
}
ObservableCollection<CarouselArticles> articles;
public ObservableCollection<CarouselArticles> Articles
{
get { return articles; }
set
{
articles = value;
OnPropertyChange();
}
}
private ObservableCollection<CarouselArticles> GetCarouselArticles()
{
return new ObservableCollection<CarouselArticles>
{
new CarouselArticles { ArticleTitle= "This Title", ArticleTextBody = "Body of the article will go here"},
new CarouselArticles { ArticleTitle = "Another Title", ArticleTextBody = "Just what I need more text for the article body" },
new CarouselArticles { ArticleTitle = "My Third Title", ArticleTextBody = "I am getting burned out on writing random things"},
new CarouselArticles { ArticleTitle = "Bravo", ArticleTextBody = "whats the deal with these free channels"}
};
}
public ICommand ChangePositionCommand { get; set; }
private CarouselArticles selectedarticle;
public CarouselArticles SelectedArticle
{
get { return selectedarticle; }
set
{
selectedarticle = value;
OnPropertyChange();
}
}
private int position;
public int Position
{
get { return position; }
set
{
position = value;
selectedarticle = articles[position];
OnPropertyChange(nameof(SelectedArticle));
}
}
private void ChangePosition(object obj)
{
string direction = (string)obj;
if (direction == "L")
{
if (position == 0)
{
Position = articles.Count - 1;
return;
}
Position -= 1;
}
else if (direction == "R")
{
if (position == articles.Count - 1)
{
Position = 0;
return;
}
Position += 1;
}
}
}
}
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!!
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.
I have custom content view with a Label as title and another Label as detail and an edit Icon ; when the icon is clicked detail label is converted to Entry to make changes and the changes are carried over to binding.
I have bound multiple of these custom views to different properties of same object and trying to edit each one and move to next one, the problem is it seems to duplicate the individual views
I have also put x:Name but still it duplicates same value to the views above it ..
Just the edit of Lastname
Now if I move to 3rd view and edit it , it replicates new value to all previously edited values. - for lastname in this case which is weird considering its not same view used in the page and on debug it hits the method only once.
Custom content view:
<StackLayout Orientation="Horizontal"
VerticalOptions="Start"
Padding="25,10,25,10">
<StackLayout x:Name="stackLayoutDetail"
HorizontalOptions="FillAndExpand">
<Label x:Name="title"
Text="{Binding Title}" />
<Label x:Name="detail"
Text="{Binding Detail}"
FontSize="Large"
FontAttributes="Bold" />
</StackLayout>
<Image x:Name="editIcon"
Source="edit_icon.png"
WidthRequest="25"
HeightRequest="25"
IsVisible="{Binding EditIconVisible}">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="EditIcon_Clicked" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
Code behind:
private static Entry newEntry = new Entry();
public static readonly BindableProperty DetailProperty = BindableProperty.Create(propertyName: nameof(Detail),
returnType: typeof(string),
declaringType: typeof(LabelledEntrywithIcon),
defaultValue: default(string));
public string Detail
{
get
{
return (string)GetValue(DetailProperty);
}
set => SetValue(DetailProperty, value);
}
private void EditIcon_Clicked(object sender, System.EventArgs e)
{
detailLabel = (Label)stackLayoutDetail.Children[1];
stackLayoutDetail.Children.RemoveAt(1);
newEntry.Text = Detail;
stackLayoutDetail.Children.Add(newEntry);
editIcon.IsVisible = false;
newEntry.Completed += NewEntry_Completed;
}
private void NewEntry_Completed(object sender, System.EventArgs e)
{
try
{
var _newText = newEntry.Text;
detailLabel.Text = _newText;
stackLayoutDetail.Children.RemoveAt(1);
stackLayoutDetail.Children.Add(detailLabel);
Detail = _newText;
editIcon.IsVisible = true;
}
catch (System.Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Page
<local:LabelledEntrywithIcon x:Name="firstName"
Title="First Name"
Detail="{Binding Fella.FirstName}" />
<local:LabelledEntrywithIcon x:Name="lastname"
Title="Last Name"
Detail="{Binding Fella.LastName}" />
<local:LabelledEntrywithIcon x:Name="gender"
Title="Gender"
Detail="{Binding Fella.Gender}" />
Code behind:
ViewModel=new MainViewModel();
BindingContext = ViewModel;
Complete code to test is at Github repo : https://github.com/pmahend1/CustomViewDuplicationIssue
Strange but I changed a line of code and it works as expected now.
On the class variables changed private static Entry newEntry= new Entry(); to
private static Entry newEntry;
in EditIcon_Clicked method instead of newEntry.Text = Detail; used
newEntry = new Entry { Text = Detail };
I am not sure why it was taking same reference even though its new Entry for each LabelledEntrywithIcon
Instead of creating a new entry and finding and removing the label and adding the new entry after, you could simplify your problem by:
<StackLayout Orientation="Horizontal"
VerticalOptions="Start"
Padding="25,10,25,10">
<StackLayout x:Name="stackLayoutDetail"
HorizontalOptions="FillAndExpand">
<Label x:Name="title"
Text="{Binding Title}" />
<Label x:Name="detail"
Text="{Binding Detail}"
IsVisible="{Binding ShowLabel}"
FontSize="Large"
FontAttributes="Bold" />
<Entry ... IsVisible="{Binding ShowEntry}" ... />
</StackLayout>
<Image x:Name="editIcon"
Source="edit_icon.png"
WidthRequest="25"
HeightRequest="25"
IsVisible="{Binding ShowLabel}">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="EditIcon_Clicked" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
Note that I intentionally wrote ... inside the entry element as Placeholder for all customizations you might want do there (font size, etc...).
Now you add two BindablyProperties (type bool) ShowEntry and ShowLabel, where ShowLabel defaults to true and ShowEntry defaults to false.
Now all you have to do is to adapt your EditIcon_Clicked Event:
private void EditIcon_Clicked(object sender, System.EventArgs e)
{
ShowLabel = false;
ShowEntry = true;
newEntry.Text = Detail;
newEntry.Completed += NewEntry_Completed;
}
And adapt NewEntry_Completed to
private void NewEntry_Completed(object sender, System.EventArgs e)
{
try
{
var _newText = newEntry.Text;
detailLabel.Text = _newText;
ShowLabel = true;
ShowEntry = false;
Detail = _newText;
}
catch (System.Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Basically this does the same as your solution, however you save yourself from having to push UI Items around in your codebehind and especially the bugs and errors coming with it.
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);
}
}