How do I solve Xamarin.Forms.Xaml.XamlParseException - xamarin

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}"/>

Related

xamarin xzing barcode scanner re-scan

I'm using the zxing barcode scanner in xamarin android forms and I can get it to scan one barcode with no issues, but I want to be able to discard the scan they have taken and have the ability to take a another scan.
I'm also using MVVM. Here is my xaml...
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<forms:ZXingScannerView x:Name="zxingView"
IsTorchOn="{Binding TorchON}"
IsScanning="{Binding IsScanning}"
IsAnalyzing="{Binding IsAnalyzing}"
Result="{Binding Result, Mode=TwoWay}"
ScanResultCommand="{Binding ScanCommand}"
/>
<forms:ZXingDefaultOverlay
x:Name="scannerOverlay"
BottomText="Place the red line over the barcode you'd like to scan." />
<Button Grid.Row="1" Text="Toggle Flash" Command="{Binding FlashToggleCommand}"></Button>
</Grid>
And this is my page model
private string barcode = string.Empty;
public string Barcode
{
get { return barcode; }
set { barcode = value; }
}
private bool _isAnalyzing = true;
public bool IsAnalyzing
{
get { return _isAnalyzing; }
set
{
if (!Equals(_isAnalyzing, value))
{
_isAnalyzing = value;
OnPropertyChanged("IsAnalyzing");
}
}
}
private bool _isScanning = true;
private bool _torchON = false;
private DynamicContainerPageModel _hhtScreen;
private readonly IDeviceManager _deviceManager;
public ScanningViewPageModel(IDeviceManager deviceManager)
{
_deviceManager = deviceManager;
}
public override void Init(object initData)
{
base.Init(initData);
_hhtScreen = initData as DynamicContainerPageModel;
}
public bool IsScanning
{
get { return _isScanning; }
set
{
if (!Equals(_isScanning, value))
{
_isScanning = value;
OnPropertyChanged("IsScanning");
}
}
}
public bool TorchON
{
set
{
if (_torchON != value)
{
_torchON = value;
OnPropertyChanged("TorchON");
}
}
get { return _torchON; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Command ScanCommand
{
get
{
return new Command(() =>
{
IsAnalyzing = false;
IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
Barcode = Result.Text;
var response = await CoreMethods.DisplayAlert("Barcode found", "Found: " + Result.Text, "Keep",
"Scan Again");
if (response)
{
//Save the value into the model
_deviceManager.BeginInvokeOnMainThread(() =>
{
_hhtScreen.SelectedControl.Text = barcode;
});
//close page
await this.CoreMethods.PopPageModel(false);
}
else
{
Result = null;
IsAnalyzing = true;
IsScanning = true;
}
});
IsAnalyzing = true;
IsScanning = true;
});
}
}
public Command FlashToggleCommand
{
get { return new Command(async () => { TorchON = !TorchON; }); }
}
public Result Result { get; set; }
When I press scan again on my pop up, I find it a bit hit and miss whether the scanning camera activates again or not, majority of the time it just freezes. Am I doing something wrong? Is there a better way to get the control to rescan?
I've hit a very similar problem in the past. What I had to end up doing was defining the Scanner view in the code behind, then conditionally add/remove from the view as needed.
This is what mine ended up looking like:
The XAML:
<!--
The barcode scanner grid. The actual barcode scanner is
created and added to the grid in the code behind class.
-->
<Grid x:Name="ScannerViewGrid"
Grid.Row="3"
HorizontalOptions="FillAndExpand"
IsVisible="{Binding IsBarcodeScannerRunning}"
VerticalOptions="FillAndExpand" />
The code behind:
private ZXingDefaultOverlay scannerOverlay;
private ZXingScannerView scannerView;
private void CreateNewScannerView()
{
var vm = BindingContext.DataContext as SearchViewModel;
ScannerViewGrid.Children.Clear();
scannerOverlay = null;
scannerView = null;
scannerOverlay = new ZXingDefaultOverlay();
scannerOverlay.ShowFlashButton = false;
scannerView = new ZXingScannerView();
scannerView.SetBinding(ZXingScannerView.ResultProperty, nameof(vm.BarcodeScanResult), BindingMode.OneWayToSource);
scannerView.SetBinding(ZXingScannerView.ScanResultCommandProperty, nameof(vm.BarcodeScanResultCommand));
scannerView.IsScanning = true;
ScannerViewGrid.Children.Add(scannerView);
ScannerViewGrid.Children.Add(scannerOverlay);
}
private void RemoveScannerView()
{
ScannerViewGrid.Children.Clear();
scannerView.IsScanning = false;
scannerView.IsAnalyzing = false;
scannerView.RemoveBinding(ZXingScannerView.ResultProperty);
scannerView.RemoveBinding(ZXingScannerView.ScanResultCommandProperty);
scannerView = null;
scannerOverlay = null;
}
How I solve this:
Xaml
<coreControls:ContainerLayout.Content>
<Grid>
<ContentView
x:Name="contentViewCamera"/>
<coreControls:SvgImage
SvgSource="img_qr_background"
VerticalOptions="FillAndExpand"
Aspect="AspectFill"/>
<Grid
x:Name="mainLayout"
RowDefinitions="48,*,*,*">
<contentViews:HeaderView Title="Canjear cupon"
TitleColor="{AppThemeBinding Light={StaticResource LightWhiteColor}, Dark={StaticResource DarkWhiteColor}}"
RightIconSvg="ic_flash_w"
Margin="6,0" />
<material:MaterialEntry
Grid.Row="3"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="240"
HeightRequest="42"
BackgroundColor="#70000000"
BorderColor="#70000000"
Placeholder="Ingresa el codigo"/>
</Grid>
</Grid>
</coreControls:ContainerLayout.Content>
code behind
public partial class RedeemCouponPage
{
private ZXingScannerView _scannerView;
public RedeemCouponPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
//mainLayout.Padding = PageHelper.GetPageSafeArea(new Thickness(0, 20), this);
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
var options = new MobileBarcodeScanningOptions
{
AutoRotate = false,
TryHarder = true,
TryInverted = true,
DelayBetweenAnalyzingFrames = 5,
DelayBetweenContinuousScans = 5,
PossibleFormats = new List<BarcodeFormat>() { BarcodeFormat.QR_CODE }
};
ViewModel.InitializeCameraCommand = new MvxCommand(() =>
{
_scannerView = new ZXingScannerView()
{
Options = options,
ScanResultCommand = ViewModel.ScanResultCommand
};
_scannerView.SetBinding(ZXingScannerView.IsScanningProperty, nameof(ViewModel.IsBusy));
_scannerView.SetBinding(ZXingScannerView.IsAnalyzingProperty, nameof(ViewModel.IsBusy));
_scannerView.SetBinding(ZXingScannerView.IsTorchOnProperty, nameof(ViewModel.IsFlashActive));
_scannerView.SetBinding(ZXingScannerView.ResultProperty, nameof(ViewModel.Result), BindingMode.TwoWay);
contentViewCamera.Content = _scannerView;
});
}
}
viewModel
public class RedeemCouponViewModel: BaseViewModel
{
private readonly ICouponService CouponService = Mvx.IoCProvider.Resolve<ICouponService>();
public IMvxCommand InitializeCameraCommand { get; set; }
private ZXing.Result _result;
public ZXing.Result Result
{
get => _result;
set
{
SetProperty(ref _result, value);
BarCodeText = value.Text;
}
}
private bool _isFlashActive;
public bool IsFlashActive
{
get => _isFlashActive;
set => SetProperty(ref _isFlashActive, value);
}
private string _barCodeText;
public string BarCodeText
{
get => _barCodeText;
set => SetProperty(ref _barCodeText, value);
}
private Coupon _coupon;
public Coupon Coupon
{
get => _coupon;
set => SetProperty(ref _coupon, value);
}
public override void ViewAppeared()
{
base.ViewAppeared();
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
IsBusy = true;
}
public IMvxAsyncCommand ScanResultCommand => new MvxAsyncCommand(async () =>
{
if (Result == null)
{
IsBusy = true;
return;
}
else
IsBusy = false;
Coupon = await CouponService.Get(BarCodeText);
PopupIsVisible = true;
}, () => IsBusy);
public IMvxCommand ConfirmRedeemCommand => new MvxCommand(()=>
{
DisplaySuccessView("Canjeado!","El cupon ha sido canjeado con exito.");
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
PopupIsVisible = false;
IsBusy = true;
});
public IMvxCommand BackToScanerCommand => new MvxCommand(() =>
{
PopupIsVisible = false;
if (DeviceInfo.DeviceType == DeviceType.Physical)
InitializeCameraCommand.Execute();
IsBusy = true;
});
}

