Calling GetImageAsPngAsync of FFImageLoading from ViewModel class? - xamarin.forms

Using https://github.com/luberda-molinet/FFImageLoading for Xamarin Forms
My XAML:
<forms:CachedImage
CacheType="All"
RetryDelay="1000"
DownsampleHeight="150"
RetryCount="5"
CacheDuration="1"
BackgroundColor="WhiteSmoke"
BitmapOptimizations="True"
SuccessCommand="{Binding CcCommand}"
Source = "{Binding Photo1}">
My ViewModel Class:
class AboutMeViewModel : INotifyPropertyChanged
{
public ICommand CcCommand { get; set; }
public AboutMeViewModel()
{
CcCommand = new Command<CachedImageEvents.SuccessEventArgs>(ffFinishLoading);
}
void ffFinishLoading(CachedImageEvents.SuccessEventArgs ea)
{
// Here I would like to call 'GetImageAsPngAsync'
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
At ffFinishLoading I would like to call GetImageAsPngAsync but I can't find a way to do it at ViewModel class as I have no option for reference forms:CachedImage from the XAML file to ViewModel file.
Any help will be much appreciated.

var stream = await ImageService.Instance.LoadUrl(Photo1).AsPNGStreamAsync();

Related

Clear entry text from ViewModel using RelayCommand

I would like to clear entry text from my ViewModel which is binded there. In the code below I tried it by using a RelayCommand, but it doesn't work.
What i want to accomplish: When clicking button named AddQuestionToQuiz, a function is executed by using Command on the button. The function OnCreateQuizClick(), located in my ViewModel, is triggerd and this function needs to clear my entry text, which i don't get for the moment.
I also tried to use a regular Command instead of using a RelayCommand, but also here it doesn't want to work.
EDIT: UNDERNEATH CODE WORKS FINE - GOT UPDATED
Code is used to clear entry text when clicking on a button from your ViewModel, implementing INotifyPropertyChanged Interface
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}
EDIT: VIEWMODEL UPDATED
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}

Media Plugin not displaying an image after capture MVVM

