I am displaying a CarouselView inside a PopupPage (using Rg.Plugins.Popup). It has 2 problems that I don't understand yet:
1. I am trying to do Swipe Down To Close Popup feature. Everything works fine if my UI is just like this:
PopupOne.xaml
<popup:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:popup="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
.....
x:Class="xxx.Views.PopupImgProd">
<ContentView>
<ContentView.Behaviors>
<behavior:SwipeDownToClosePopupPage CloseAction="SwipeDownToClosePopupPage_CloseAction"
ClosingEdge="0"
ClosingTimeInMs="400"/>
</ContentView.Behaviors>
<StackLayout HorizontalOptions="FillAndExpand" Padding="0" BackgroundColor="#fff">
<AbsoluteLayout HorizontalOptions="FillAndExpand">
<Frame>
....
</Frame>
<Frame>
....
</Frame>
</AbsoluteLayout>
<Label Text="sss"/>
</StackLayout>
</ContentView>
</popup:PopupPage>
SwipeDownToClosePopupPage.cs
public class SwipeDownToClosePopupPage : Behavior<View>
{
private PanGestureRecognizer PanGestureRecognizer { get; set; }
private DateTimeOffset? StartPanDownTime { get; set; }
private DateTimeOffset? EndPanDownTime { get; set; }
private double TotalY { get; set; }
private bool ReachedEdge { get; set; }
/// <summary>
/// Close action, depends on your navigation mode
/// </summary>
public event Action CloseAction;
public static readonly BindableProperty ClosingEdgeProperty = BindableProperty.Create(propertyName: nameof(ClosingEdge)
, returnType: typeof(Double)
, declaringType: typeof(SwipeDownToClosePopupPage)
, defaultValue: Convert.ToDouble(100)
, defaultBindingMode: BindingMode.TwoWay
, propertyChanged: ClosingEdgePropertyChanged);
/// <summary>
/// The height from the bottom that will trigger close page
/// </summary>
public Double ClosingEdge
{
get { return (Double)GetValue(ClosingEdgeProperty); }
set { SetValue(ClosingEdgeProperty, Convert.ToDouble(value)); }
}
private static void ClosingEdgePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (SwipeDownToClosePopupPage)bindable;
if (newValue != null)
{
control.ClosingEdge = Convert.ToDouble(newValue);
}
}
public static readonly BindableProperty ClosingTimeInMsProperty = BindableProperty.Create(propertyName: nameof(ClosingTimeInMs)
, returnType: typeof(Int64)
, declaringType: typeof(SwipeDownToClosePopupPage)
, defaultValue: Convert.ToInt64(500)
, defaultBindingMode: BindingMode.TwoWay
, propertyChanged: ClosingTimeInMsPropertyChanged);
/// <summary>
/// Scroll time less than this value will trigger close page
/// </summary>
public Int64 ClosingTimeInMs
{
get { return (Int64)GetValue(ClosingTimeInMsProperty); }
set { SetValue(ClosingTimeInMsProperty, Convert.ToInt64(value)); }
}
private static void ClosingTimeInMsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (SwipeDownToClosePopupPage)bindable;
if (newValue != null)
{
control.ClosingTimeInMs = Convert.ToInt64(newValue);
}
}
public SwipeDownToClosePopupPage()
{
this.PanGestureRecognizer = new PanGestureRecognizer();
}
protected override void OnAttachedTo(View v)
{
PanGestureRecognizer.PanUpdated += Pan_PanUpdated;
v.GestureRecognizers.Add(this.PanGestureRecognizer);
base.OnAttachedTo(v);
}
protected override void OnDetachingFrom(View v)
{
PanGestureRecognizer.PanUpdated -= Pan_PanUpdated;
v.GestureRecognizers.Remove(this.PanGestureRecognizer);
base.OnDetachingFrom(v);
}
private void Pan_PanUpdated(object sender, PanUpdatedEventArgs e)
{
View v = sender as View;
switch (e.StatusType)
{
case GestureStatus.Started:
StartPanDownTime = DateTime.Now;
break;
case GestureStatus.Running:
TotalY = e.TotalY;
if (TotalY > 0)
{
if (Device.RuntimePlatform == Device.Android)
{
v.TranslateTo(0, TotalY + v.TranslationY, 20, Easing.Linear);
//Too close to edge?
ReachedEdge = TotalY + v.TranslationY > v.Height - ClosingEdge;
}
else
{
v.TranslateTo(0, TotalY, 20, Easing.Linear);
//Too close to edge?
ReachedEdge = TotalY > v.Height - ClosingEdge;
}
}
break;
case GestureStatus.Completed:
EndPanDownTime = DateTimeOffset.Now;
if (TotalY > 0
|| ReachedEdge)
//Swipe too fast
CloseAction?.Invoke();
else
{
v.TranslateTo(0, 0, 20, Easing.Linear);
}
break;
}
if (e.StatusType == GestureStatus.Completed || e.StatusType == GestureStatus.Canceled)
{
StartPanDownTime = null;
EndPanDownTime = null;
}
}
}
----------> The above it works great. When I SwipeDown it closes the Popup. Very good
However, now what I display is: CarouselView.
The problem occurs here: When I SwipeDown it doesn't close the Popup? Even though I have wrapped the event for it as it was at the beginning.
PopupOne.xaml
<popup:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:popup="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
.....
x:Class="TopSell.Views.PopupImgProd">
<ContentView>
<ContentView.Behaviors>
<behavior:SwipeDownToClosePopupPage CloseAction="SwipeDownToClosePopupPage_CloseAction"
ClosingEdge="0"
ClosingTimeInMs="400"/>
</ContentView.Behaviors>
<StackLayout HorizontalOptions="FillAndExpand" Padding="0" BackgroundColor="#fff">
<AbsoluteLayout HorizontalOptions="FillAndExpand">
<Frame>
....
</Frame>
<Frame>
....
</Frame>
</AbsoluteLayout>
<CarouselView x:Name="_stdata" ItemsSource="{Binding imglistProd}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0" Margin="0" VerticalOptions="FillAndExpand" x:DataType="model:ProductImages">
<Image Aspect="AspectFill" VerticalOptions="CenterAndExpand" Source="{Binding Images}"/>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</StackLayout>
</ContentView>
</popup:PopupPage>
PopupOne.xaml.cs
private void SwipeDownToClosePopupPage_CloseAction()
{
PopupNavigation.Instance.PopAllAsync();
}
---> Is there a conflict between the left and right movement of CarouselView and SwipeDown
2. The displayed image of the CarouselView seems to cut off the top and bottom of the image (Image is cropped when I put it in CarouselView )
Looking forward to everyone's help. Thank you
Related
I have this listview and in the listview I have an ItemTemplate with a DataTemplate and a ViewCell in which I have a checkbox named "box1". I want to make it stay checked when i switch pages, but i can't acess it via name because it is in a DataTemplate and in a ViewCell. I have tried to name all the controls down to the checkbox and get access to it like that, but it does not seem to work...
This is my xaml:
<ListView SeparatorVisibility="None"
BackgroundColor="Transparent"
VerticalOptions="Center"
x:Name="listView"
HasUnevenRows="True"
>
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Meal"
x:Name="mydt"
>
<ViewCell
x:Name="myvc"
>
<Grid BackgroundColor="Transparent"
x:Name="mygrid"
>
<Frame BackgroundColor="Transparent"
CornerRadius="20"
x:Name="myframe"
>
<StackLayout Orientation="Horizontal"
>
<Image Source="meal.png" WidthRequest="59" Margin="0, 0, 15, 0"/>
<StackLayout Orientation="Vertical" WidthRequest="300">
<Label VerticalOptions="Start"
FontSize="20"
Text="{Binding Name}"
FontAttributes="Bold"/>
<Label VerticalOptions="Start"
FontSize="15"
Text="{Binding Ingredients}"/>
<StackLayout Orientation="Horizontal">
<Label VerticalOptions="Start"
FontSize="15"
Text="{Binding Calories}"
TextColor="OrangeRed"/>
<Label Text="kcal"
FontSize="15"
TextColor="OrangeRed"/>
</StackLayout>
</StackLayout>
<CheckBox
x:Name="box1"
IsChecked="{Binding Checked}"
Color="Green"
Margin="60, 0, 0, 0"
CheckedChanged="box1_CheckedChanged"
BindingContext="{Binding ., Mode=TwoWay}"
/>
</StackLayout>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is my event handler from the Content Page in cs:
private void box1_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var meal = listView.SelectedItem as Meal;
if (listView.SelectedItem != null)
{
if (e.Value == true)
{
long cal = long.Parse(meal.Calories);
calories_consumed = calories_consumed + cal;
ch = true;
}
else
{
long cal = long.Parse(meal.Calories);
calories_consumed = calories_consumed - cal;
ch = false;
}
}
label_cal.Text = calories_consumed.ToString();
}
This is my updated Meal class using INotifyPropertyChanged:
public class Meal : INotifyPropertyChanged
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
private string ingredients;
public string Ingredients
{
get { return ingredients; }
set
{
ingredients = value;
OnPropertyChanged(nameof(Ingredients));
}
}
private string calories;
public string Calories
{
get { return calories; }
set
{
calories = value;
OnPropertyChanged(nameof(Calories));
}
}
private bool isChecked;
public bool IsChecked
{
get
{
return isChecked;
}
set
{
isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And this is my code behind for the ContentPage:
protected override async void OnAppearing()
{
base.OnAppearing();
listView.ItemsSource = new ObservableCollection<Meal>(await App.Database.GetMealAsync());
}
public static bool ch;
Event handler for the checkbox in the code behind:
private void box1_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var meal = listView.SelectedItem as Meal;
if (listView.SelectedItem != null)
{
if (e.Value == true)
{
long cal = long.Parse(meal.Calories);
calories_consumed = calories_consumed + cal;
ch = true;
}
else
{
long cal = long.Parse(meal.Calories);
calories_consumed = calories_consumed - cal;
ch = false;
}
}
meal.IsChecked = ch;
label_cal.Text = calories_consumed.ToString();
}
To be short you can just save the value in Preferences or make a variable in a model that is static and bind that values OnAppearing
There are mainly two ways that you could store the checkbox's state.
1.You could use Json to Serialize and deserialize the model that has a IsChecked property that binding with the checkbox.For more details, you could refer to this thread.
Code in checkbox_CheckedChanged event:
private void checkbox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var checkbox = (CheckBox)sender;
var selectMeal = checkbox.BindingContext as Meal;
selectMeal.Checked = e.Value;
//save the data and checkbox state,you could save the data as a json string
string json = JsonConvert.SerializeObject(blistView);
Preferences.Set("listmeals", json);
}
2.You could store the state of the checkbox using sqlite-net-pcl.Please refer to below MS official docs for more details.
And then retrieve the check state of the checkbox via OnAppearing Method.
protected override void OnAppearing()
{
// retrieve the check state of the checkbox in your sqlite database.
base.OnAppearing();
TodoItemDatabase database = await TodoItemDatabase.Instance;
listView.ItemsSource = await database.GetItemsAsync();
}
MS official docs:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/data/databases
The way you are connecting your class your view/xaml is incorrect. If you want to do it without MVVM you can go about it by creating a model that implements INotifyPropertyChanged and an ObservableCollection as listView itemssource.
Model class Meal could look like this:
public class Meal : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
private string ingredients;
public string Ingredients
{
get { return ingredients; }
set
{
ingredients = value;
OnPropertyChanged(nameof(Ingredients));
}
}
private string calories;
public string Calories
{
get { return calories; }
set
{
calories = value;
OnPropertyChanged(nameof(Calories));
}
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
#region INotify property changed
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
The MainPage.xaml like this:
<StackLayout>
<ListView SeparatorVisibility="None"
BackgroundColor="Transparent"
VerticalOptions="Center"
x:Name="listView"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Meal" x:Name="mydt" >
<ViewCell x:Name="myvc" >
<Grid BackgroundColor="Transparent"
x:Name="mygrid" >
<Frame BackgroundColor="Transparent"
CornerRadius="20"
x:Name="myframe">
<StackLayout Orientation="Horizontal">
<Image Source="meal.png" WidthRequest="59" Margin="0, 0, 15, 0"/>
<StackLayout Orientation="Vertical" WidthRequest="300">
<Label VerticalOptions="Start"
FontSize="20"
Text="{Binding Name}"
FontAttributes="Bold"/>
<Label VerticalOptions="Start"
FontSize="15"
Text="{Binding Ingredients}"/>
<StackLayout Orientation="Horizontal">
<Label VerticalOptions="Start"
FontSize="15"
Text="{Binding Calories}"
TextColor="OrangeRed"/>
<Label Text="kcal" FontSize="15" TextColor="OrangeRed"/>
</StackLayout>
</StackLayout>
<CheckBox x:Name="box1" IsChecked="{Binding IsChecked}" Color="Green" Margin="60, 0, 0, 0" />
</StackLayout>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Clicked="Button_Clicked" Text="Check status items"/>
</StackLayout>
And your MainPage.xaml.cs code behind like this:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
listView.ItemsSource = new ObservableCollection<Meal>(new List<Meal>
{
new Meal { Name = "Meal 01", Ingredients = "Ingredients 01", Calories = "250" },
new Meal { Name = "Meal 02", Ingredients = "Ingredients 02", Calories = "350" },
new Meal { Name = "Meal 03", Ingredients = "Ingredients 03", Calories = "450" }
});
}
/// <summary>
/// check items ischecked status
/// </summary>
private void Button_Clicked(object sender, EventArgs e)
{
foreach (var item in listView.ItemsSource)
{
if (item is Meal meal && meal.IsChecked)
System.Diagnostics.Debug.WriteLine($"{meal.Name} is checked");
}
}
}
Look at the other answer for saving the model and/or list. When saved the list can be retrieved in the OnAppearing and set instead of recreated as shown in this example. I've added a button on the bottom that prints out which Meals are selected in the output window. See screenshot:
I'm trying to use a single Label to display one of the two data fields alternately in Xamarin Forms. Only Label 1 Displaying the binding field (Contact_Name), while second Label which I am trying to use a variable "DisplayField" is not displaying either 'Contact_Address' or 'Contact_eMail' .
Question posted before and Another user tried to help but it didn't work!
Model Class
public class Contacts
{
[PrimaryKey][Autoincrement]
public int Contact_ID { get; set; }
public string Contact_Name { get; set; }
public string Contact_Address { get; set; }
public string Contact_eMail { get; set; }
}
XAML Page
<StackLayout>
<Button Text="Display Address" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Fill" Clicked="Display_Address" />
<Button Text="Display Email" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Fill" Clicked="Display_eMail" />
<Entry HorizontalOptions="FillAndExpand" Text="{Binding DisplayField}" />
<ListView x:Name="listView" HasUnevenRows="True" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand" >
<Frame >
<StackLayout Orientation="Vertical" VerticalOptions="Center">
<Label Text="{Binding Contact_Name}" FontSize="Medium" LineBreakMode="WordWrap" />
<Label Text="{Binding DisplayField}" LineBreakMode="WordWrap" />
</StackLayout>
</Frame>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Code Behind
public partial class FieldSwap : ContentPage
{
readonly FieldViewModel _fieldViewModel;
readonly SQLiteAsyncConnection _connection = DependencyService.Get<ISQLite>().GetConnection();
public ObservableCollection<Contacts> CList { get; set; }
public static string DisplayField { get; private set; }
public static int caseSwitch { get; private set; }
public FieldSwap()
{
InitializeComponent();
_fieldViewModel = new FieldViewModel();
_fieldViewModel.Field = "Contact_Address";
this.BindingContext = _fieldViewModel;
}
public static void SelectField()
{
switch (caseSwitch)
{
case 1:
DisplayField = "Contact_Address";
break;
case 2:
DisplayField = "Contact_eMail";
break;
default:
DisplayField = ("Contact_Address");
break;
}
}
private void Display_Address(object sender, EventArgs e)
{
caseSwitch = 1;
SelectField();
ReadData();
}
private void Display_eMail(object sender, EventArgs e)
{
caseSwitch = 2;
SelectField();
ReadData();
}
public void ReadData()
{
var list = _connection.Table<Contacts>().ToListAsync().Result;
CList = new ObservableCollection<Contacts>(list);
listView.ItemsSource = CList;
}
}
View Model Class
public class FieldViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
String _field;
public string Field
{
set
{
if (!value.Equals(_field, StringComparison.Ordinal))
{
_field = value;
OnPropertyChanged("DisplayField");
}
}
get
{
return _field;
}
}
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
}
You could use IsVisible property to achieve that, not need to bind only one lable.
Therefore, binding Contact_Address and Contact_eMail with two lables in StackLayout as follows:
<StackLayout Orientation="Vertical" VerticalOptions="Center">
<Label Text="{Binding Contact_Name}" FontSize="Medium" LineBreakMode="WordWrap" />
<Label Text="{Binding Contact_Address}" IsVisible="{Binding AddressVisible}" LineBreakMode="WordWrap" />
<Label Text="{Binding Contact_eMail}" IsVisible="{Binding EMailVisible}" LineBreakMode="WordWrap" />
</StackLayout>
Then in Contacts add two visiable proerty:
public class Contacts: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
...
private bool addressVisible;
public bool AddressVisible
{
set
{
if (addressVisible != value)
{
addressVisible = value;
OnPropertyChanged("AddressVisible");
}
}
get
{
return addressVisible;
}
}
private bool eMailVisible;
public bool EMailVisible
{
set
{
if (eMailVisible != value)
{
eMailVisible = value;
OnPropertyChanged("EMailVisible");
}
}
get
{
return eMailVisible;
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now in Contentpage, you could modify the visiable propery when button be clicked:
private void Display_Address(object sender, EventArgs e)
{
foreach(var item in CList )
{
item.AddressVisible = true;
item.EMailVisible = false;
}
}
private void Display_eMail(object sender, EventArgs e)
{
foreach (var item in CList )
{
item.AddressVisible = false;
item.EMailVisible = true;
}
}
Here is the effect:
I'm trying to use a single Label to display one of the two data fields alternately in Xamarin Forms. Only Label 1 Display the binding field and second Label which I am trying to use a variable "DisplayField" is not displaying either 'Contact_Address' or 'Contact_eMail'
Model class
public class Contacts
{
[PrimaryKey][Autoincrement]
public int Contact_ID { get; set; }
public string Contact_Name { get; set; }
public string Contact_Address { get; set; }
public string Contact_eMail { get; set; }
public string DisplayField { get; set; }
}
XAML page
<StackLayout>
<Button Text="Display Address" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Fill" Clicked="Display_Address" />
<Button Text="Display Email" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Fill" Clicked="Display_eMail" />
<Entry HorizontalOptions="FillAndExpand" Text="{Binding DisplayField}" />
<ListView x:Name="listView" HasUnevenRows="True" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand" >
<Frame >
<StackLayout Orientation="Vertical" VerticalOptions="Center">
<Label Text="{Binding Contact_Name}" FontSize="Medium" LineBreakMode="WordWrap" />
<Label Text="{Binding DisplayField}" LineBreakMode="WordWrap" />
</StackLayout>
</Frame>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Code behind
public partial class FieldSwap : ContentPage
{
readonly FieldViewModel _fieldViewModel;
readonly SQLiteAsyncConnection _connection = DependencyService.Get<ISQLite>().GetConnection();
public ObservableCollection<Contacts> CList { get; set; }
public static string DisplayField { get; private set; }
public static int caseSwitch { get; private set; }
public FieldSwap()
{
InitializeComponent();
_fieldViewModel = new FieldViewModel();
_fieldViewModel.Field = "Contact_Address";
this.BindingContext = _fieldViewModel;
}
public static void SelectField()
{
switch (caseSwitch)
{
case 1:
DisplayField = "Contact_Address";
break;
case 2:
DisplayField = "Contact_eMail";
break;
default:
DisplayField = ("Contact_Address");
break;
}
}
private void Display_Address(object sender, EventArgs e)
{
caseSwitch = 1;
SelectField();
ReadData();
}
private void Display_eMail(object sender, EventArgs e)
{
caseSwitch = 2;
SelectField();
ReadData();
}
public void ReadData()
{
var list = _connection.Table<Contacts>().ToListAsync().Result;
CList = new ObservableCollection<Contacts>(list);
listView.ItemsSource = CList;
}
}
View model class
public class FieldViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
String _field;
public string Field
{
set
{
if (!value.Equals(_field, StringComparison.Ordinal))
{
_field = value;
OnPropertyChanged("DisplayField");
}
}
get
{
return _field;
}
}
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
}
Screen Shot
Screen Shot 2
If you want to display different value in ListView by user selected, I suggest you can use Picker to choose, I do one sample that you can take a look.
<ContentPage.Content>
<StackLayout>
<Picker x:Name="choose" SelectedIndexChanged="choose_SelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Contact_Address</x:String>
<x:String>Contact_eMail</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
<ListView
x:Name="listview1"
HasUnevenRows="True"
ItemsSource="{Binding items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" VerticalOptions="Center">
<Label
FontSize="Medium"
LineBreakMode="WordWrap"
Text="{Binding Contact_Name}" />
<Label
IsVisible="{Binding Source={x:Reference root}, Path=BindingContext.selectedm}"
LineBreakMode="WordWrap"
Text="{Binding Contact_eMail}" />
<Label
IsVisible="{Binding Source={x:Reference root}, Path=BindingContext.selecteda}"
LineBreakMode="WordWrap"
Text="{Binding Contact_Address}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class Page31 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<Contacts> items { get; set; }
private Boolean _selecteda;
public Boolean selecteda
{
get { return _selecteda; }
set
{
_selecteda = value;
RaisePropertyChanged("selecteda");
}
}
private Boolean _selectedm;
public Boolean selectedm
{
get { return _selectedm; }
set
{
_selectedm = value;
RaisePropertyChanged("selectedm");
}
}
public Page31()
{
InitializeComponent();
items = new ObservableCollection<Contacts>();
for(int i=0;i<20;i++)
{
Contacts contact = new Contacts()
{
Contact_ID = i, Contact_Name = "cherry " + i, Contact_Address = "the street " + i, Contact_eMail = "cherry#outlook.com "+i
};
items.Add(contact);
}
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private void choose_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
if (selectedIndex ==0)
{
selecteda = true;
selectedm = false;
}
else
{
selectedm = true;
selecteda = false;
}
}
}
I have created dynamic 20 entries and want to focus on next entry after user enter a digit and max length of entry is 1. The focus should be automatically move on next entry.I am sharing my code.Thanks in advance for help.
//model
public class CrossingUIModel
{
public int Id { get; set; }
public string FieldValue { get; set; }
}
//on change property
private ObservableCollection<CrossingUIModel> bindCrossingUIModel;
public ObservableCollection<CrossingUIModel> BindCrossingUIModel
{
get { return bindCrossingUIModel; }
set
{
bindCrossingUIModel = value;
OnPropertyChanged(nameof(BindCrossingUIModel));
}
}
//creating ui
public void CreateUI()
{
UserDialogs.Instance.ShowLoading();
BindCrossingUIModel = new ObservableCollection<CrossingUIModel>();
for (int i = 1; i < 21; i++)
{
CrossingUIModel model = new CrossingUIModel();
model.Id = i;
BindCrossingUIModel.Add(model);
}
UserDialogs.Instance.HideLoading();
}
//xml file
<CollectionView x:Name="CrossingView" ItemsSource="{Binding BindCrossingUIModel, Mode=TwoWay}" SelectionMode="Multiple">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand">
<Entry x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
ReturnType="Next" MaxLength="1" Keyboard="Numeric"
TextChanged="Fields_TextChanged" ></Entry>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Since you had used Data-Binding , it would be better to handle all the logic in ViewModel .
in code behind
Define a custom Entry
public class MyEntry:Entry
{
public static readonly BindableProperty IsFocusProperty =BindableProperty.Create("IsFocus", typeof(bool), typeof(MyEntry), false,propertyChanged: OnChanged);
static void OnChanged(BindableObject bindable, object oldValue, object newValue)
{
var entry = bindable as MyEntry;
var focus = (bool)newValue;
if(focus)
{
entry.Focus();
}
else
{
entry.Unfocus();
}
}
public bool IsFocus
{
get { return (bool)GetValue(IsFocusProperty); }
set {
SetValue(IsFocusProperty, value);
}
}
public MyEntry()
{
this.Focused += MyEntry_Focused;
this.Unfocused += MyEntry_Unfocused;
}
private void MyEntry_Unfocused(object sender, FocusEventArgs e)
{
this.IsFocus = false;
}
private void MyEntry_Focused(object sender, FocusEventArgs e)
{
this.IsFocus = true;
}
}
in Model
public class CrossingUIModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string fieldValue;
public string FieldValue
{
get
{
return fieldValue;
}
set
{
if (fieldValue != value)
{
fieldValue = value;
OnPropertyChanged("FieldValue");
}
}
}
bool isFocus = false;
public bool IsFocus
{
get
{
return isFocus;
}
set
{
if (isFocus != value)
{
isFocus = value;
OnPropertyChanged("IsFocus");
}
}
}
}
in ViewModel
public class MyViewModel
{
public ObservableCollection<CrossingUIModel> BindCrossingUIModel { get; set; }
public MyViewModel()
{
BindCrossingUIModel = new ObservableCollection<CrossingUIModel>();
for (int i = 1; i < 21; i++)
{
CrossingUIModel model = new CrossingUIModel();
model.Id = i;
BindCrossingUIModel.Add(model);
}
foreach (CrossingUIModel model in BindCrossingUIModel)
{
model.PropertyChanged += Model_PropertyChanged;
}
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "FieldValue")
{
var model = sender as CrossingUIModel;
if(model.FieldValue.Length==1)
{
model.FieldValue = model.FieldValue.Substring(0, 1);
model.IsFocus = false;
int id = model.Id;
BindCrossingUIModel[id].IsFocus = true;
}
}
}
}
in xaml
Now we don't need to set MaxLength and TextChanged any more .
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<CollectionView x:Name="CrossingView" ItemsSource="{Binding BindCrossingUIModel, Mode=TwoWay}" SelectionMode="Multiple">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout WidthRequest="100" HeightRequest="30" HorizontalOptions="FillAndExpand">
<local:MyEntry WidthRequest="80" BackgroundColor="LightBlue" HeightRequest="30" x:Name="Fields" Text="{Binding FieldValue, Mode=TwoWay}"
IsFocus="{Binding IsFocus, Mode=TwoWay}"
ReturnType="Next" Keyboard="Numeric"
></local:MyEntry>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
By the way , you could use Grid instead of StackLayout as the Parent Layout of the Entry.
In maine, on my button click event handler I do:
private void addIconButton_Click(object sender, EventArgs e)
{
if (test)
{
MessageBox.Show("enters addIcon Main");
Note note = new Note();
note.Modified = DateTimeOffset.Now;
if (note != null)
{
Settings.NotesList.Add(note); //this causes the issue.
//Settings.NotesList[0] = note;
}
Settings.CurrentNoteIndex = 0;
test = false;
MessageBox.Show("right before navigate");
this.NavigationService.Navigate(new Uri("/DetailsPage.XAML", UriKind.Relative));
MessageBox.Show("after navigate");
}
//DetailsPage mynewPage = new DetailsPage();
//this.Content = mynewPage;
}
and on my on navigatedTo I do:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
MessageBox.Show("enters onNav Main");
DataContext = null;
DataContext = Settings.NotesList;
Settings.CurrentNoteIndex = -1;
Listbox.SelectedIndex = -1;
if (Settings.NotesList != null)
{
if (Settings.NotesList.Count == 0)
{
Notes.Text = "No Notes";
}
else
{
Notes.Text = "";
}
}
}
Inside my front end code I do:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="Listbox" SelectionChanged="listbox_SelectionChanged" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Width="800" MinHeight="60">
<StackPanel>
<TextBlock x:Name="Title" VerticalAlignment="Center" FontSize="{Binding TextSize}" Text="{Binding Name}"/>
<TextBlock x:Name="Date" VerticalAlignment="Center" FontSize="{Binding TextSize}" Text="{Binding Modified,
Mode=TwoWay, Converter={StaticResource dateConverter}}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
I have an observable collections in a static class where in the constructor I set it to:
public static class Settings
{
public static ObservableCollection<Note> NotesList;
static IsolatedStorageSettings settings;
private static int currentNoteIndex;
static Settings()
{
NotesList = new ObservableCollection<Note>();
settings = IsolatedStorageSettings.ApplicationSettings;
MessageBox.Show("enters constructor settings");
}
and then inside the Notes class it just looks like this:
public class Note
{
public DateTimeOffset Modified { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int TextSize { get; set; }
}
when I click the app button and it calls the invent handler right after the navigationService is called I get:
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
this only happens when Settings.NotesList.Add(note); is added in the addIconButton_click method..
Any suggestions???
I fixed it by setting default values to the instance variables in the Notes class inside the constructor...