Xamarin Forms: How to Change the textcolor of Collectionview SelectedItem?

I have a CarouselPage having 5 children and every child has a horizontal collection view. When selecting an item in Collectionview or swiping the pages, I need to give a different text color and need to add an underline for the selected item. I have tried like below:
CarouselHomePage.cs
public partial class CarouselHomePage : CarouselPage
{
public List<Activity> activityList { get; set; }
public CarouselHomePage()
{
InitializeComponent();
activityList = new List<Activity>();
AddActivities();
MessagingCenter.Subscribe<App, string>((App)Xamarin.Forms.Application.Current, "child", (s, child) =>
{
CurrentPage = Children[Int32.Parse(child)];
});
}
private void AddActivities()
{
activityList.Add(new Activity() { Title = "PageNumber1" });
activityList.Add(new Activity() { Title = "PageNumber2" });
activityList.Add(new Activity() { Title = "PageNumber3" });
activityList.Add(new Activity() { Title = "PageNumber4" });
activityList.Add(new Activity() { Title = "PageNumber5" });
AddChild(activityList);
}
public void AddChild(List<Activity> activityList)
{
this.Children.Add(new PageNumber1(activityList));
this.Children.Add(new PageNumber2(activityList));
this.Children.Add(new PageNumber3(activityList));
this.Children.Add(new PageNumber4(activityList));
this.Children.Add(new PageNumber5(activityList));
}
}
Activity.cs
public class Activity
{
public string Title { get; set; }
public bool visibility { get; set; }
public bool Visibility
{
set
{
if (value != null)
{
visibility = value;
NotifyPropertyChanged();
}
}
get
{
return visibility;
}
}
private Color textColor;
public Color TextColor
{
set
{
if (value != null)
{
textColor = value;
NotifyPropertyChanged();
}
}
get
{
return textColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
PageNumber1.xaml
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<CollectionView
SelectionMode="Single"
x:Name="ActivityList"
Margin="5,10,5,10"
SelectionChanged="TagItemTapped"
ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout
Orientation="Vertical"
Margin="15">
<Label
TextColor="{Binding TextColor}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Text="{Binding Title}">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>18</OnIdiom.Phone>
<OnIdiom.Tablet>27</OnIdiom.Tablet>
<OnIdiom.Desktop>18</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<BoxView
HeightRequest="2"
IsVisible="{Binding Visibility}"
BackgroundColor="{Binding TextColor}"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Start"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>30</OnIdiom.Phone>
<OnIdiom.Tablet>60</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</CollectionView.HeightRequest>
</CollectionView>
<Label Text="Welcome to PageNumber1"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
PageNumber1.xaml.cs
public partial class PageNumber1 : ContentPage
{
public PageNumber1(List<Activity> activityList)
{
InitializeComponent();
if (activityList == null)
{
ActivityList.IsVisible = false;
}
else
{
for (int i = 0; i < activityList.Count; i++)
{
if (activityList[i].Title == "PageNumber1")
{
activityList[i].TextColor = Color.FromHex("#26b4d8");
activityList[i].Visibility = true;
}
else
{
activityList[i].TextColor = Color.Gray;
activityList[i].Visibility = false;
}
}
ActivityList.ItemsSource = activityList;
}
}
public void TagItemTapped(object sender, SelectionChangedEventArgs e)
{
var selectedItem = (e.CurrentSelection.FirstOrDefault() as Activity);
if (selectedItem != null)
{
string childnumber = "";
if (selectedItem.Title == "PageNumber1")
{
childnumber = "0";
}
else if (selectedItem.Title == "PageNumber2")
{
childnumber = "1";
}
else if (selectedItem.Title == "PageNumber3")
{
childnumber = "2";
}
else if (selectedItem.Title == "PageNumber4")
{
childnumber = "3";
}
else if (selectedItem.Title == "PageNumber5")
{
childnumber = "4";
}
MessagingCenter.Send<App, string>((App)Xamarin.Forms.Application.Current, "child", childnumber);
}
}
}
I have added the same code on all the other child pages with the corresponding title in the if statement. But the selected page title color is not working and underline is not showing.
Screenshot:
Also if I select the last item in the collectionview, I need to scroll the collection on the last child to the last item. For this I have used ScrollTo feature of Collectioview. But that is also not working.
protected override void OnAppearing()
{
ActivityList.ScrollTo(4);
}
The above code will work if I manually swipe the pages. When directly tap the collectionview item, the scrolling is not working.
I have uploaded a sample project here.
About underline not showing , the reason is HeightRequest of CollectionView setted too small with 30 .
Modify that to above 35 , it will show correcttly . Such as :
<CollectionView.HeightRequest>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>40</OnIdiom.Phone>
<OnIdiom.Tablet>60</OnIdiom.Tablet>
<OnIdiom.Desktop>30</OnIdiom.Desktop>
</OnIdiom>
</CollectionView.HeightRequest>
The effect :
About selected problem , this is the sample project here .

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); }
}
}

