Xamarin Forms - How To Use A ListVIew - xamarin.forms

I am trying to populate a listview with a customcell I made. I know how to do it with a tableview, but i have no idea how to do it with a listview. Can someone explain to me please, thank you

Excuse the very rough snippet of code but it is merely to explain the concept behind listviews and databinding.
In my example below I create a listview, assign a custom viewCell and bind the view cell to a model. The model data is populated by XML data retrieved from the server(Code emitted for simplicity). Simpler implementations can be done should you require a listview with hardcoded data.
I suggest reading through the http://developer.xamarin.com/guides/cross-platform/xamarin-forms/introduction-to-xamarin-forms/ and researching the MVVM principle
Additionally the best example code I have found to go through is an enlightening application written by James Montemagno https://github.com/jamesmontemagno/Hanselman.Forms
public TestPage()
{
private ListView listView; // Create a private property with the Type of Listview named listview
listView = new ListView(); //Instantiate the listview
var viewTemplate = new DataTemplate(typeof(CustomViewCell)); //Create a variable for the custom data template
listView.ItemTemplate = viewTemplate; // set the data template to the variable created above
this.Content = new StackLayout()
{
Children = {
listView
}
};
}
Custom View Cell:
public class CustomViewCell : ViewCell
{
public CustomViewCell()
{
Label Test = new Label()
{
Font = Font.BoldSystemFontOfSize(17),
TextColor = Helpers.Color.AppGreen.ToFormsColor(),
BackgroundColor = Color.White
};
var image = new Image();
image.Source = ImageSource.FromFile("testing.png");
Label Test2 = new Label();
tour_Desc.SetBinding(Label.TextProperty, "Test");
round_Desc.SetBinding(Label.TextProperty, "Test2");
var grid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
RowDefinitions =
{
new RowDefinition {Height = GridLength.Auto },
new RowDefinition {Height = GridLength.Auto },
new RowDefinition {Height = 1}
},
ColumnDefinitions =
{
new ColumnDefinition {Width = GridLength.Auto},
new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)},
new ColumnDefinition {Width = 80 }
}
,BackgroundColor = Color.White,
};
grid.Children.Add(image, 0, 0);
Grid.SetRowSpan(image, 2);
grid.Children.Add(tour_Desc, 1, 0);
Grid.SetColumnSpan(tour_Desc, 2);
grid.Children.Add(Test, 1, 1);
grid.Children.Add(Test2, 1, 2);
this.View = grid;
}
}
View Model:
public partial class GamesResult
{
public string Test { get; set; }
public string Test1 { get; set; }
}

Related

Why rectangle's radius gets ignored in this situation?

I have a Xamarin.Forms (v5.0.0.2515) project.
I have encountered this strange behavior using this scenario:
I have a view with 5 rectangles. RadiusX/RadiusY for each rectangle is 20.
I assign this view to a ContentView and everything looks normal (Fig. 1)
I assign another view to the ContentView
I assign back the view to the ContentView and rounded borders are gone (Fig. 2)
What can cause this issue?
This is a sample code to reproduce the issue.
using Xamarin.Forms;
namespace App
{
public class SamplePage : ContentPage
{
public SamplePage()
{
var grid = new Grid
{
RowDefinitions = new RowDefinitionCollection
{
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Star }
}
};
var btn = new Button { Text = "Replace the view!" };
Grid.SetRow(btn, 0);
grid.Children.Add(btn);
var rect = new Xamarin.Forms.Shapes.Rectangle
{
RadiusX = 20,
RadiusY = 20,
Fill = Brush.Azure,
StrokeThickness = 3,
Stroke = Brush.Black
};
var cv = new ContentView { Content = rect };
Grid.SetRow(cv, 1);
grid.Children.Add(cv);
btn.Clicked += (_, __) =>
{
cv.Content = new Label();
cv.Content = rect;
};
Content = grid;
}
}
}
Thanks.
Fig. 1
Fig. 2
According to your demo you provided, I found that the code cv.Content = new Label(); caused the problem. I changed the demo and you can check.
public partial class MainPage : ContentPage
{
public MainPage()
{
var grid = new Grid
{
RowDefinitions = new RowDefinitionCollection
{
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Star }
}
};
var btn = new Button { Text = "Replace the view!" };
Grid.SetRow(btn, 0);
grid.Children.Add(btn);
var rect = new Xamarin.Forms.Shapes.Rectangle
{
RadiusX = 50,
RadiusY = 10,
Fill = Brush.Azure,
StrokeThickness = 10,
Stroke = Brush.Black
};
var rect2 = new Xamarin.Forms.Shapes.Rectangle
{
RadiusX = 20,
RadiusY = 20,
Fill = Brush.Azure,
StrokeThickness = 3,
Stroke = Brush.Black
};
var cv = new ContentView {Content = rect2};
Grid.SetRow(cv, 1);
grid.Children.Add(cv);
btn.Clicked += (_,__) =>
{
cv.Content = rect;
};
Content = grid;
}
}
}

