How to display Image on Xaml Xamarin Forms on MVVM - xaml

I have tried to display and image from Mobile camera using Media Plugin. but It does not display after select the image. I am using MVVM.
I'd appreciate any help.
private byte[] imgAsBytes;
private ImageSource imageSource;
//constructor
public NoticesAdminViewModel()
{
_connection = DependencyService.Get<IConnection>().GetConnection();
UploadImage = new DelegateCommand(async () => await GetImage());
}
public DelegateCommand UploadImage { get; set; }
public ImageSource DisplayImageSource
{
get { return imageSource; }
set
{
imageSource = value;
SetProperty(ref imageSource, value);
}
}
public async Task GetImage()
{
file = await DependencyService.Get<IMediaFile>().GetFile();
//file is not null
DisplayImageSource = ImageSource.FromStream(() => file.GetStream());
}
//My Xaml
//Display Image Here
<Image
Aspect="AspectFit"
HeightRequest="200"
Source="{Binding DisplayImageSource}"
WidthRequest="200"/>
// Button to call image
<Image Source="camera.png" HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding UploadImage}" />
</Image.GestureRecognizers>
</Image>
Which file returns the MediaFile content successfully, and I can upload the image to server, but the Xaml to display image is blank.
What have I done wrong?
Thanks

First, your code shows you're binding the image's Source to UploadImage, but you'd want it to be bound to your ImageSource property.
Also, in your GetImage() method, you are copying the file contents to a memory stream, copying that into a byte array, but you never use it. Why?
You then immediately dispose both the file and stream and setup the image source pointing to file.GetStream(), but you've already disposed that file.
Try cutting out all that and just do:
public async Task<ImageSource> GetImage()
{
file = await DependencyService.Get<IMediaFile>().GetFile();
ImageSource = ImageSource.FromStream(() => file.GetStream());
return ImageSource;
}

Related

How do you pass parameters in MAUI without using a ViewModel?

