How to add Photo from Gallery in CollectionView Project? - c#

I have a collection view and I want to add a photo from the gallery to it.If I add for example picture from gallery in tag Image.it works.But I want to add it in my collection view and I don`t understand why it does not add picture
XAML
<StackLayout>
<Button Text="Select"
Clicked="Handle_Clicked" />
<StackLayout HeightRequest="120" BackgroundColor="LightGray">
<!-- <Label Text="No photo yet" TextColor="#616161" HorizontalOptions="CenterAndExpand" FontSize="Large"
VerticalOptions="CenterAndExpand" ></Label>-->
<CollectionView x:Name="AddCar" ItemsSource="{Binding Types}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal"
/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="120" />
</Grid.RowDefinitions>
<Frame CornerRadius="10" BorderColor="Black" Margin="5,5,5,5" Padding="0" >
<Image Source="{Binding Source}"
HorizontalOptions="Center"
BackgroundColor="{Binding CustButtonColor}"/>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</StackLayout>
Code Behind
public MainPage()
{
InitializeComponent();
BindingContext = new VM();
}
async void Handle_Clicked(object sender, System.EventArgs e)
{
//! added using Plugin.Media;
await CrossMedia.Current.Initialize();
var image = new Image();
//// if you want to take a picture use this
// if(!CrossMedia.Current.IsTakePhotoSupported || !CrossMedia.Current.IsCameraAvailable)
/// if you want to select from the gallery use this
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert("Not supported", "Your device does not currently support this functionality", "Ok");
return;
}
//! added using Plugin.Media.Abstractions;
// if you want to take a picture use StoreCameraMediaOptions instead of PickMediaOptions
var mediaOptions = new PickMediaOptions()
{
PhotoSize = PhotoSize.Medium
};
// if you want to take a picture use TakePhotoAsync instead of PickPhotoAsync
var selectedImageFile = await CrossMedia.Current.PickPhotoAsync(mediaOptions);
/* if (selectedImage == null)
{
await DisplayAlert("Error", "Could not get the image, please try again.", "Ok");
return;
}
*/
image.Source = ImageSource.FromStream(() => selectedImageFile.GetStream());
var page = new VM();
page.Types.Add(image);
}
}
VM
class VM : INotifyPropertyChanged
{
// public Command Photo { get; set; }
public ObservableCollection<Image> types { get; set; }
public ObservableCollection<Image> Types { get => types; set { types = value; OnPropertyChanged("Types"); } }
public VM()
{
// Photo = new Command(OnPickPhotoButtonClicked);
Types = new ObservableCollection<Image>();
Types.Add(new Image() { Source = "heart", BackgroundColor = Color.White });
Types.Add(new Image() { Source = "heart", BackgroundColor = Color.White });
Types.Add(new Image() { Source = "heart", BackgroundColor = Color.White });
Types.Add(new Image() { Source = "heart", BackgroundColor = Color.White });
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Please help. There is a link to my project on GitHub
https://github.com/kamenskyh/PhotoFromGallery/tree/master/Photos

first, keep a reference to your VM
VM ViewModel;
public MainPage()
{
InitializeComponent();
BindingContext = ViewModel = new VM();
}
then, when you add the picture, do NOT create a new VM instance
var page = new VM();
page.Types.Add(image);
instead, use the instance of the VM your page is already bound to
ViewModel.Types.Add(image);

Related

CollectionView updating ImageButton scroll repeating UI issue

I've got a CollectionView containing an ImageButton. When the image is pressed I replace a.png with b.png.
This is working fine but when I scroll down the list, every 10th item is now showing b.png!
If instead of setting the button.source, I called the line below again after saving to the DB which solves my problem but then I start at the top of the list and not at the current position I was at:
ItemsListView.ItemsSource = items;
How can I set the button.source without it creating this bug on every 10th item?
<CollectionView x:Name="Items">
<CollectionView.ItemTemplate>
<DataTemplate>
<ImageButton CommandParameter="{Binding Id}" Source="a.png" Clicked="OnInventoryClicked" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
void OnInventoryClicked(object sender, EventArgs e)
{
var button = (sender as ImageButton);
var itemId = button.CommandParameter.ToString();
var inventoryItem = await App.Database.GetItemAsync(itemId);
inventoryItem.IsInInventory = !inventoryItem.IsInInventory;
await App.Database.SaveItemAsync(inventoryItem);
button.Source = inventoryItem.IsInInventory? "b.png" : "a.png";
}
You could change with the souce property.
Xaml:
<CollectionView x:Name="Items" ItemsSource="{Binding infos}">
<CollectionView.ItemTemplate>
<DataTemplate>
<!--<ImageButton
Clicked="OnInventoryClicked"
CommandParameter="{Binding Id}"
Source="a.png" />-->
<ImageButton Clicked="ImageButton_Clicked" Source="{Binding image}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Info> infos { get; set; }
public MainPage()
{
InitializeComponent();
infos = new ObservableCollection<Info>()
{
new Info{image = "pink.jpg" },
new Info{image = "pink.jpg" },
new Info{image = "pink.jpg" },
new Info{image = "pink.jpg" },
new Info{image = "pink.jpg" },
};
this.BindingContext = this;
}
private void ImageButton_Clicked(object sender, EventArgs e)
{
var button = (sender as ImageButton);
button.Source = "dog.jpg";
}
}
public class Info
{
public string image { get; set; }
}

Xamarin Forms Stop loading data in CollectionView

I have a problem. I created a CollectionView that uses a custom ViewModel. In that ViewModel I do a webcall to my webpage to get 20 filenames of images. After I got the result I do foreach filename a call to get the ImageSource of that filename. Now I created a Load data incrementally code to load the CollectionView data in bundles of 20. Here is my xaml:
<ContentPage.Content>
<StackLayout HorizontalOptions="Fill" Padding="15">
<Frame IsClippedToBounds="True" HeightRequest="45" CornerRadius="5" Padding="0" BackgroundColor="Transparent">
<Entry Placeholder="Search" ReturnType="Done" PlaceholderColor="Gray" x:Name="txtSearch" Margin="5,0,0,0" TextColor="White" />
</Frame>
<CollectionView ItemsSource="{Binding sourceList}" RemainingItemsThreshold="6"
RemainingItemsThresholdReachedCommand="{Binding LoadTemplates}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ff:CachedImage
Source="{Binding Source}"
VerticalOptions="Center"
HorizontalOptions="Center"
WidthRequest="{Binding WidthHeight}"
HeightRequest="{Binding WidthHeight}">
<ff:CachedImage.GestureRecognizers>
<TapGestureRecognizer Tapped="imgTemplate_Clicked" />
</ff:CachedImage.GestureRecognizers>
</ff:CachedImage>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
Here is the page constructor:
public TemplateList()
{
InitializeComponent();
TemplateListViewModel vm = new TemplateListViewModel();
BindingContext = vm;
}
Here is the ViewModel:
public class TemplateListViewModel
{
public ICommand LoadTemplates => new Command(LoadTemplateList);
public int CurrentTemplateCountReceived;
public ObservableCollection<TemplateSource> sourceList { get; set; }
public double MemeWidthHeight { get; set; }
public TemplateListViewModel()
{
CurrentTemplateCountReceived = 0;
sourceList = new ObservableCollection<TemplateSource>();
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
var width = mainDisplayInfo.Width;
var density = mainDisplayInfo.Density;
var ScaledWidth = width / density;
MemeWidthHeight = (ScaledWidth / 2);
loadingTemplates += onLoadingTemplates;
LoadTemplateList();
}
private event EventHandler loadingTemplates = delegate { };
private void LoadTemplateList()
{
loadingTemplates(this, EventArgs.Empty);
}
private async void onLoadingTemplates(object sender, EventArgs args)
{
List<Template> templateList = await App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived);
foreach (var template in templateList)
{
ImageSource source = ImageSource.FromUri(new Uri("mysite.org/myapp/" + template.FileName));
TemplateSource templateSource = new TemplateSource { Id = template.Id, Source = source, WidthHeight= MemeWidthHeight, FileName = template.FileName };
sourceList.Add(templateSource);
}
CurrentTemplateCountReceived = sourceList.Count;
}
}
Now App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived); just returns me a list with filenames, but the problem is that it keeps doing webcalls when I got nothing to receive anymore. On my server I have 38 images, so after 2 webcalls the app got everything. After that the result that the app receives from the webcall is "Nothing".
So my question is:
How can I stop doing the webcalls when I am at the bottom of my CollectionView?
bool moreData = true;
private async void onLoadingTemplates(object sender, EventArgs args)
{
if (!moreData) return;
List<Template> templateList = await App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived);
if (templateList is null or templateList.Count == 0) {
moreData = false;
return;
}
foreach (var template in templateList)
{
ImageSource source = ImageSource.FromUri(new Uri("mysite.org/myapp/" + template.FileName));
TemplateSource templateSource = new TemplateSource { Id = template.Id, Source = source, WidthHeight= MemeWidthHeight, FileName = template.FileName };
sourceList.Add(templateSource);
}
CurrentTemplateCountReceived = sourceList.Count;
}

Capture photo in xamarin forms

I want to take multiple photos and it is shown in a list, but I must use the mvvm structure if I put this code in the CS of the XAML it works but I cannot use it that way ...
The view model works and everything but does not show the image on the screen
What can be ??
You can tell me that I am doing wrong he tried everything but he does not show me the image.
Reference XAML (view) SEE MODEL and MODEL
public class CapturePhotoViewModel : BaseViewModel
{
ObservableCollection Photos = new ObservableCollection();
public ICommand CapturePhotoCommand => new Command(async (s) => await CaptureFoto());
private async Task CaptureFoto()
{
var IniciandoEvento = await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsPickPhotoSupported || !CrossMedia.IsSupported || !IniciandoEvento)
{
await DialogService.DisplayAlertAsync("No se puede Acceder a la camara", "¡Error!", "Aceptar");
return;
}
var newPhoto = Guid.NewGuid();
using (var photo = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
{
Name = newPhoto.ToString(),
SaveToAlbum = true,
DefaultCamera = CameraDevice.Rear,
Directory = "Demo",
CustomPhotoSize = 50
}))
{
if (string.IsNullOrWhiteSpace(photo?.Path))
{
return;
}
var newphotomedia = new MediaModel()
{
MediaID = newPhoto,
Path = photo.Path,
LocalDateTime = DateTime.Now
};
Photos = new ObservableCollection<MediaModel>();
Photos.Add(newphotomedia);
}
}
}
THIS IS MY XAML CODE which goes the list of image that is captured, but I do not know what I am doing wrong and does not show me the image
<Button Text="Tomar Foto" x:Name="photobutton" Command="{Binding CapturePhotoCommand}"/>
<ListView x:Name="ListPhotos" ItemsSource="{Binding Photos.Source}" RowHeight="400"
HeightRequest="300">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Frame OutlineColor="LightGray" HasShadow="true" Margin="4" WidthRequest="200" HeightRequest="250">
<Frame.Content>
<StackLayout>
<Image Source="{Binding Source}" VerticalOptions="StartAndExpand" HeightRequest="280"/>
<Button Text="Delete" />
</StackLayout>
</Frame.Content>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Y Este es mi model
public class MediaModel
{
public Guid MediaID { get; set; }
public string Path { get; set; }
public DateTime LocalDateTime { get; set; }
private FileImageSource source = null;
public FileImageSource Source => source ?? (source = new FileImageSource() { File = Path });
}
first, get rid of this line
Photos = new ObservableCollection<MediaModel>();
then fix your ItemsSource binding
<ListView x:Name="ListPhotos" ItemsSource="{Binding Photos}" RowHeight="400"
then fix your Image binding
<Image Source="{Binding Path}" VerticalOptions="StartAndExpand" HeightRequest="280"/>

How to Insert Method Inside MasterDetailPage XamarinForms

I have masterdetailpage that have Logout function inside that (so instead navigating to other page it will shows display alert), but i dont know how to insert the logout method inside the masterdetailpage, i already tried using ICommand but seem it didnt works and make my application force close .
Here is my MasterPageItem Model
public class MasterPageItem
{
public string Title { get; set; }
public string Icon { get; set; }
public Type TargetType { get; set; }
public ICommand Commando { get; set; }
}
here is the listview for MasterDetailPage
<ListView x:Name="navigationDrawerList"
RowHeight="45"
SeparatorVisibility="None"
BackgroundColor="#000000"
ItemSelected="OnMenuItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!-- Main design for our menu items -->
<StackLayout BackgroundColor="#000000" VerticalOptions="FillAndExpand"
Orientation="Horizontal"
Padding="20,10,0,10"
Spacing="20">
<Image Source="{Binding Icon}"
WidthRequest="60"
HeightRequest="60"
VerticalOptions="Center" />
<Label FontFamily="Panton-LightCaps.otf#Panton-LightCaps" Text="{Binding Title}"
FontSize="Medium"
VerticalOptions="Center"
TextColor="White"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and i tried to insert the method like this
public ICommand GetOff { get; private set; }
public MainPage()
{
GetOff = new Command(LogoutCommand)
var page9 = new MasterPageItem() { Title = "LOGOUT", Commando = GetOff };
}
public async void LogoutCommand ()
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result == true)
{
App.AuthenticationClient.UserTokenCache.Clear(Constants.ApplicationID);
Application.Current.MainPage = new NavigationPage(new NewPageLogin());
}
}
Is there another way to insert method inside MasterdetailPage ? Any suggestion would be appreciated
Option 1
You can add a footer to your listview and attach a tap gesture recognizer to it, like this:
<ListView.Footer>
<StackLayout BackgroundColor="#000000"
VerticalOptions="FillAndExpand"
Orientation="Horizontal"
Padding="20,10,0,10"
Spacing="20">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LogoutCommand}" />
</StackLayout.GestureRecognizers>
<Image Source="YourIcon"
WidthRequest="60"
HeightRequest="60"
VerticalOptions="Center" />
<Label FontFamily="Panton-LightCaps.otf#Panton-LightCaps"
Text="{Binding Title}"
FontSize="Medium"
VerticalOptions="Center"
TextColor="White"/>
</StackLayout>
</ListView.Footer>
It should go inside the ListView tag.
As you see, it supports Command, so you can use the one you already have.
Option 2
You can set the TargetType of your sign out item to null and do something like this:
private void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item == null)
return;
// Check if sign out was tapped
if (item.TargetType != null)
{
var page = (Page)Activator.CreateInstance(item.TargetType);
page.Title = item.Title;
Detail = new NavigationPage(page);
IsPresented = false;
}
else
{
// Manage your sign out action
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result == true)
{
App.AuthenticationClient.UserTokenCache.Clear(Constants.ApplicationID);
Application.Current.MainPage = new NavigationPage(new NewPageLogin());
}
}
}

Xamarin forms how to pass Entry value from one to class to another class List?

I want to pass inserted number value from page where I am inserting value to another page list and update ListView with new value.
Here is my AddCardPage xaml:
<Entry x:Name="CardNr" Text="" Completed="Entry_Completed" Keyboard="Numeric" />
here is AddCardPage xaml.cs method:
void Entry_Completed(object sender, EventArgs e)
{
var text = ((Entry)sender).Text; //cast sender to access the properties of the Entry
new Cards { Number = Convert.ToInt64(text) };
}
Here is my CardsPage class where I am already declared some data:
public CardsPage()
{
InitializeComponent();
base.Title = "Cards";
List<Cards> cardsList = new List<Cards>()
{
new Cards { Number=1234567891234567, Name="dsffds M", ExpDate="17/08", Security=123 },
new Cards { Number=9934567813535135, Name="Jason T", ExpDate="16/08", Security=132 },
new Cards { Number=4468468484864567, Name="Carl S", ExpDate="17/01", Security=987 },
};
listView.ItemsSource = cardsList;
AddCard.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(() => OnLabelClicked()),
});
}
private void OnLabelClicked()
{
Navigation.PushAsync(new AddCardPage());
}
and CardsPage xaml with ListView:
<ListView x:Name="listView">
<ListView.Footer>
<StackLayout Padding="5,5" Orientation="Horizontal" >
<Label x:Name="AddCard" Text="Add new Card" FontSize="15" />
</StackLayout>
</ListView.Footer>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Number}" FontSize="15" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
How to pass inserted Entry value from page to another class List and update ListView content?
In the page you want to get the value change it's constructor to accept the value and pass that value from the calling page.
Eg:
class Page1
{
private async void OnSomeEvent()
{
await Navigation.PushAsync(new Page2("xyz"));
}
}
class Page2
{
public Page2(string myValue)
{
}
}

Categories