Xamarin forms understanding ObservableCollection binding context

I'm having some issue getting my ObservableCollection to bind to alexrainman CarouselView.
After reading some basic articles I created my view model:
public class PostObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string postOwner = string.Empty;
string id = string.Empty;
string profileimage = string.Empty;
string post = string.Empty;
List<string> postimages = null;
public string PostOwner
{
set
{
if (postOwner != value)
{
postOwner = value;
OnPropertyChanged("PostOwner");
}
}
get
{
return postOwner;
}
}
public string Id {
set
{
if (id != value)
{
id = value;
OnPropertyChanged("Id");
}
}
get
{
return id;
}
}
public string Post
{
set
{
if (post != value)
{
post = value;
OnPropertyChanged("Post");
}
}
get
{
return post;
}
}
public string ProfileImage
{
set
{
if (profileimage != value)
{
profileimage = value;
OnPropertyChanged("ProfileImage") ;
}
}
get
{
return profileimage;
}
}
public List<string> PostImages
{
set
{
if (postimages != value)
{
postimages = value;
OnPropertyChanged("PostImages");
}
}
get
{
return postimages;
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I retrieve my data via a REST call to my server:
public static bool GetMyPostData(ref ObservableCollection<PostObject> myPosts, string groupid, string apikey)
{
try
{
string newURL = URL + "GetPosts";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
MultipartFormDataContent formdata = new MultipartFormDataContent
{
{ new StringContent(apikey), "apikey" },
{ new StringContent(groupid), "groupid" }
};
HttpResponseMessage response = client.PostAsync(newURL, formdata).Result; // Blocking call! Program will wait here until a response is received or a timeout occurs.
if (response.IsSuccessStatusCode)
{
try
{
myPosts = response.Content.ReadAsAsync<ObservableCollection<PostObject>>().Result;
}
catch (Exception e)
{
Debug.WriteLine(e);
return false;
}
}
}
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return false;
}
}
Which works I get my data correctly, now I set up my Binding context like so:
ObservableCollection<PostObject> GroupPosts = new ObservableCollection<PostObject>();
public Posts (GroupInfo ginfo)
{
InitializeComponent ();
GroupTitle.Text = ginfo.Title;
CurrentGroupInfo = ginfo;
GetDataPosts();
BindingContext = GroupPosts;
}
public void GetDataPosts()
{
try
{
GroupPosts.Clear();
if (RestController.GetMyPostData(ref GroupPosts, CurrentGroupInfo.Id.ToString(), apikey))
{
Debug.WriteLine("Data downloaded");
}
}
catch(Exception e)
{
Debug.WriteLine(e.Message);
}
And finally I have my XAML set up like 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:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
xmlns:cv="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"
NavigationPage.HasNavigationBar="True"
NavigationPage.HasBackButton="False"
NavigationPage.BackButtonTitle="Back"
x:Class="forms.Posts">
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal" VerticalOptions="Center" Spacing="10" >
<Label x:Name="GroupTitle" TextColor="White" FontSize="Medium"/>
</StackLayout>
</NavigationPage.TitleView>
<ContentPage.ToolbarItems>
<ToolbarItem Name="iconexample" Icon="settings.png" Priority="0" Order="Primary" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<cv:CarouselViewControl x:Name="carousel"
ItemsSource="{Binding PostImages}"
ShowArrows="true"
ShowIndicators="true"
Orientation="Horizontal">
</cv:CarouselViewControl>
</ContentPage.Content>
</ContentPage>
However I get an error
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
So I'm not sure what I'm missing or I need to read up on this a little more? any help would be great.
You want to do a few changes here:
Change the field definition to a property, you won't be able to bind to the field:
public ObservableCollection<PostObject> GroupPosts { get; } = new ObservableCollection<PostObject>();
If you updating the reference then you have to raise property changed event, so your property definition should look like that:
private ObservableCollection<PostObject> _groupPosts = new ObservableCollection<PostObject>();
public ObservableCollection<PostObject> GroupPosts
{
get { return _groupPosts; }
set
{
_groupPosts = value;
RaisePropertyChanged(.....); // here you should notify your binding that value has changed
}
}
Because you are trying to pass this list by reference (ref parameter), you won't be able to compile it with a property so it's better just to return value from your data provider and then apply it:
GroupPosts.Clear();
var newData = RestController.GetMyPostData(CurrentGroupInfo.Id.ToString(), apikey);
GroupPosts = newData;
it's a bad practice to pass the observable collection to an underlying data provider because it will limit it to operate on UI thread only (otherwise after updating the collection on non-ui thread you can crash the app). But this is a top for another post :)

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");
}
}

Resources