While learning how to use Xamarin Forms and StackLayouts I'm having trouble rendering the items.
I'm trying to bind a collection of somethings to a StackLayout. The binding (more or less) works. The visual render is wrong.
For this question, I've picked some random thing (in this case, credit cards) to highlight my experimenting.
This is what I'm rending:
Notice how each custom row looks to be fixed height's or something?
Show me the code! Ok, so this is what I've done:
First, the page:
public class MyPage : ContentPage
{
protected override void OnAppearing()
{
var creditCards = new SomeFakeListOfCreditCards();
var stackLayout = new CreditCardStackLayout(creditCards);
Contet = stackLayout;
}
}
Now for the custom control...
public class CreditCardStackLayout : StackLayout
{
public CreditCardStackLayout(IEnumerable<CreditCard> creditCards)
{
// Note: credit cards can be null (because we don't have any).
CreateContent(creditCards);
}
private void CreateContent(IEnumerable<CreditCard> creditCards)
{
VerticalOptions = LayoutOptions.Center;
Children.Add(TitleLabel);
if (creditCards == null)
{
Children.Add(NoCreditCardLabel);
}
else
{
Children.Add(CreateCreateCardList(creditCards));
}
}
private static ListView CreateCreateCardList(IEnumerable<CreditCard> creditCards)
{
if (creditCards == null)
{
throw new ArgumentNullException("creditCards");
}
// Project the credit cards into a list view model.
var viewModel = creditCards.Select(x => new CreditCardViewModel(x));
return new ListView
{
ItemsSource = viewModel,
ItemTemplate = new DataTemplate(typeof(CreditCardViewCell))
};
}
private Label TitleLabel
{
get
{
return new Label
{
HorizontalOptions = LayoutOptions.Center,
Text = "A list of credit cards"
};
}
}
private Label NoCreditCardLabel
{
get
{
return new Label
{
Text = "You have no real credit cards on file."
};
}
}
}
So far so good.. Now for the binding..
public class CreditCardViewCell : ViewCell
{
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var stackLayout = new StackLayout
{
BackgroundColor = new Color(46, 81, 163),
VerticalOptions = LayoutOptions.Center,
Children =
{
NameLabel,
TypeImage,
NumberLabel,
ExpiresOnLabel,
RemoveButton
}
};
View = stackLayout;
}
private Label NameLabel
{
get
{
var label = new Label
{
FontSize = 14
};
label.SetBinding(Label.TextProperty, "Name");
return label;
}
}
private Label NumberLabel
{
get
{
var label = new Label
{
FontSize = 14
};
label.SetBinding(Label.TextProperty, "Number");
return label;
}
}
private Label ExpiresOnLabel
{
get
{
var label = new Label
{
FontSize = 14
};
label.SetBinding(Label.TextProperty, "ExpiresOn");
return label;
}
}
private Image TypeImage
{
get
{
var creditCardModel = (CreditCardViewModel)BindingContext;
string fileName = null;
switch (creditCardModel.Type)
{
case "Visa":
fileName = "visa.png";
break;
default:
fileName = "mastercard.png";
break;
}
var image = new Image
{
Source = ImageSource.FromFile("Icons/" + fileName)
};
return image;
}
}
private Button RemoveButton
{
get
{
var button = new Button
{
//Image = "Icons/remove.png"
Text = "Remove"
};
button.Clicked += async (object sender, EventArgs e) =>
{
// TODO when I figure out how to do this stuff.
int i = 0;
i++;
};
return button;
}
}
}
}
Also, please do not suggest going to a XAML file, I'm trying to learn this stuff programmatically.
Side thought: I wish I could turn on borders so I can see how things are laid out.
Use ListView's HasUnevenRows property. Change this:
return new ListView
{
ItemsSource = viewModel,
ItemTemplate = new DataTemplate(typeof(CreditCardViewCell))
};
to:
return new ListView
{
HasUnevenRows = true,
ItemsSource = viewModel,
ItemTemplate = new DataTemplate(typeof(CreditCardViewCell))
};
By default Xamarin.Forms ListView row size is fixed and set to the same value for every row (RowHeight property). With this configuration row auto-sizing won't work, but it also gives best ListView performance.
You can auto-size rows by setting ListView's HasUnevenRows property to true. It allows modifying ViewCell size by setting ViewCell's RowHeight property (eg. in OnBindingContextChanged override). In newer Xamarin.Forms versions it also sizes ViewCells automatically (on older it worked only on Android).
Related
I have a DataGrid added programmatically in my App. I have a event which is called when the MainDisplayInfo gets changed. In this event I call my function to create the grid.
Grid grid = this.dataGrid.Children[0] as Grid; // the header
ContentView view = this.dataGrid.Children[1] as ContentView;
ListView lv = this.dataGrid.Children[2] as ListView; // the body of the datagrid
lv.ItemsSource = null;
lv.ItemsSource = viewModel.Statistiks;
I'm adding my Header and my Columns like this:
foreach (var item in zip)
{
if (IsPortrait)
{
cc.Add(new DataGridColumn() { Title = item.Headers, PropertyName = item.Tables, Width = GridLength.Star, HorizontalContentAlignment = LayoutOptions.CenterAndExpand });
}
else
cc.Add(new DataGridColumn() { Title = item.Headers, PropertyName = item.Tables, Width = GridLength.Star, HorizontalContentAlignment = LayoutOptions.CenterAndExpand });
}
My Itemssource Collection looks like this:
private ObservableCollection<StatistikModel> statistiks;
public ObservableCollection<StatistikModel> Statistiks
{
get { return statistiks; }
set { statistiks = value; OnPropertyChanged("Statistiks"); }
}
But when the orientation gets changed only the Header changes correctly but not the listview.
Hi guys I have a listview that I have made in Xamarin but not using Xamarin Forms. when I tap on a selected item it turns orange by default but I want to show a DisplayAlert when I tap on it. does anybody know how to solve this? below is my list view in C#
public string PhoneNumber;
StackLayout stackLayout = new StackLayout();
_listView = new ListView
{
ItemTemplate = new DataTemplate(() =>
{
Label nameLabel = new Label();
nameLabel.SetBinding(Label.TextProperty, "Name");
nameLabel.FontSize = 20;
Label addressLabel = new Label();
addressLabel.SetBinding(Label.TextProperty, "Address");
Label phoneLabel = new Label();
phoneLabel.SetBinding(Label.TextProperty, "Phone");
PhoneNumber = phoneLabel.Text;
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(20, 5),
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Spacing = 0,
Children =
{
nameLabel,
addressLabel,
phoneLabel,
}
}
}
}
};
})
};
_listView.HasUnevenRows = true;
_listView.ItemsSource = db.Table<Company>().OrderBy(x => x.Name).ToList();
stackLayout.Children.Add(_listView);
_listView.ItemTapped += _listView_ItemTapped;
Content = stackLayout;
}
private void _listView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var phoneDialer = CrossMessaging.Current.PhoneDialer;
if (phoneDialer.CanMakePhoneCall)
phoneDialer.MakePhoneCall("11112222");
DisplayAlert("Phone Dial","Dialing" + e.Item.PhoneNumber, "Ok");
}
Just create ItemTapped method
_listView.ItemTapped += _listView_ItemTapped;
And implement it
private void _listView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var item = e.Item as Company; //Company is your model class
DisplayAlert("Alert", item.Name, "Cancel");
}
Output screen
I have an ObservableCollection with a list of users' data which is wrapped for multiselect page.
I added SearchBar to the multiselect page but I cannot make it work.
Code
public class WrappedItemSelectionTemplate : ViewCell
{
public WrappedItemSelectionTemplate() : base()
{
Label Title = new Label() { TextColor = Color.Black };
Title.SetBinding(Label.TextProperty, new Binding("Item.Title"));
Label Email = new Label() { FontSize = 14 };
Email.SetBinding(Label.TextProperty, new Binding("Item.Email"));
Switch mainSwitch = new Switch() { HorizontalOptions = LayoutOptions.End };
mainSwitch.SetBinding(Switch.IsToggledProperty, new Binding("IsSelected"));
StackLayout Stack = new StackLayout();
Stack.Children.Add(Title);
Stack.Children.Add(Email);
Grid grid = new Grid();
grid.Children.Add(Stack, 0,0);
grid.Children.Add(Email, 0, 1);
grid.Children.Add(mainSwitch, 1, 0);
View = grid;
}
}
public List<WrappedSelection<T>> WrappedItems = new List<WrappedSelection<T>>();
public SelectMultipleBasePage(List<T> items)
{
WrappedItems = items.Select(item => new WrappedSelection<T>() { Item = item, IsSelected = false }).ToList();
ListView mainList = new ListView()
{
ItemsSource = WrappedItems,
ItemTemplate = new DataTemplate(typeof(WrappedItemSelectionTemplate)),
};
mainList.ItemSelected += (sender, e) =>
{
if (e.SelectedItem == null) return;
var o = (WrappedSelection<T>)e.SelectedItem;
o.IsSelected = !o.IsSelected;
((ListView)sender).SelectedItem = null; //de-select
};
// SearchBar added
StackLayout Stack = new StackLayout();
SearchBar Search = new SearchBar();
Stack.Children.Add(Search);
Stack.Children.Add(mainList);
Search.TextChanged += (sender, e) =>
{
SearchBar_TextChanged();
};
Content = Stack;
void SearchBar_TextChanged()
{
//string keyword = Search.Text;
//mainList.ItemsSource =
}
}
When I used SearchBar in my cases before I was using new ObservableCollection for ItemsSource of my ListView, it was filtering it as I required.
But now am stuck and don't know how I can use the SearchBar for the field Title in that case with WrappedItems?
Although I prefer to see the code in Xaml and bound to VM with MVVM binding to make it easier to read . But I think I got what you need.
What you need to :
Search within WrappedItems and save the found result
Clear the ItemSource of the list view mainList.ItemSource.Clear();
Go through the foundItems and mainList.ItemSource.Add(foundItem);
If the Key is empty string "" then you go through original list and do the same work (i.e. clear the itemsource and add the whole data )
I want to do something like this example, but i can't.
I try a lot of plugins and I cant find the way to implement.
Someone know or can tell me how can I do?
I want to show a display popup menu when click in one ToolbarItem on a MasterDetailPage.
My actual app:
What i want:
I think you can take a look to SlideOverKit
public SlideDownMenuView ()
{
InitializeComponent ();
// You must set HeightRequest in this case
this.HeightRequest = 600;
// You must set IsFullScreen in this case,
// otherwise you need to set WidthRequest,
// just like the QuickInnerMenu sample
this.IsFullScreen = true;
this.MenuOrientations = MenuOrientation.TopToBottom;
// You must set BackgroundColor,
// and you cannot put another layout with background color cover the whole View
// otherwise, it cannot be dragged on Android
this.BackgroundColor = Color.FromHex ("#D8DDE7");
}
Otherwise with enter link description here
you can try with this code...
public partial class App : Application
{
public App()
{
InitializeComponent();
MasterDetailPage mdpage = new MasterDetailPage();
mdpage.Master = new ContentPage() { Title = "Master", BackgroundColor = Color.Red };
ToolbarItem tbi = new ToolbarItem() { Text = "POPUP" };
tbi.Clicked += async (object sender, System.EventArgs e) => {
StackLayout sl = new StackLayout() { HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Start, BackgroundColor = Color.Pink, WidthRequest = 100, HeightRequest = 200 };
Rg.Plugins.Popup.Pages.PopupPage mypopup = new Rg.Plugins.Popup.Pages.PopupPage() {BackgroundColor = Color.Transparent };
mypopup.Content = sl;
await MainPage.Navigation.PushPopupAsync(mypopup);
};
ContentPage detail = new ContentPage() { Title = "Detail", BackgroundColor = Color.Green, };
detail.ToolbarItems.Add(tbi);
mdpage.Detail = new NavigationPage(detail);
MainPage = mdpage;
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
I have a multiple ViewCell and ImageCell subclasses inside a TableView.
I would like to execute some code when the user taps the cell. The whole cell highlights whenever it's touched, but I don't see any event handler to use here.
Isn't there a XAML Tapped equivalent for this but in code only?
private void SetTableView()
{
Content = new TableView
{
HasUnevenRows = true,
Intent = TableIntent.Menu,
Root = new TableRoot()
{
new TableSection()
{
new ProfileCell(ImageSource.FromFile("profile_placeholder.png"), "Casa de Férias")
},
new TableSection()
{
new InfoCell()
{
Type = InfoCellTypes.Pending,
Text = "Avaliação do Imóvel",
Detail = "Estado do Processo"
}
}
}
};
}
I'm sure there must be some API that handles this. Maybe I'm just not looking in the right place?
Thank you!
This can be accomplished by adding a TapGestureRecognizer to the cell's view layout.
var absoluteLayout = new AbsoluteLayout
{
Children =
{
photo,
editImage,
label
}
};
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.NumberOfTapsRequired = 1;
tapGestureRecognizer.Tapped += (s, e) => {
// handle the tap
};
absoluteLayout.GestureRecognizers.Add(tapGestureRecognizer);
View = absoluteLayout;
EDIT:
Or, a better alternative, using the Tapped property of the ViewCell, so it doesn't break the "Tap Animation":
Tapped += new EventHandler((e, s) => {
if (command.CanExecute(null))
{
command.Execute(null);
}
});