Description
I am using Entry in TableView and displaying the formatted value in focus. In this case virtual keyboard not showing while change focus within Entry.
Steps to Reproduce
Run the sample
Focus the 1st Entry
Focus 2nd Entry
Unfocus both views
Focus 2st Entry. See the keyboard not showing.
Sometimes while unfocus both Entry, keyboard not getting collapsed.
Basic Information
Version with issue: 4.8.0.1451
Platform Target Frameworks:
Android: Nexus 6 Marshmallow 6.0 - API 23 emulator.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EntryWithTableView.MainPage">
<TableView>
<TableRoot>
<TableSection >
<ViewCell >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Entry Keyboard="Numeric" Focused="entry1_Focused" Unfocused="entry1_Unfocused"
x:Name="entry1" Grid.Column="1" WidthRequest="100"/>
<Entry Keyboard="Numeric" Focused="entry1_Focused" Unfocused="entry1_Unfocused"
Grid.Column="2" x:Name="entry2" WidthRequest="100"/>
</Grid>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</ContentPage>
[C#]:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
double value = 5;
private void entry1_Focused(object sender, FocusEventArgs e)
{
Entry entry = sender as Entry;
if(entry != null)
{
entry.Text = value.ToString();
}
}
private void entry1_Unfocused(object sender, FocusEventArgs e)
{
Entry entry = sender as Entry;
if (entry != null)
{
entry.Text = value.ToString("F");
}
}
}
It caused by the TableView . You could use Grid as the root layout directly .
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Entry Keyboard="Numeric" Focused="entry1_Focused" Unfocused="entry1_Unfocused"
x:Name="entry1" Grid.Column="1" WidthRequest="100" />
<Entry Keyboard="Numeric" Focused="entry1_Focused" Unfocused="entry1_Unfocused"
Grid.Column="2" x:Name="entry2" WidthRequest="100"/>
</Grid>
If you want to display a collection , you could use ListView .
Related
I want to set focus on SearchBar when he appears. The problem is that SearchBar is placed inside of popup view and I need to access him from ViewModel.
In standard way I would use
Xamarin.Forms.SearchBar tmp_SearchBar = this.Page.FindByName("fro_SearchBar_NewItem") as Xamarin.Forms.SearchBar;
but that's not working anymore.
Here is XAML
<sfPopup:SfPopupLayout x:Name="fro_Popup_NewItem" Opened="fro_Popup_NewItem_Opened" Grid.Row="1" HorizontalOptions="Center" VerticalOptions="Center" BackgroundColor="Black">
<sfPopup:SfPopupLayout.PopupView>
<sfPopup:PopupView BackgroundColor="Black" WidthRequest ="400" HeightRequest ="100" ShowFooter="False" ShowHeader="False">
<sfPopup:PopupView.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Search bar-->
<Grid Grid.Row="0" HorizontalOptions="Center" VerticalOptions="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<SearchBar x:Name="fro_SearchBar_NewItem"
Grid.Column="0"
Text="{Binding SearchText_Popup, Mode=TwoWay}"
SearchCommand="{Binding SearchCommand}"
Placeholder="Find"
CancelButtonColor="White"
TextColor="White"
PlaceholderColor="Gray"/>
</Grid>
</Grid>
</DataTemplate>
</sfPopup:PopupView.ContentTemplate>
</sfPopup:PopupView>
</sfPopup:SfPopupLayout.PopupView>
</sfPopup:SfPopupLayout>
Nested FindByName doesn't work either.
Syncfusion.XForms.PopupLayout.SfPopupLayout tmp_Popup = _Page.FindByName("fro_Popup_NewItem") as Syncfusion.XForms.PopupLayout.SfPopupLayout;
Xamarin.Forms.SearchBar tmp_SearchBar = tmp_Popup.FindByName("fro_SearchBar_NewItem") as Xamarin.Forms.SearchBar;
Thanks
You can use Task to open a new thread to complete this operation in code-behind.
Xaml:
<SearchBar x:Name="fro_SearchBar_NewItem" Placeholder="...." BackgroundColor="White"/>
Code behind:
public partial class PagePop : Popup
{
public PagePop()
{
InitializeComponent();
Task.Run(() => myTask());//Create and start the thread
}
private void myTask()
{
Thread.Sleep(300);
fro_SearchBar_NewItem.Focus();
}
}
It seems that this wasn't so far from right direction, neither of the comments actually.
Syncfusion.XForms.PopupLayout.SfPopupLayout tmp_Popup = _Page.FindByName("fro_Popup_NewItem") as Syncfusion.XForms.PopupLayout.SfPopupLayout;
Xamarin.Forms.SearchBar tmp_SearchBar = tmp_Popup.FindByName("fro_SearchBar_NewItem") as Xamarin.Forms.SearchBar;
but I found the one which works and I don't have to create separate XAML for Popup.
private void fro_Popup_NewItem_Opened(object sender, EventArgs e)
{
try
{
var nativeObject = (object)fro_Popup_NewItem.GetType().GetRuntimeProperties().FirstOrDefault(x => x.Name.Equals("NativeObject")).GetValue(fro_Popup_NewItem);
var formsPopupviewContentTemplate = nativeObject.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name.Equals("formsPopupViewContentTemplate")).GetValue(nativeObject);
var SearchBar = (formsPopupviewContentTemplate as Grid).FindByName<SearchBar>("fro_SearchBar_NewItem");
SearchBar?.Focus();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Thanks for help, everyone.
In a Xamarin project I have this ListView inside a view called menu.xaml:
<ListView x:Name="listView" x:FieldModifier="public">
<ListView.ItemsSource>
<x:Array Type="{x:Type local1:MasterPageItem}">
<local1:MasterPageItem Title="foo" TargetType="{x:Type local:FooPage}" />
<local1:MasterPageItem Title="bar" TargetType="{x:Type local:BarPage}" />
<local1:MasterPageItem Title="logout" TargetType="{x:Type local:LogoutPage}" />
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding IconSource}" />
<Label Grid.Column="1" Text="{Binding Title}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
These produces this list:
In my viewmodel, I have this command:
class MasterViewModel : BaseViewModel
{
public ICommand LogoutActivity { get; private set; }
public MasterViewModel()
{
LogoutActivity = new Command(async () => await LogoutAsync());
}
}
Tapping one of these items opens a corresponding page. I want the logout link to fire a command instead of opening a page. How do I accomplish this?
You can add Tapped Gesture Recognizer to your grid:
<Grid Padding="5,10">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LoginActivity}" CommandParameter="{Binding .}">
</TapGestureRecognizer>
</Grid.GestureRecognizers>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding IconSource}" />
<Label Grid.Column="1" Text="{Binding Title}" />
</Grid>
For executing specific action for menu items, you can add unique property to your MasterPageItem model and set for each of them.
public partial class MainPage : MasterDetailPage
{
public MainPage ()
{
masterPage.listView.ItemSelected += OnItemSelected;
}
void OnItemSelected (object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null) {
// Check here if is the logout link that is clicked and perform the required a action
}
}
}
I have a custom label renderer to autosize text into single line according to the length of text.
The auto-sizing is working fine but the issues is when I scroll multiple times in listview, the text will be getting smaller.
I found that this issues only happened when it's a long list (more than 25 items) and each item label is different length of text. If long list with same text length for each item label, scrolling autosizing text wouldn't make text size decrease.
I wonder if anyone could give me some advice. Many Thanks.
Custom Label Renderer for autosizing text:
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace WhiteLabel.Mobile.App.Droid.Renderers
{
public class CustomLabelRenderer : LabelRenderer
{
public CustomLabelRenderer(Android.Content.Context ctx) : base(ctx)
{
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control != null)
{
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, 15, 50, 1, TypedValue.DensityDefault);
Control.SetLines(1);
}
}
}
}
XAML code:
<ListView
ItemsSource="{Binding Asset}"
SelectedItem="{Binding SelectedItem}"
ItemSelected="OnItemSelected"
SeparatorColor="Transparent"
HasUnevenRows="True"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="White"
Footer="">
<ListView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="1.3*"/>
</Grid.ColumnDefinitions>
<!-- Some Label Here -->
<!-- ...... -->
<customControls:CustomLabel
Text="{Binding Price}"
Grid.Row="1"
Grid.Column="1"
HorizontalOptions="EndAndExpand"
HorizontalTextAlignment="End" />
<!-- Some Label Here -->
<!-- ...... -->
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This my ViewModel:
namespace DietAndFitness.ViewModels
{
public class FoodItemsViewModel
{
private SQLiteAsyncConnection database;
public static ObservableCollection<GlobalFoodItem> FoodItems { get; set; }
public FoodItemsViewModel(SQLiteAsyncConnection _database)
{
database = _database;
}
public async void LoadList()
{
FoodItems = new ObservableCollection<GlobalFoodItem>(await database.Table<GlobalFoodItem>().ToListAsync());
}
public void Add()
{
FoodItems.Add(new GlobalFoodItem("Item"));
}
}
}
I want to bind an instance of this VM to the list in my View but I just can't get it to work properly. The only thing I've managed to get to work is this:
<?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:local="clr-namespace:DietAndFitness.ViewModels"
x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding FoodItems}" RowHeight="120">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="5">
<Frame OutlineColor="Accent" Padding="10">
<StackLayout>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="0"/>
<Label Text="{Binding Calories}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="1"/>
</Grid>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="Carbohydrates" Grid.Row="1" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="Proteins" Grid.Row="1" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="Fats" Grid.Row="1" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Carbohydrates}" Grid.Row="2" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Proteins}" Grid.Row="2" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Fats}" Grid.Row="2" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
</Grid>
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="AddFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Add" Grid.Row="0" Grid.Column="0" Clicked="AddFoodItemButton_Clicked" />
<Button x:Name="EditFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Edit" Grid.Row="0" Grid.Column="1" />
<Button x:Name="DeleteFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Delete" Grid.Row="0" Grid.Column="2" />
</Grid>
</StackLayout>
</ContentPage.Content>
But this has problems too:
If I create the VM in the page constructor the list won't load unless I refresh the page
If I create the VM inside App.cs the list will load on the first try but then I can't seem to access the instance of the VM
How do I bind my ListView to this VM? And is it possible to do so without making my ObservableCollection static?
I'm using a Master-Detail page in my app if it makes any difference (this is one of the detail pages).
Edit: Here is what I did in the constructor. SQLiteConnection.Database is just a static class that contains the connection to the database.
public FoodDatabasePage ()
{
FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
FoodDatabase.LoadList();
InitializeComponent ();
}
There's a few things that you have there.
When binding a ViewModel usually it's against the whole Page making the ViewModel its BindingContext specially in cases like yours when you have methods that want to access later.
So to do this you will need a few changes.
Your Page 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:local="clr-namespace:DietAndFitness.ViewModels"
x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding FoodItems}"
RowHeight="120">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame OutlineColor="Accent" Padding="15">
<StackLayout>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}"
FontSize="22"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Grid.Row="0"
Grid.Column="0"/>
</Grid>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Check that I changed the ListView ItemSource for the name of the collection in your FoodItemsViewModel.
Also, part of your XAML disappeared so I could not complete it.
Your Code Behind class: (FoodDatabasePage.xaml.cs)
public FoodItemsViewModel FoodDatabase {get; set;}
public FoodDatabasePage ()
{
InitializeComponent ();
FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
BindingContext = FoodDatabase;
}
public override void OnAppearing()
{
FoodDatabase.LoadList();
}
Here is where the "magic" happens. We are creating and storing the object of our FoodItemsViewModel so you can access it later as you need and then we are setting this object recently created as the BindingContext of the page (as explained above). Setting the ViewModel as the BindingContext of Page the latter will be able to access any public property of the FoodItemsViewModel.
Also, you will notice the LoadList() method was removed from the constructor, this is because there shouldn't be any heavy transaction (actually we should avoid any transactions) in the Page constructor. We moved it instead to the OnAppearing callback method that is executed by the Page when this is about to be displayed.
Note: This method is called every time the Page is presented on the screen so if you navigate away and come back it will be called again.
Your ViewModel:
namespace DietAndFitness.ViewModels
{
public class FoodItemsViewModel
{
private SQLiteAsyncConnection database;
//You don't need it static
public ObservableCollection<GlobalFoodItem> FoodItems { get; set; }
public FoodItemsViewModel(SQLiteAsyncConnection _database)
{
database = _database;
}
public async void LoadList()
{
// separated it only for readability.
var items = await database.Table<GlobalFoodItem>().ToListAsync();
FoodItems = new ObservableCollection<GlobalFoodItem>(items);
}
public void Add()
{
FoodItems.Add(new GlobalFoodItem("Item"));
}
}
}
This remains almost the same. The only change was removing the static qualifier to the FoodItems property since it does not need it anymore.
The above is just the basic of Mvvm and DataBinding. There're a few other things that you will need to take into consideration like INotifiedPropertyChanged, Commands and other important things.
You can find more information regarding this here
Hope this helps.-
PS: I couldn't check all of your code for any error.
My goal is to have a listview with a resizeable icon on the left, and 3 lines of text in the other column. Here is the template I've tested so far:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Margin="0,0" Padding="0,0" ColumnSpacing="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*"/>
<ColumnDefinition Width="85*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="{Binding sImageSource}" Aspect="AspectFit" />
<StackLayout Grid.Row="0" Grid.Column="1" Orientation="Vertical" Margin="0" Padding="0" Spacing="0">
<Label Text="{Binding sDisplayName}" FontSize="Medium" LineBreakMode="TailTruncation" YAlign="Center" TextColor="Black"/>
<Label Text="{Binding sFileSize}" FontSize="Micro" TextColor="Gray" YAlign="Center"/>
<Label Text="{Binding sFileDate}" FontSize="Micro" TextColor="Gray" YAlign="Center"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
If the ColumnDefinition Width="15*" is changed to "25*" I just get a wider column, and the picture is not changed in height (As I though it would).
Actually what I want is that RowDefinition Height="ColumnDefinition Width="15*"", so the both resize linear based on the parameter set as width.
Does anyone have tips regarding this?
PS: I have also tested HasUnevenRows=True, but that sets the height of each cell too high.
Edit:
Another variation has been tested:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" x:Name="MyImageGrid"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition BindingContext="{x:Reference MyImageGrid}" Height="{Binding Path=ActualWidth}"/>
</Grid.RowDefinitions>
But that did not work either. Have anyone done such a solution before?
I sort-of solved this little nut, but it was more complicated than it should be.
Here is the solution if anyone wonders.
In your contentpage-csfile, you have to add an property to store the value needed
public partial class YourPage : ContentPage
{
//....
public class RowHeight : INotifyPropertyChanged
{
private int nInternalHeight;
public event PropertyChangedEventHandler PropertyChanged;
public int Height
{
get { return nInternalHeight; }
set
{
nInternalHeight = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Height"));
}
}
}
//....
public RowHeight oRowHeight { get; set; }
//....
public YourPage()
{
oRowHeight = new RowHeight();
}
//....
protected override void OnAppearing()
{
base.OnAppearing(); //Width is -1 if set right away in YourPage() creation
oRowHeight.Height = (int)(App.Current.MainPage.Width * 0.15);
}
//....
Now in the XAML part of the code.
Our object oRowHeight.Height needs to be bound to our RowDefinition Height property.
The ListView also must have the following criterias:
1. HasUnevenRows = True
2. Page has to have a name for the reference
<?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="YourApp.Views.YourPage"
x:Name="YourPageName">
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding OtherItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Margin="0,0" Padding="0,0" ColumnSpacing="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*"/>
<ColumnDefinition Width="85*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Source={x:Reference YourPageName}, Path=BindingContext.oRowHeight.Height}" />
</Grid.RowDefinitions>
<!-- Like the rest of XAML above -->