CollectionView using DataTemplate is not updating ReactiveContentView BindingContext or ViewModel

I'm using ReactiveUI and trying to get a CollectionView to use a ReactiveContentPage as it's item template in code behind. This works okay until the ReactiveContentView is nested under another Element.
The problem I have is that after scrolling the collection view, the items get duplicated. After debugging, it would seem that the ReactiveContentView's BindingContext or ViewModel aren't updated.
The samples below have been simplified to highlight the issue:
This doesn't work:
public partial class SampleCollectionPage : ReactiveContentPage<SampleCollectionViewModel>
{
private void Build()
{
Content = new CollectionView
{
ItemTemplate = new DataTemplate(() =>
{
return new ContentView
{
Content = new ReactiveContentView<CollectionItemViewModel>
{
Content = new StackLayout
{
Children =
{
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Title)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Description)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.SomeTime)),
}
}
}
};
}),
}
.Bind(CollectionView.ItemsSourceProperty, path: nameof(SampleCollectionViewModel.Items));
}
}
This works:
public partial class SampleCollectionPage : ReactiveContentPage<SampleCollectionViewModel>
{
private void Build()
{
Content = new CollectionView
{
ItemTemplate = new DataTemplate(() =>
{
return new ReactiveContentView<CollectionItemViewModel>
{
Content = new StackLayout
{
Children =
{
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Title)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Description)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.SomeTime)),
}
}
};
}),
}
.Bind(CollectionView.ItemsSourceProperty, path: nameof(SampleCollectionViewModel.Items));
}
}
The only difference being that the working version doesn't put the ReactiveContentView in a ContentView. I'm not sure if I'm missing something or whether this is an bug in Xamarin Forms or ReactiveUI?
(edited to add actual problem: doh)

Xamarin.forms - BoxView in Customized ViewCell

I want to set VisibleProperty of BoxView as false for the last element of list which is defined in a viewcell containing stacklayout (grid+boxview). Is there any way to make it false so that last element wont contain boxview separator line?
Solution:
You can add a bool property isShow to your viewModel and use this property to control whether the boxView is visible or not.
public class MyViewModel
{
//use this property to control whether the boxView is visible
public bool isShow { get; set; }
}
And then binding this property to the boxView in your customCell By boxOne.SetBinding(BoxView.IsVisibleProperty, new Binding("isShow")):
public CustomCell()
{
//instantiate each of our views
var grid = new Grid();
var horizontalLayout = grid;
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var boxOne = new BoxView { BackgroundColor = Color.Purple };
var boxTwo = new BoxView { BackgroundColor = Color.AliceBlue };
grid.Children.Add(boxOne, 0, 0);
grid.Children.Add(boxTwo, 1, 1);
//Binding here
boxOne.SetBinding(BoxView.IsVisibleProperty, new Binding("isShow"));
View = horizontalLayout;
}
At last , when you creating a new instance of MyViewModel, you can set the isShow property true/false to control whether the boxView is visible in your cell.
public MainViewCode()
{
myCollection = new ObservableCollection<MyViewModel>();
ListView lstView = new ListView();
lstView.ItemTemplate = new DataTemplate(typeof(CustomCell));
myCollection.Add(new MyViewModel { isShow = true });
myCollection.Add(new MyViewModel { isShow = false });
lstView.ItemsSource = myCollection;
Content = lstView;
}

