Xamarin Forms: Flowlistview item tapping issue - image

I am implementing a game in my xamarin forms application, a name match game. It has two lists: one for showing the images and the other one for showing the names. The player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists.
I am using flowlistview for showing the image list and name list because I need to show multiple items in a row. When tapping the image and name I have done the matching and remove the image and name if they matched. But I need to highlight the selected image or selected name when tapping and disable selection for other items. I have done highlighting feature using this thread, but it is not working perfectly. Sometimes multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting.
I have created a sample project and uploaded it here. Please help me to complete this game. We have this game on our website, https://www.catholicbrain.com/edu-namematch/39524/1/the-two-great-commandments , please have a look at it for the working of the game. I will give you login details by DM.
Edit 1
#LucasZhang-MSFT I am accepting that, but the current question is different. It has 2 different flowlistviews. On the top an image list and on the bottom a name list. This is a simple game for kids, the player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists. When not match I reset the items background colors like below:
foreach (var item1 in ImageItems)
{
item.BGColor = Color.White;
}
foreach (var item2 in NameItems)
{
item.BGColor = Color.White;
}
OnPropertyChanged("NameMatchImagItems");
OnPropertyChanged("NameMatchNameItems");
After this point, multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting.
If you have time, can you please download the sample and have a look? I tried my best, but no luck.