I am using Media Plugin and everything worked fine until i have decided to move my logic to ViewModel.
This is my Xaml
<Frame BackgroundColor="LightGray" HasShadow="True">
<Image
x:Name="Photo"
Grid.Row="2"
HeightRequest="100"
Source="{Binding postViewModel.SelectedPhoto}"
VerticalOptions="Start"/>
</Frame>
My Binding to MasterViewModel
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
postViewModel = new PostViewModel(navigation);
}
}
Taking Picture in View Model
private MediaFile _selectedPhoto;
public MediaFile SelectedPhoto { get => _selectedPhoto; set => SetValue(ref
_selectedPhoto, value); }
private async Task TakePicture()
{
await Permission();
var imageSource = await DependencyService.Get<IMessage>().ShowActionSheet(AppResources.AlertPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource == AppResources.AlertNewPhoto)
{
var imageFileName = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
{
Name = $"{DateTime.UtcNow}.jpg",
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Rear,
PhotoSize = PhotoSize.Medium,
SaveToAlbum = true
});
if (imageFileName == null) return;
else
{
SelectedPhoto = imageFileName;
}
}
}
I can see tthe adress of the picture however the picture doesnt display on my xaml. I have tried to follow this
Bind Plugin.Media fromViewModel
But still didnt work. Please some suggestion on what am i doing wrong
I use you code and write a demo with binding a string, it works well. You can have a look at the code and may get some idea from it.
Code in xaml:
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding postViewModel.SelectedPhoto}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="click me" Command ="{Binding postViewModel.NewCommand}"/>
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
}
class MasterPostsViewModel
{
public PostViewModel postViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
}
}
class PostViewModel : INotifyPropertyChanged
{
string _selectedPhoto;
public ICommand NewCommand { private set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public PostViewModel()
{
SelectedPhoto = "default text";
NewCommand = new Command(TakePicture);
}
private void TakePicture()
{
SelectedPhoto = "test text After click button";
}
public string SelectedPhoto
{
set
{
if (_selectedPhoto != value)
{
_selectedPhoto = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
}
get
{
return _selectedPhoto;
}
}
}
Sample project has been uploaded here.

How send data (selectItem of listView) from xaml to ViewModel

I want get data selected in my viewModel
this is my xaml, but i donot know how solved my xaml, because this is bad, how use my behavior here in my xaml?
<ListView.Behaviors>
<behaviors:ItemTappedBehavior EventName="ItemSelected">
<behaviors:InvokeCommandAction Command="{Binding SelectedTagChanged}" />
</behaviors:ItemTappedBehavior>
</ListView.Behaviors>
<ListView ItemsSource="{Binding actors}" ItemTapped="ListView_ItemTapped">
in my behindcode
public partial class ActorsView : ContentPage
{
public AccountsView()
{
InitializeComponent();
}
async void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Actor selectedItem = (Actor)e.Item;
Console.WriteLine("WORK"+ Actor.Name);
}
but i want to get in my viewModel, dont in my behind code
i've seen to solved is with commands, or behaviors
this is my viewModel:
public class ActorsViewModel : ViewModelBase
{
public List<Actor> actors { get; set; }
public AccountsViewModel(IActorManager actorManager)
: base()
{
Edit, i am using commands but i dont know how use the answer from John Livermore, i want to use and show the console Console.WriteLine("ROW");.
public class ItemTappedBehavior : Behavior<ListView>
{
public ICommand Command { get; set; }
protected override void OnAttachedTo(ListView bindable)
{
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
}
public Command SelectedTagChanged
{
get
{
return new Command(row =>
{
Console.WriteLine("ROW");
});
}
}
}
Since you had asked for a simpler Behavior for ListView selection changed. Please use the following:
Following class is inherited from BehaviorBase which takes care of BindingContext allocation.
On the OnAttachedTo override the ItemSelected event is hooked.
On the OnDetachingFrom override the ItemSelected event is unhooked.
Command bindable property Binds to the Command in ViewModel
In the event the e.SelectedItem is passed to the Command. (Change
it to whatever value you wish to pass)
public class SelectionChangedEventBehavior : BehaviorBase<ListView>
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(SelectionChangedEventBehavior), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
protected override void OnAttachedTo(BindableObject bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject.ItemSelected += AssociatedObject_ItemSelected;
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
AssociatedObject.ItemSelected -= AssociatedObject_ItemSelected;
}
private void AssociatedObject_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (Command != null)
{
Command.Execute(e.SelectedItem);
}
}
}
Usage
Xaml
<ListView ItemsSource="{Binding Collection}">
<ListView.Behaviors>
<local:SelectionChangedEventBehavior Command="{Binding SelectionCommand}" />
</ListView.Behaviors>
</ListView>
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public Command<object> SelectionCommand { get; set; }
public ViewModel()
{
SelectionCommand = new Command<object>(OnSelection);
}
private void OnSelection(object item)
{
var selectedActor = (item as Actor);
}
}
Hope this could be useful for you. If possible use the EventToCommandBehavior as whole, just because you can reuse it for any control and any event.
In your XAML do the following:
Set your <ListView ItemsSource="{Binding actors}" ItemTapped="ListView_ItemTapped">
to be <ListView ItemsSource="{Binding actors}" SelectedItem = "{Binding SelectedActor, Mode=TwoWay}>
In your view model which your current XAML is bound to, create two new variables as so:
private Actor selectedActor;
public Actor SelectedActor {
get {return selectedActor;}
set {
selectedActor = value;
if(selectedActor != null) {//set properties here if needed}
OnPropertyChanged("SelectedActor"); //or however you raise your events, this is a
//custom method i've made
}
Now, where we define our actors item source, we will also define what a selectedActor is as so:
//get your actors, loop over them and add them to the list of actors
var actorItem = new Actor();
actors.Add(actorItem);
//outside of list raise the property changed event on your actors list.
OnPropertyChanged("actors")
That is a the pattern that i've used at work to get my selected item. If you then need to do something with it you could define it within the SelectedActor variable. For us our app is a hybrid between old xamarin (.Android and .iOS) and .Forms, so we pass Actions from the native level to the view model (ex: loading the summary page for the selected item)

Xamarin Android RecyclerView with CardView button clicked multiple times

I’m trying to make a RecyclerView with CardViews. Each CardView has multiple (4) buttons. The app runs fine but if I scroll in the RecyclerView when I press the button once it clicks/runs the code multiple times.
I know what the problem is but I can’t solve it. The buttons get delegated multiple times.
So how do I need to delegate my buttons in order to only run them ones even on scroll?
My RecyclerAdapter looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V7.Widget;
using MobileApp.Models;
using Android.Support.Design.Widget;
namespace MobileApp
{
public class EvenementRecyclerAdapter : RecyclerView.Adapter
{
private List<Knop> Knoppen;
private RecyclerView mRecyclerView;
private Context mContext;
private string mEvenementNaam;
public EvenementRecyclerAdapter(List<Knop> knoppen, RecyclerView recyclerView, Context context, string evenementNaam)
{
Knoppen = knoppen;
mRecyclerView = recyclerView;
mContext = context;
mEvenementNaam = evenementNaam;
}
public class MyView : RecyclerView.ViewHolder
{
public View mMainView { get; set; }
public TextView mName { get; set; }
public TextView mInfo { get; set; }
public TextView mSales { get; set; }
public Button mBtnOne { get; set; }
public Button mBtnTwo { get; set; }
public Button mBtnFive { get; set; }
public Button mBtnDeleteOne { get; set; }
public MyView(View view) : base(view)
{
mMainView = view;
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View row = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.CardKnopEvenement, parent, false);
TextView knopNaam = row.FindViewById<TextView>(Resource.Id.knopNaam_cardview);
TextView knopInfo = row.FindViewById<TextView>(Resource.Id.knopInfo_cardview);
TextView aantalVerkocht = row.FindViewById<TextView>(Resource.Id.aantalVerkocht_cardview);
Button buttonOne = row.FindViewById<Button>(Resource.Id.btnOne);
Button buttonTwo = row.FindViewById<Button>(Resource.Id.btnTwo);
Button buttonFive = row.FindViewById<Button>(Resource.Id.btnFive);
Button buttonDeleteone = row.FindViewById<Button>(Resource.Id.btnDeleteOne);
MyView view = new MyView(row) { mName = knopNaam, mInfo = knopInfo, mSales = aantalVerkocht, mBtnOne = buttonOne, mBtnTwo = buttonTwo, mBtnFive = buttonFive, mBtnDeleteOne = buttonDeleteone };
return view;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyView myHolder = holder as MyView;
myHolder.mMainView.Click += mMainView_Click;
myHolder.mName.Text = Knoppen[position].Naam;
myHolder.mSales.Text = ConnectionDB.GetVerkochtAantal(mEvenementNaam, myHolder.mName.Text);
//Create button click events
myHolder.mBtnOne.Click += delegate {
Toast.MakeText(mContext, myHolder.mSales.Text, ToastLength.Short).Show();
};
myHolder.mBtnTwo.Click += delegate {
Toast.MakeText(mContext, myHolder.mSales.Text, ToastLength.Short).Show();
};
myHolder.mBtnFive.Click += delegate {
Toast.MakeText(mContext, myHolder.mSales.Text, ToastLength.Short).Show();
};
myHolder.mBtnDeleteOne.Click += delegate {
//Toast.MakeText(mContext, myHolder.mSales.Text, ToastLength.Short).Show();
};
}
void mMainView_Click(object sender, EventArgs e)
{
int position = mRecyclerView.GetChildAdapterPosition((View)sender);
}
public override int ItemCount
{
get { return Knoppen.Count; }
}
}
}
My app design looks like:
Xamarin Android app with RecyclerView and CardView
I didn't find anything I could use on the internet. Thanks in advance!
EDIT SOLVED
I fixed it by putting a if statement around my delegate to check if it had a OnClickListener
if (!myHolder.mBtnOne.HasOnClickListeners)
{
myHolder.mBtnOne.Click += delegate {
Toast.MakeText(mContext, myHolder.mSales.Text, ToastLength.Short).Show();
};
}

MVMLight Messaging and Silverlight

I am trying to get a sample to work using MVVM Light and the Messaging Class. In the sample, I have a test project created from the MVVM Template for Silveright 4. I have added a button on the main page. When the button is clicked, it updates a property on the ViewModel. When the property is updated, I want to show a messagebox with the new value.
The key line of code is:
Messenger.Default.Register(this, new Action(ShowMessage));
I can get this to work in WPF, but not silverlight. It should call ShowMessage with the string parameter when the property changes, but it does not. If I use:
Messenger.Default.Send("Hello MVVM");
This works and the string is sent as a message to ShowMessage.
However, the message does not get sent if the property changes, even though the property was created with the MVVMINPC snippet and has the following line:
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
This should have the same effect as Messager.Default.Send but it seems to be ignored. ThePropertyChangedEvent is indeed raised, but the messanger part seems to be disconnected.
Am I doing something wrong? Here is the full MainViewModel:
public class MainViewModel : ViewModelBase
{
public RelayCommand MyRelayCommand { get; set; }
public const string MyPropertyPropertyName = "MyProperty";
private string _myProperty = "test";
public string MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
var oldValue = _myProperty;
_myProperty = value;
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
public void DoSomething()
{
//Messenger.Default.Send("Hello MVVM"); //Works
this.MyProperty = "Hello World"; //Doesn't work.
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
Messenger.Default.Register(this, new Action<string>(ShowMessage));
MyRelayCommand = new RelayCommand(new Action(DoSomething));
this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);
}
void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
MessageBox.Show(e.PropertyName);
}
}public class MainViewModel : ViewModelBase
{
public RelayCommand MyRelayCommand { get; set; }
public const string MyPropertyPropertyName = "MyProperty";
private string _myProperty = "test";
public string MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
var oldValue = _myProperty;
_myProperty = value;
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
public void DoSomething()
{
//Messenger.Default.Send("Hello MVVM"); //Works
this.MyProperty = "Hello World"; //Doesn't work.
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
Messenger.Default.Register(this, new Action<string>(ShowMessage));
MyRelayCommand = new RelayCommand(new Action(DoSomething));
this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);
}
void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
MessageBox.Show(e.PropertyName);
}
}v
OK, I found that the Register line should look like this:
Messenger.Default.Register(this, new Action<PropertyChangedMessage<string>>(ShowMessage));
The point being there are different types of messages, and you have to register the PropertyChangedMessage type to recieve property changed messages.
Then also, the Action that recieves the message needs to take the correct parameter, like this:
public void ShowMessage(PropertyChangedMessage<string> e)
{
MessageBox.Show(e.NewValue.ToString());
}

Resources