How to find whether Listview scrolled up or down in xamarin forms?

I need to hide some view, when listview scrolled up and display that view again, when list view scrolled down.
I used
private void ScheduleServiceList_ItemAppearing(object sender, Xamarin.Forms.ItemVisibilityEventArgs e)
{
var currentItem = e.Item as VehicleService;
double listviewheight = ScheduleServiceListView.Height;
if (viewModel.VehicleServiceList[0].Id == currentItem.Id)
{
SearchEntryLayout.IsVisible = true;
HeadingGrid.Margin = new Thickness(0, -70, 0, 0);
}
}
What about:
var listView = new ListView(ListViewCachingStrategy.RecycleElement)
{
....
ItemTemplate = new DataTemplate(() =>
{
return new MyListViewCellView(new List<int> { ... /* ids to disable or other objects */ });
})
};

xamarin binding a button in code to viewModel (without xaml)

I'm using the mvvm approach to develop a barcode scanning app with xamarin. The main hurdle was that the 3rd party scanner object does not work in xaml. I used a ContentPage to create a simple logic-less c# code view which allows me to have a footer with buttons and a logo overlayed at the bottom of the scanner. My problem is that could not find any great best practices for binding items from your code view to your viewModel, as opposed binding a xaml view to a viewModel. Here is some of my view below.
public class BarcodeScannerPage : ContentPage
{
ZXingScannerView zxing;
BarcodeViewModel viewModel;
public BarcodeScannerPage() : base()
{
try
{
viewModel = new BarcodeViewModel();
BindingContext = viewModel;
zxing = new ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Options = new MobileBarcodeScanningOptions
{
TryHarder = true,
DelayBetweenContinuousScans = 3000
},
ScanResultCommand = viewModel.GetResult
};
var cancelButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Cancel",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.CancelButton
};
Binding cancelBinding = new Binding
{
Source = viewModel.CancelIsAvailable,
//Path = "ShowCancel",
Mode = BindingMode.OneWay,
};
cancelButton.SetBinding(IsVisibleProperty, cancelBinding);
var doneButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Done",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.DoneButton
};
Binding doneBinding = new Binding
{
Source = viewModel.DoneIsAvailable,
//Path = "ShowDone",
Mode = BindingMode.OneWay,
};
doneButton.SetBinding(Button.IsVisibleProperty, doneBinding);
When a barcode is scanned my command, GetResultCommand, sends the result to my BarcodeView model. I have created two Bools in my BarcodeView model named isDoneAvailable and isCancelAvailable. I want to bind these values to the Visibility property of the doneButton and cancelButton in my view. Right now the buttons are bound to whatever the bool values are at the creation of BarcodeViewModel, but they DO NOT update. I need to be able to control visibility from the GetResultCommand method of my BarcodeViewModel. Specifically, when a certain number of barcodes are scanned, I want to make the buttons appear and disappear. I have a feeling they don't update because the path is not set, but when I uncomment the path, the binding doesn't work at all. Any ideas what I've done wrong with the bindings of the buttons, or the correct way to set the Path to my bools in the viewModel? Here is some of my BarcodeViewModel code below.
public class BarcodeViewModel : INotifyPropertyChanged
{
public bool CancelIsAvailable { get { return _cancelIsAvailable; } set { _cancelIsAvailable = value; OnPropertyChanged("ShowCancel"); } }
public bool DoneIsAvailable { get { return _doneIsAvailable; } set { _doneIsAvailable = value; OnPropertyChanged("ShowDone"); } }
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
I still would like to know the correct way to get this binding to update but, I was able to work-around this issue by creating a button in my viewModel and referencing it in my view. Then when I dynamically updated the button in my viewModel, it also updated in my view.

Resources