I have this on one page:
await Shell.Current.GoToAsync(nameof(Pages.StartPage), true, new Dictionary<string, object>
{
{ "LoginData", result }
});
result is an object/class
In my Pages.StartPage I want to get that object. I have tried using [QueryProperty... but that always returns a null. E.g.
[QueryProperty(nameof(GetLoginData), "LoginData")]
public partial class StartPage : ContentPage
...
private JsonApiResult GetLoginData { set { _loginData = value; } }
I've just started using MAUI, and I am converting an app from Xamarin to MAUI. The pages I have built take care of themselves, so I don't want to use ViewModels, I just need a value from that passed-in object for the page to do its stuff. I don't want to have to rewrite all my pages unless there is no other way
Any help would be much appreciated. I've watched loads of videos on this, and I can't make it work, what am I missing?
UPDATE
I should add that to make matters more complex for myself, I am also using Dependency Injection (DI)
here it comes an example!
Main page .xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
On main page .cs:
public MainPage()
{
InitializeComponent();
}
private async void OnCounterClicked(object sender, EventArgs e)
{
List<Student> myStudentsList = new List<Student>
{
new Student {Name="Carlos",Course="IT",Age=18},
new Student {Name="Juan",Course="IT",Age=19},
new Student {Name="Leandro",Course="IT",Age=20}
};
await Navigation.PushAsync(new PageWithStudentsList(myStudentsList));
}
PageWithStudentsList .cs :
public partial class PageWithStudentsList : ContentPage
{
public PageWithStudentsList(List<Student> students)
{
Console.WriteLine(students);
InitializeComponent();
}
}
And you dont need to use viewmodel!
EDIT: in case you need another example with SHELL NAVIGATION, here is a microsoft example in their docs! Hope it helps!
private JsonApiResult _loginData;
public JsonApiResult LoginGetData {
get => _loginData;
set { _loginData = value; }
}
It seems this was the solution though I can't see why. I'll dig into it another time but right now its working so I can crack on

Button will not work, unable to find suitable setter or getter

public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
Queue = new QueuePanelViewModel();
Merge = new MergePanelViewModel();
CurrentQueuePanel ??= new QueuePanel();
CurrentMergePanel ??= new MergePanel();
_selectedView = CurrentQueuePanel;
}
public QueuePanelViewModel Queue { get; }
public MergePanelViewModel Merge { get; }
private UserControl _selectedView;
public UserControl SelectedView
{
get
{
return _selectedView;
}
set
{
_selectedView = value;
}
}
private static QueuePanel CurrentQueuePanel { get; set; }
private static MergePanel CurrentMergePanel { get; set; }
private void OnPanelButtonClickHandler(object sender, RoutedEventArgs e)
{
switch (((Button)sender).Tag)
{
case "Queue":
SelectedView = CurrentQueuePanel;
break;
case "Merge":
SelectedView = CurrentMergePanel;
break;
default:
((Button)sender).Content = "Somethin went wrong...";
break;
}
e.Handled = true;
}
}
And in the .axaml
<Button Tag="Queue" Click="{Binding OnPanelButtonClickHandler}" ClickMode="Press" Margin="0" Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="Queue" Classes="btn" />
The button event will not work in any fashion I have tried. In this attempt It gives me the exception
'Unable to find suitable setter or adder for property Click of type Avalonia.Controls:Avalonia.Controls.Button for argument Avalonia.Markup:Avalonia.Data.Binding, available setter parameter lists are:
System.EventHandler`1[[Avalonia.Interactivity.RoutedEventArgs, Avalonia.Interactivity, Version=0.10.12.0, Culture=neutral, PublicKeyToken=c8d484a7012f9a8b]] Line 40, position 26.' Line number '40' and line position '26'.
If I use a Command instead of Click, it will compile however the button becomes disabled.
You are getting this exception because Click is the RoutedEvent and OnPanelButtonClickHandler should be in the *.axaml.cs code behind.
If you want to call the function in your view model from the view you should use Command property and bind to the function or implement a command in your view model.
In your case the button is inactive when you bind to the command because you do not pass the required parameters. This should work:
private void OnPanelButtonClickHandler(string parameter)
<Button Command="{Binding OnPanelButtonClickHandler}" CommandParameter="Queue" .../>
You can find more information in the docs

Set Tag value in xaml

In Xamarin, Is there any way to set tag to button in xaml file?
I tried this,
<Button Text="OK"
Tag="{Binding Email}"/>
but Tag property doesn't supporting in xaml
what property is there in xaml to support to set tag value to view
You can easily create your own custom Button and add a Tag BindableProperty.
public class ButtonWithTag : Button
{
public object Tag
{
get { return (object)GetValue(TagProperty); }
set { SetValue(TagProperty, value); }
}
public static readonly BindableProperty TagProperty =
BindableProperty.Create(nameof(Tag), typeof(object), typeof(ButtonWithTag), null);
}
You use it like this in XAML
xmlns:local="clr-namespace:YourProjectName"
<local:ButtonWithTag
Text="Ok"
Tag="{Binding Email}" />
You can use x:Name="MyButton", and then access the button in your code-behind .cs file by simply using MyButton.Text = "updated text", for example. Assuming thats what you meant.
I think the best solution for this is CommandParameter. You can do this using CommandParameter. Try this,
.xaml
<Button Text="OK"
Command="{Binding TapCommand}"
CommandParameter="EmailAddress"/>
.cs
ICommand tapCommand;
public ICommand TapCommand {
get { return tapCommand; }
}
public ConstructorName {
tapCommand = new Command (OnTapped);
}
void OnTapped (object s) {
Debug.WriteLine ("parameter: " + s);
}

ReactiveUI Binding does not work in Release build in a UWP app

I'm hitting a weird issue with ReactiveUI and binding where the binding works fine in Debug build but not in Release build.
Here I have the code for a sample app that shows the issue. In Debug builds, as I type something in the textbox the InputText property in the view model gets updated accordingly and when I tap the button it shows the updated input text back to me in a message dialog. But the same code in Release build does not work, as InputText always remains empty.
Anyone knows what's going on here?
<Page x:Class="RxBind.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox x:Name="MyTextBox" Margin="10"/>
<Button x:Name="MyButton" Content="Show Dialog" Margin="10"/>
</StackPanel>
</Page>
public sealed partial class MainPage : IViewFor<MainPageViewModel>
{
public MainPage()
{
InitializeComponent();
ViewModel = new MainPageViewModel();
this.WhenActivated(d =>
{
d(this.BindCommand(ViewModel, vm => vm.MyButtonCommand, v => v.MyButton));
d(this.Bind(ViewModel, vm => vm.InputText, x => x.MyTextBox.Text));
});
}
#region IViewFor impl
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (MainPageViewModel)value; }
}
public MainPageViewModel ViewModel { get; set; }
#endregion //IViewFor impl
}
public class MainPageViewModel : ReactiveObject
{
private string _inputText = string.Empty;
public string InputText
{
get { return _inputText; }
set { this.RaiseAndSetIfChanged(ref _inputText, value); }
}
public ReactiveCommand<Unit, Unit> MyButtonCommand { get; }
public MainPageViewModel()
{
MyButtonCommand = ReactiveCommand.CreateFromTask(async () =>
{
await new MessageDialog($"InputText={InputText}").ShowAsync();
});
}
}
This isn't really an answer to your question of "what's going on here," but I'm the guy who filed that other bug Matt Whilden linked to and I worked around it for the time being by referencing the text on the button event and calling the command from there too instead of binding the command directly to the button, sort of like this:
d(Observable.FromEventPattern<RoutedEventHandler, object, RoutedEventArgs>(h => MyButton.Click += h, h => MyButton.Click -= h).Subscribe(x=>
{
ViewModel.InputText = MyTextBox.Text;
ViewModel.MyButtonCommand.Execute(null);
}));
Not elegant but it works for me because I don't really need to update on property changed - just on button click. Maybe this will work for you as well before the issue is solved.
As Matt Whilden mentioned in this thread, using runtime directive approach solves the problem so I'm marking this as the right answer. Many thanks Matt Whilden.

Image doesn't show in Xamarin.forms (PCL)

I have an image withn name fb.png and it's in root project(prtable) and I add this image to Resource>drawble in Droid project.
Thees is my MainPage.xaml code:
<Image x:Name="img1"></Image>
And Thees is my MainPage.xaml.cs code:
public MainPage()
{
InitializeComponent();
ImageSource img = ImageSource.FromResource("App2.fb.png");
img1.Source = img;
img1.Aspect = Aspect.AspectFit;
img1.BackgroundColor = Color.Navy;
}
What changed is need that image will be appeared?
If the file is saved in the Resources/Drawable directory, then you use FromFile, not FromResource. FromResource is used for images packaged as embedded resources in your built library.
You also need to specify the exact name of the file as it appears in Resources/Drawable, so this should do it:
public MainPage()
{
InitializeComponent();
ImageSource img = ImageSource.FromFile("fb.png");
img1.Source = img;
img1.Aspect = Aspect.AspectFit;
img1.BackgroundColor = Color.Navy;
}
Here is a full implementation of MVVM bound image resource to Image control. You need to set your viewmodel as the context of your page where the XAML is. Also accessing as "App2.fb.png" seems odd, it should just be fb.png. That might be a simpler fix.. just rename the image source to the exact name of the image as listed in Droid > resources
XAML
<Image
Aspect="AspectFit"
Source="{Binding PropertyImageStatusSource}">
Base ViewModel
Have your viewmodels inherit from a viewmodel base class so INotifyPropertyChanged is implemented on your accessors universally.
public class _ViewModel_Base : INotifyPropertyChanged
{
//figure out what is getting set
public virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
//attach handler with property name args to changing property, overridable in inheriting classes if something else needs to happen on property changed
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ViewModel
Public MyViewModel : _ViewModel_Base
{
private string ImageStatusSource = "fb.png";
public string PropertyImageStatusSource
{
set { SetProperty(ref ImageStatusSource, value); }
get { return ImageStatusSource; }
}
}