Cause:
You set the ItemsSource of two flowlistview with the same source _allItems !!
Solution:
in xaml
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<!--imageflowlistview-->
<flv:FlowListView
x:Name="NameMatchImageList"
FlowItemTappedCommand="{Binding ImageItemTappedCommand}"
FlowItemsSource="{Binding ImageItems}"
FlowColumnCount="2"
FlowLastTappedItem="{Binding LastImageTappedItem}"
HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout BackgroundColor="{Binding BGColor}" Orientation="Vertical">
<Frame
Padding="5"
Margin="5"
HasShadow="False"
BorderColor="#a4e6f9"
CornerRadius="15">
<ffimageloading:CachedImage
Source="{Binding imageUrl, Converter={StaticResource urlJoinConverter}}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
HeightRequest="100"
Aspect="AspectFill"/>
</Frame>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
<!--NamesFlowlistview-->
<flv:FlowListView
x:Name="NameMatchNameList"
FlowItemTappedCommand="{Binding NameItemTappedCommand}"
FlowItemsSource="{Binding NameItems}"
FlowColumnCount="2"
FlowLastTappedItem="{Binding LastNameTappedItem}"
HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label
TextColor="Black"
FontSize="Large"
BackgroundColor="{Binding BGColor}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Text="{Binding name}"/>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
</ContentPage.Content>
in code behind
namespace FlowListView_Tap
{
class NameMatchViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<NameMatchList> imageItems;
public ObservableCollection<NameMatchList> ImageItems { get
{
return imageItems;
}
set {
if(value!=null)
{
imageItems = value;
OnPropertyChanged("ImageItems");
}
}
}
public ObservableCollection<NameMatchList> nameItems;
public ObservableCollection<NameMatchList> NameItems
{
get
{
return nameItems;
}
set
{
if (value != null)
{
nameItems = value;
OnPropertyChanged("NameItems");
}
}
}
public bool isImageSelected = false;
public bool isNameSelected = false;
public ICommand NameItemTappedCommand { get; set; }
public ICommand ImageItemTappedCommand { get; set; }
private NameMatchList lastImageTappedItem;
public NameMatchList LastImageTappedItem
{
get
{
return lastImageTappedItem;
}
set
{
if(value!=null)
{
lastImageTappedItem = value;
OnPropertyChanged("LastImageTappedItem");
}
}
}
private NameMatchList lastNameTappedItem;
public NameMatchList LastNameTappedItem
{
get
{
return lastNameTappedItem;
}
set
{
if (value != null)
{
lastNameTappedItem = value;
OnPropertyChanged("LastNameTappedItem");
}
}
}
public NameMatchViewModel()
{
ImageItemTappedCommand = new Command((obj) => {
try
{
//reset the bg color
foreach (var item in ImageItems)
{
item.BGColor = Color.White;
}
NameMatchList imageList = obj as NameMatchList;
int index = ImageItems.IndexOf(imageList);
imageList.BGColor = Color.Red;
///ImageItems.RemoveAt(index);
//ImageItems.Insert(index, imageList);
//Storing name and imageurl to local db
Application.Current.Properties["NameMatchImageList_Image"] = imageList.imageUrl;
Application.Current.Properties["NameMatchImageList_Name"] = imageList.name;
Application.Current.Properties["ImageItem"] = imageList;
isImageSelected = true;
if (isImageSelected && isNameSelected)
{
//If both image and name selected by player startes checking the matching
StartNameMatchCheck(imageList);
}
}
catch (Exception imagetapEx)
{
Debug.WriteLine("imagetapEx:>>" + imagetapEx);
}
});
NameItemTappedCommand = new Command((obj) => {
try
{
//reset the bg color
foreach (var item in NameItems)
{
item.BGColor = Color.White;
}
NameMatchList nameList = obj as NameMatchList;
int index = NameItems.IndexOf(nameList);
nameList.BGColor = Color.Red;
//NameItems.RemoveAt(index);
//NameItems.Insert(index, nameList);
//Storing name and imageurl to local db
Application.Current.Properties["NameMatchNameList_Image"] = nameList.imageUrl;
Application.Current.Properties["NameMatchNameList_Name"] = nameList.name;
Application.Current.Properties["NameItem"] = nameList;
isNameSelected = true;
if (isImageSelected && isNameSelected)
{
//If both image and name selected by player startes checking the matching
StartNameMatchCheck(nameList);
}
}
catch (Exception nametapEx)
{
Debug.WriteLine("nametapEx:>>" + nametapEx);
}
});
}
public async void StartNameMatchCheck(NameMatchList item)
{
isImageSelected = false;
isNameSelected = false;
//Fetching data from local db
string NameMatchImageListImage = Application.Current.Properties["NameMatchImageList_Image"].ToString();
string NameMatchImageListName = Application.Current.Properties["NameMatchImageList_Name"].ToString();
string NameMatchNameListImage = Application.Current.Properties["NameMatchNameList_Image"].ToString();
string NameMatchNameListName = Application.Current.Properties["NameMatchNameList_Name"].ToString();
//Match check
if ((NameMatchImageListImage == NameMatchNameListImage) && (NameMatchImageListName == NameMatchNameListName))
{
await Application.Current.MainPage.DisplayAlert("Alert", "Success", "Ok");
//Removing the items from list if they match
ImageItems.Remove(LastImageTappedItem);
NameItems.Remove(LastNameTappedItem);
LastImageTappedItem = null;
LastNameTappedItem = null;
}
else
{
await Application.Current.MainPage.DisplayAlert("Alert", "Failed", "Ok");
//resetting the colors
LastImageTappedItem.BGColor = Color.White;
LastNameTappedItem.BGColor = Color.White;
}
}
public async void CallNameMatch()
{
try
{
//HttpClient client = new HttpClient();
//var nameMatchResponse = await client.GetAsync("");
//if (nameMatchResponse.IsSuccessStatusCode)
//{
// var Response = await nameMatchResponse.Content.ReadAsStringAsync();
// var imageResponse = JsonConvert.DeserializeObject<Games>(Response.ToString());
// var namematch = JsonConvert.DeserializeObject<Games>(Response.ToString());
ImageItems = new ObservableCollection<NameMatchList>();
ImageItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
ImageItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
ImageItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
ImageItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
//shuffling image list
//Random r1 = new Random();
//int randomIndex1 = 0;
//while (ImageItems.Count > 0)
//{
// randomIndex1 = r1.Next(0, ImageItems.Count);
// ImageItems[randomIndex1].BGColor = Color.White;
// ImageItems.Add(ImageItems[randomIndex1]);
// ImageItems.RemoveAt(randomIndex1);
//}
//NameMatchImagItems = new ObservableCollection<NameMatchList>(ImageItems);
NameItems = new ObservableCollection<NameMatchList>();
NameItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
NameItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
NameItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
NameItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
//shuffling name list
//Random r2 = new Random();
//int randomIndex2 = 0;
//while (NameItems.Count > 0)
//{
// randomIndex2 = r2.Next(0, NameItems.Count);
// NameItems[randomIndex2].BGColor = Color.White;
// NameItems.Add(NameItems[randomIndex2]);
// NameItems.RemoveAt(randomIndex2);
//}
// NameMatchNameItems = new ObservableCollection<NameMatchList>(NameItems);
//}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("NMException:>" + ex);
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ShowAlert(string message)
{
Device.BeginInvokeOnMainThread(async () => {
await Application.Current.MainPage.DisplayAlert("Alert", message, "Ok");
});
}
}
}

Related

Saving Picker Value null exception cannot convert string to model

I am having an issue with saving of my picker value. I am rewriting old code and i am struggling with saving a picker value. Once I select the picker i have the value but it doesn't go through the getter. I have similar method on other picker it works there perfectly co I dont really understand what is wrong. The only thing is that if you notice the older piece there is the Model property.Position when filling the value. Please see my notes in the code. I have Inotify in the model as advised and in the vm
public class SettingsViewModel : ObservableObject
{
private List<CustomTextSize> _textSize = new List<CustomTextSize>();
private CustomTextSize _selectedTextSize;
public CustomTextSize SelectedTextSize
{
get => _selectedTextSize; // doesnt go through here
set
{
if (_selectedTextSize != value && _selectedTextSize != null)
{
this.IsBusy = true;
SetProperty(ref _selectedTextSize, value);
NotificationService.ShowToast("Probíhá ukládání, prosím čekejte");
UserSettings newSetting = new UserSettings()
{
DateSaved = DateTime.Now,
OptionId = (int)SettingOption.TextFontSize,
Value = value.Position.ToString()
};
new Thread(new ThreadStart(async () =>
{
await LangUpDataSaverLoader.SaveUserSettings(newSetting);
LangUpDataSaverLoader.LoadUserSettingsAndFillAppValues();
})).Start();
this.IsBusy = false;
}
}
}
public List<CustomTextSize> TextSize
{
get => _textSize;
set => SetProperty(ref _textSize, value);
}
_textSize = FillTextSizeOptions();
var customSize = _textSize.FirstOrDefault(m => m.Value == LangUpUserCustomSettings.CustomFontSize); // here should be position? But then I am getting that I cannot conevrt int to Model.
if (customSize != null)
{
_selectedTextSize = customSize;
}
}
Model
public class CustomTextSize : ObservableObject {
public int Position { get; set; }
public int Value { get; set; }
public string Text { get; set; }
}
Xaml
<Picker x:Name="textSize" WidthRequest="300"
VerticalOptions="CenterAndExpand" FontSize="20"
Title="{ grial:Translate A_PickerSizeOfText }"
ItemsSource ="{Binding TextSize}" ItemDisplayBinding="{Binding Text}"
SelectedItem ="{Binding SelectedTextSize, Mode=TwoWay}"
grial:PickerProperties.BorderStyle="Default"
BackgroundColor="Transparent"
TextColor= "{ DynamicResource ListViewItemTextColor }" >
</Picker>
Old piece of code that I am rewrting
Spinner textSizeSpinner = new Spinner(MainActivity.Instance);
ArrayAdapter textSizeAdapter = new ArrayAdapter(MainActivity.Instance, Android.Resource.Layout.SimpleSpinnerDropDownItem);
textSizeAdapter.AddAll(FillTextSizeOptions().Select(m => m.Text).ToList());
textSizeSpinner.Adapter = textSizeAdapter;
textSizeSpinner.SetBackgroundColor(Color.FromHex("#3680b2").ToAndroid());
fontSizeStepper.Children.Add(textSizeSpinner);
initialTextSizeSpinnerPosition = FillTextSizeOptions().FirstOrDefault(m => m.Value == LangUpUserCustomSettings.CustomFontSize).Position; //here is the .Position
textSizeSpinner.SetSelection(initialTextSizeSpinnerPosition);
protected async void TextSizeSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
if (e.Position != initialTextSizeSpinnerPosition)
{
dialog.SetProgressStyle(ProgressDialogStyle.Spinner);
dialog.SetCancelable(false);
dialog.SetMessage("Probíhá ukládání, prosím čekejte");
dialog.Show();
Spinner spinner = (Spinner)sender;
Model.UserSettings newSetting = new Model.UserSettings()
{
DateSaved = DateTime.Now,
OptionId = (int)SettingOption.TextFontSize,
Value = FillTextSizeOptions().First(m => m.Position == e.Position).Value.ToString(),
};
await LangUpDataSaverLoader.SaveUserSettings(newSetting);
initialTextSizeSpinnerPosition = e.Position;
LangUpDataSaverLoader.LoadUserSettingsAndFillAppValues();
dialog.Dismiss();
}
}

Highlight URL using label span - xamarin.forms

I am creating a chat application in xamarin.forms.What I am trying to achieve is whenever user typed message contains a URL, that should be highlighted and provide click to it.For this feature I found Span in Label text.When user click on send button of chat , I will check for URL and make it as another span.I got this idea from Lucas Zhang - MSFT form this question here.
The problem is I am trying to do the spanning in view model and the individual chat bubble is in another view cell which will call as ItemTemplate in my chat listview. Anyway the spanning is not working as I intended ie; it doesn't highlight .
My view Model.
public Queue<Message> DelayedMessages { get; set; } = new Queue<Message>();
public ObservableCollection<Message> Messages { get; set; } = new ObservableCollection<Message>();
public string TextToSend { get; set; }
public ChatPageViewModel()
{
OnSendCommand = new Command(() =>
{
if (!string.IsNullOrEmpty(TextToSend))
{
var urlStr = TextToSend;
int startIndex = 0, endIndex = 0;
if (urlStr.Contains("www."))
{
startIndex = urlStr.IndexOf("www.");
}
if (urlStr.Contains(".com"))
{
endIndex = urlStr.IndexOf(".com") + 3;
}
if (startIndex != 0 || endIndex != 0)
{
var formattedString = new FormattedString();
Span span1 = new Span() { Text = urlStr.Substring(0, startIndex), TextColor = Color.Black };
formattedString.Spans.Add(span1);
Span span2 = new Span() { Text = urlStr.Substring(startIndex, endIndex - startIndex + 1), TextColor = Color.LightBlue };
span2.GestureRecognizers.Add(new TapGestureRecognizer()
{
NumberOfTapsRequired = 1,
Command = new Command(() => {
})
});
formattedString.Spans.Add(span2);
Span span3 = new Span() { Text = urlStr.Substring(endIndex, urlStr.Length - 1 - endIndex), TextColor = Color.Black };
formattedString.Spans.Add(span3);
var message = new Message
{
Text = formattedString.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
else
{
var message = new Message
{
Text = urlStr.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
}
});
}
Single chat Bubble XAML
<Label x:Name="OutgoingMessage" TextColor="White" FormattedText="{Binding Text}" HorizontalOptions="End" >
</Label>
My Chat page XAML
<Grid RowSpacing="0" Margin="0,20,0,0"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages,Mode=OneWay}"
Margin="0"
SelectionMode="None"
FlowDirection="RightToLeft"
HasUnevenRows="True" x:Name="ChatList"
VerticalOptions="FillAndExpand"
SeparatorColor="Transparent"
>
</ListView>
<BoxView HorizontalOptions="FillAndExpand"
HeightRequest="1"
BackgroundColor="#F2F3F5"
Grid.Row="1"/>
<partials:ChatInputBarView Grid.Row="2"
Margin="0,0,0,0"
x:Name="chatInput"/>
</Grid>
ChatPage.xaml.cs
public partial class ChatPage : ContentPage
{
ChatPageViewModel vm;
public ChatPage()
{
InitializeComponent();
this.BindingContext = vm= new ChatPageViewModel();
}
}
Messages class
public class Message : ObservableObject
{
string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}
Any Help is appreciated.
EDIT:
This question was actually continuation of question. Previously I used AwesomeHyperLinkLabel fromlink. The problem was I cant manage the click event of that label.Thats why I moved with label span.Thanks to Leo Zhu - MSFT For the render changes.
For Android:
[assembly: ExportRenderer(typeof(AwesomeHyperLinkLabel), typeof(AwesomeHyperLinkLabelRenderer))]
namespace App18.Droid
{
public class AwesomeHyperLinkLabelRenderer : LabelRenderer
{
public AwesomeHyperLinkLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (AwesomeHyperLinkLabel)Element;
if (view == null) return;
TextView textView = new TextView(Forms.Context);
textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
textView.SetTextColor(view.TextColor.ToAndroid());
// Setting the auto link mask to capture all types of link-able data
textView.AutoLinkMask = MatchOptions.All;
// Make sure to set text after setting the mask
textView.Text = view.Text;
AddHyperlinksManually(textView);
//textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
// overriding Xamarin Forms Label and replace with our native control
SetNativeControl(textView);
}
public static void AddHyperlinksManually(TextView _tv)
{
SpannableStringBuilder currentSpan = new SpannableStringBuilder(_tv.Text);
Linkify.AddLinks(currentSpan, MatchOptions.WebUrls);
var objects = currentSpan.GetSpans(0, currentSpan.Length(), Java.Lang.Class.FromType(typeof(URLSpan)));
var urlSpans = new URLSpan[objects.Length];
for (var i = 0; i < urlSpans.Length; i++)
{
urlSpans[i] = objects[i] as URLSpan;
}
foreach (URLSpan _url in urlSpans)
{
int iStart = currentSpan.GetSpanStart(_url);
int iEnd = currentSpan.GetSpanEnd(_url);
currentSpan.RemoveSpan(_url);
currentSpan.SetSpan(new CustomURLSpan(_url.URL), iStart, iEnd, SpanTypes.InclusiveInclusive);
_tv.SetText(currentSpan, TextView.BufferType.Normal);
_tv.MovementMethod = LinkMovementMethod.Instance;
}
}
public class CustomURLSpan : ClickableSpan
{
string mTargetURL;
public CustomURLSpan(string _url) {
mTargetURL =_url;
}
public override void OnClick(Android.Views.View widget)
{
//here you could handle the click event,and you could use MessagingCenter to send mTargetURL to your Page.
Console.WriteLine("Click");
}
}
}
The mistake was with my model.Changed string to FormattedString and also changed in the viewmodel
public class Message : ObservableObject
{
FormattedString text;
public FormattedString Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}

Why activity indicator takes nearly 3 seconds to start when I click button

I have ActivityIndicator in separate xaml file. Which I am using in MainPage xaml as CustomLoader. This CustomLoader visibility is being controlled by bool variable VisibleLoader
<custom:CustomLoader IsVisible="{Binding VisibleLoader}"
AbsoluteLayout.LayoutBounds="0.5,0.5,1,1"
AbsoluteLayout.LayoutFlags="All" Grid.Row="2">
</custom:CustomLoader>
In MainPageViewModel when I am clicking button stating ActivityIndicator it is taking time to start. See code below
MainPageViewModel code
private bool _visibleLoader = false;
public MainPageViewModel()
{
VisibleLoader = true;
current = this;
GetAllProducts();
SyncCommand = new Command(SyncDevice);
}
public bool VisibleLoader
{
get
{
return _visibleLoader;
}
set
{
this._visibleLoader = value;
NotifyPropertyChanged("VisibleLoader");
}
}
Button click event code
public async void SyncDevice()
{
try
{
VisibleLoader = true;
IsBusy = true;
PageMessage = string.Empty;
var strSerialNumber = DependencyService.Get<IDataHelper>().GetIdentifier();
//VisibleButtons = true;
resourceService = new ResourceService(new RequestProvider());
if (GlobalSettings.deviceId == 0)
{
DeviceDetails deviceDtls = await resourceService.GetDeviceDetails(strSerialNumber);
GlobalSettings.deviceId = deviceDtls.Id.Value;
}
if (GlobalSettings.deviceId.ToString() != null)
{
DevicesList data = await resourceService.GetProducts(GlobalSettings.deviceId);
//this.lstProducts = data.Devices[0].Products;
this.lstProducts = data != null ? data.Devices[0].Products : null;
if (lstProducts != null && lstProducts.Count != 0)
{
//Filter Products from Parameter file
var filterProducts = GlobalSettings.GetFilterProducts();
if (filterProducts.Count > 0)
{
//lstProducts = lstProducts.Where(product => !filterProducts.Any(fp => product.Name.ToLowerInvariant().Contains(fp.ToLowerInvariant()))).ToList();
lstProducts = lstProducts.Where(product => filterProducts.Any(fp => product.Name.ToLowerInvariant().Contains(fp.ToLowerInvariant()))).ToList();
}
TotalProducts = lstProducts.Count;
lstProducts = lstProducts.OrderBy(s => s.Order).ThenBy(s => s.Name).ToList();
}
else
{
PageMessage = "No products found.";
}
}
else
{
PageMessage = "Error occured. Please try again later.!";
}
NotifyPropertyChanged("lstProducts");
resourceService = new ResourceService(new RequestProvider());
await resourceService.SyncDevice(GlobalSettings.deviceId);
}
catch (Exception)
{
VisibleLoader = false;
}
VisibleLoader = false;
IsBusy = false;
}
}
Button in xaml file
<Button Text="Sync device" Command="{Binding SyncCommand}" HorizontalOptions="FillAndExpand"
BackgroundColor="LightBlue" Margin="5" TextColor="White" BorderRadius="10" HeightRequest="40"></Button>
How can I fix this issue?
Well first of all if your sync device method is not an event I would suggest you make it a task instead since async voids are not a good practice.
public async Task SyncDevice()
Also, it could be that your boolean in not changing in the main thread which is, in turn, causing this situation so try something like
Device.BeginInvokeOnMainThread(()=>{ //Change visibility... });
Make sure that you only add relevant code in here because this blocks the main thread, in turn, makes the UI freeze
Update
Then your command would look something like:
new Command(async ()=>{await SyncDevice()});

How to display the registered data from database in text field in Xamarin Forms to update

I would like to Update/Editthe player details in my Xamarin Forms app. Once the player is logged in to the app, on click on the profile image should navigate to Player Details (Register.xaml) screen with player details populated from database. How to get the data displayed in text fields?
// The Register.xaml:
<ContentPage.Content>
<StackLayout Spacing="20" Padding="20">
<Label Text="Player Details" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" FontSize="25"></Label>
<Entry x:Name="fullNameEntry" Placeholder="Full Name" Text="{Binding FullName}"></Entry>
<Entry x:Name="mobileEntry" Placeholder="Mobile" Text="{Binding Mobile}"></Entry>
<Entry x:Name="soccerpostionEntry" Placeholder="Soccer Position" Text="{Binding SoccerPosition}"></Entry>
<Button Text="Register" Clicked="RegisterSave_OnClicked" TextColor="White" BackgroundColor="ForestGreen"></Button>
</StackLayout>
</ContentPage.Content>
Below OnProfilePicClicked will grab the logged in user from database
private async void OnProfilePicClicked(object sender, EventArgs e)
{
//Navigate to Register screen with player data loaded:
var emailText = emailEntry.Text;
await Navigation.PushAsync(new Register(){});
List<PlayerDetails> details = (from x in conn.Table<PlayerDetails>() where x.Email == emailText select x).ToList();
if (details!= null)
{
// found the record
PlayerDetails playerDetails = new PlayerDetails();
playerDetails.FullName = details[0].FullName;
playerDetails.Mobile = details[0].Mobile;
playerDetails.SoccerPosition = details[0].SoccerPosition;
}
}
PlayerDetails model class:
string fullname;
string mobile;
string soccerposition;
public PlayerDetails()
{
}
public string FullName
{
set
{
if (fullname != value)
{
fullname = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FullName"));
}
}
}
get
{
return fullname;
}
}
public string Mobile
{
set
{
if (mobile != value)
{
mobile = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Mobile"));
}
}
}
get
{
return mobile;
}
}
public string SoccerPosition
{
set
{
if (soccerposition != value)
{
soccerposition = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SoccerPosition"));
}
}
}
get
{
return soccerposition;
}
}
Solution:
You should pass the model when you pushing to Register page.
In your Register page, add a PlayerDetails parameter in the construction function and set BindingContext to the model:
public partial class Register : ContentPage
{
PlayerDetails myDetails;
public Register(PlayerDetails playD)
{
InitializeComponent ();
myDetails = playD;
BindingContext = myDetails;
}
}
And when you push, pass the model:
private async void OnProfilePicClicked(object sender, EventArgs e)
{
//Navigate to Register screen with player data loaded:
var emailText = emailEntry.Text;
List<PlayerDetails> details = (from x in conn.Table<PlayerDetails>() where x.Email == emailText select x).ToList();
if (details != null)
{
// found the record
PlayerDetails playerDetails = new PlayerDetails();
playerDetails.FullName = details[0].FullName;
playerDetails.Mobile = details[0].Mobile;
playerDetails.SoccerPosition = details[0].SoccerPosition;
await Navigation.PushAsync(new Register(playerDetails) { });
}
else {
Console.WriteLine("Can't find the playerDetails");
}
}

How do I solve Xamarin.Forms.Xaml.XamlParseException

I am new to Xamarin Forms and C# too. Kindly help me to get rid of the above issue.
I am getting
Xamarin.Forms.Xaml.XamlParseException
while I am trying to add the string format of my selected image source to CandidateDetails.cs.
The pages are as follow:
CandidateDetails.cs
public event PropertyChangedEventHandler PropertyChanged;
private string imageBase64;
public string ImageBase64
{
get { return imageBase64; }
set
{
imageBase64 = value;
OnPropertyChanged("ImageBase64");
CandImage = Xamarin.Forms.ImageSource.FromStream(
() => new MemoryStream(Convert.FromBase64String(imageBase64)));
}
}
private Xamarin.Forms.ImageSource _candImage;
public Xamarin.Forms.ImageSource CandImage
{
get { return _candImage; }
set
{
_candImage = value;
OnPropertyChanged("CandImage");
}
}
public string _candName;
public string CandName
{
get { return _candName; }
set
{
if (_candName == value)
return;
_candName = value;
OnPropertyChanged("CandName");
}
}
public string _candInst;
public string CandInst
{
get { return _candInst; }
set
{
if (_candInst == value)
return;
_candInst = value;
OnPropertyChanged("CandInst");
}
}
public string _candEmailId;
public string CandEmailId
{
get { return _candEmailId; }
set
{
if (_candEmailId == value)
return;
_candEmailId = value;
OnPropertyChanged("CandEmailId");
}
}
public string _candMob;
public string CandMob
{
get { return _candMob; }
set
{
if (_candMob == value)
return;
_candMob = value;
OnPropertyChanged("CandMob");
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
CandidateDetailsModalPage.xaml.cs
public partial class CandidateDetailsModalPage : ContentPage
{
Stream input;
string imageAsString;
public static ObservableCollection<CandidateDetails> _candidate = new ObservableCollection<CandidateDetails>();
async void OnDoneClicked(object sender, System.EventArgs e)
{
_candidate.Add(new CandidateDetails
{
CandName = (string)candNameEntry.Text,
CandEmailId = (string)candEmailId.Text,
CandMob = (string)candMobNumber.Text,
ImageBase64 = imageAsString
});
await Navigation.PopModalAsync();
}
public CandidateDetailsModalPage()
{
InitializeComponent();
pickPhoto.Clicked += async (sender, args) =>
{
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert("Photos Not Supported", ":( Permission not granted to photos.", "OK");
return;
}
var file = await CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
if (file == null)
return;
input = file.GetStream();
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
imageAsString = Convert.ToBase64String(ms.ToArray());
}
//image.Source = ImageSource.FromStream(() => new MemoryStream(Convert.FromBase64String(imageAsString)));
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
};
}
}
CandidateDetailsModalPage.xaml
<StackLayout Orientation="Vertical" BackgroundColor="#3B4371" Padding="0">
<Button Text="Done" Clicked="OnDoneClicked" />
<ic:CircleImage x:Name="image"/>
<Button x:Name="pickPhoto" Text="Pick Photo"/>
<StackLayout VerticalOptions="FillAndExpand">
<Entry x:Name="candNameEntry" Placeholder="Candidate Name" />
<Entry x:Name="candInst" Placeholder="Candidate Institution / Party"/>
<Entry x:Name="candEmailId" Placeholder="Candidate Email Id" />
<Entry x:Name="candMobNumber" Placeholder="Candidate Mobile Number"/>
</StackLayout>
</StackLayout>
The following is the backend of the xaml page where I am displaying the data CandidateDisplayPage.xaml.cs
candidateListView.ItemsSource = CandidateDetailsModalPage._candidate;
CandidateDisplayPage
<ListView x:Name="candidateListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image x:Name="candImage" ImageSource="{Binding Path=CandImage}"/>
<Label Text="{Binding CandName}" x:Name="candName"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You say in a comment (that should have been part of the original report):
This is the error that's causing the exception Cannot assign property "ImageSource": Property does not exists, or is not assignable, or mismatching type between value and property
Indeed, Image doesn't have an ImageSource property, but instead a Source property of type ImageSource.
public ImageSource Source { get; set; }
So your Xaml should look like
Image x:Name="candImage" Source="{Binding Path=CandImage}"/>

Resources