ImageButton not firing Command binding in Xamarin Forms - xamarin.forms

I have an ImageButton that is not firing Command binding command using VMMV architecture. First all other bindings are working properly in the view.
Here is button:
<ImageButton Command="{Binding SelectedItemCommand}" Source="{Binding Logo}" Grid.Row="0" Grid.Column="1" HeightRequest="150" WidthRequest="150" HorizontalOptions="CenterAndExpand" VerticalOptions="EndAndExpand"></ImageButton>
and in ViewModel:
public ICommand SelectedItemCommand => new Command(GetSelectedItem);
When I click the image nothing happens. I've even tried to bind to Pressed parameter but from everything I have read only the Command parameter should be used in a binding scenario. Putting a breakpoint on the function GetSelectedItem never gets reached.
What am I doing wrong here?
Sorry been away for a few days. So nothing was working on the suggestions even though they really should be clicking wouldn't fire command. Anyway I managed to get it to fire now using an eventhandler like so:
SelectedItemCommand = new Command<string>(param => OnItemSelected(param));
public void OnItemSelected(string img1_2)
{
PressedEventHandler?.Invoke(this, EventArgs.Empty);
}
The param captures the CommandParameter so I know which image to the question was clicked "img1" "img2" to do something specific. So my function now accepts a sender object and empty eventarg. I would like to instead pass img1_2 value but that doesn't appear to be possible as of now. What is cusrious is the sender object contains all the properties and values from the images (like an array of all my properties) but I cannot seem to get at them.
Attempted this:
string str = Item1Image.ToString(); // property in sender and viewmodel
But this returns a null value and not value listed in the sender object value?
Any additional thoughts?
TIA!
Rick...

public ICommand SelectedItemCommand {get; private set;}
...
public YourViewModel(){
...
SelectedItemCommand = new Command(GetSelectedItem);
...
}
...
Or
public ICommand SelectedItemCommand{
get
{
return new Command(() => {
//Do something
});
}
}

Related

MvvmCross MvxAsyncCommand CanExecute does not disable/enable attached button

I am using MvvmCross in a Xamarin.Forms solution. I have the command attached to the button with the following code
<Button Text="Login" Command="{Binding LoginCommand}"/>
The command is setup in the ViewModel like so
private ICommand _loginCommand;
public ICommand LoginCommand
{
get
{
_loginCommand = _loginCommand ?? new MvxAsyncCommand(Login,
() =>
{
return IsOnline;
});
return _loginCommand;
}
}
I then query when the network connection changes which should in turn change the boolean returned in the CanExecute Func in the MvxAsyncCommand.
{
base.OnlineSignalChanged(isOnline);
(LoginCommand as MvxAsyncCommand)?.RaiseCanExecuteChanged(); //tried calling other methods here as well in case I was calling the wrong thing
}
In that last code block I am able to stop a breakpoint to see that the CanExecute is queried again. However, the button is not enabled/disabled properly.
Is this a know issue with MvvmCross implementation of MvxAsyncCommand? I ended up using the advice of the following thread use John Thiriet version of an AsyncCommand and all works well.
How to implement Async Command

Update OnPropertyChanged on a Parent View from a child view

In Xamarin for mac, I decided to make multiple views to be used within my main view using the MVVM pattern.
The thing is that I have a ListView within my MainPage which pulls a List of items from a model, and the list is populated within a child view, with its own ViewModel.
When I add a new service from the child view, I would like for the OnPropertyChanged event on the parent view model to trigger.
It is working by navigating to the parent view and setting the animation to false, but this is not really nice looking. It worked though when I had all code within one ViewModel.
How I tried to achieve this, and the errors I got:
0 - Accessing the command within the child model from the parent model, and passing the propertychanged event handler along.
I Couldn't do it. I tried this by making a bindable command like below, but this is not doable for me as I don't think it is possible for the command to know when the property will be changed, which is the whole point of this problem.
If it is doable, I don't know how.
//public static readonly BindableProperty SaveServiceClickedCommandProperty =
// BindableProperty.Create(
// "SaveServiceClicked",
// typeof(Command),
// typeof(NewServiceViewModel),
// null);
1 - Passing the parent view model on the child view model, and put a OnPropertyChanged(nameof(parentModel.List)) at the clicked event handler.
public class ChildViewModel : INotifyPropertyChanged
{
public ICommand AddEntryClickedCommand { get; private set; }
private MainModel mainModel;
// property changed handler
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public NewServiceViewModel()
{
Navigation = MainPage;
//async void execute() => await OpenPage();
//OpenPageCommand = new Command(execute, () => !IsBusy);
//async Task OpenPage()
//{
// await Navigation.PushAsync(new MainPage());
//}
// Here I tried to access the data from within the main model.
mainModel = new MainModel(Navigation);
InitMainModel();
void InitMainModel()
{
MainPage mainView = new MainPage();
mainView.BindingContext = mainModel;
}
async void c1() => await AddEntryClicked();
AddEntryClickedCommand = new Command(c1);
}
public async Task<bool> AddEntryClicked()
{
OnPropertyChanged(nameof(mainModel.List))
}
The attempt above created some errors as the object is already populated.
Leading to me thinking that I don't have the right approach altogether.
My solution being to re-introduce the child view within the parent view, and change IsVisible according to the button being clicked or not, as I already did with other smaller component.
I have thought about pulling the list from the child view, but that's raises the same issue of non-null collection.
Of course, the code has been modified to show only the gist.
Thanks in advance.

xamarin.forms Handle Clicked event on WebView

I want to handle the click/tap event on a WebView control
I've tried the GestureRecognizers but nothing happens, i think maybe the WebView has some sort of making the event handled "true".
<WebView>
<WebView.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnWebViewClick"
NumberOfTapsRequired="1" />
</WebView.GestureRecognizers>
</WebView>
And I've tried it using c# code behind too but no luck :/
I am using this hack: put the WebView in a Grid and insert a BoxView in the same cell. Then put a handler on the BoxView.
You can use the HybridWebView from XLabs, and use javascript injection to invoke and handle clicks in your Xamarin control. The injected javascript code can add a click-event listener at capture stage. When a click is detected it uses Native callback to send information back to C# code.
For example - you can define a custom control like this:
public class ClickEventArgs : EventArgs
{
public string Element { get; set; }
}
public class ClickableWebView : XLabs.Forms.Controls.HybridWebView
{
public event EventHandler<ClickEventArgs> Clicked;
public static readonly BindableProperty ClickCommandProperty =
BindableProperty.Create("ClickCommand", typeof(ICommand), typeof(ClickableWebView), null);
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
public ClickableWebView()
{
LoadFinished += (sender, e) =>
InjectJavaScript(#"
document.body.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
Native('invokeClick', 'tag='+target.tagName+' id='+target.id+' name='+target.name);
}, true /* to ensure we capture it first*/);
");
this.RegisterCallback("invokeClick", (string el) => {
var args = new ClickEventArgs { Element = el };
Clicked?.Invoke(this, args);
ClickCommand?.Execute(args);
});
}
}
Sample XAML usage
<local:ClickableWebView
Uri="https://google.com"
Clicked="Handle_Clicked"
/>
and sample code-behind
void Handle_Clicked(object sender, CustomWebView.ClickEventArgs e)
{
Xamarin.Forms.Application.Current.MainPage.DisplayAlert("WebView Clicked", e.Element, "Dismiss");
}
** Output **
Alternatively, you can also bind ClickCommand property to implement this using MVVM pattern.
Another option is to handle the click in html and do a navigation that doesn't go anywhere. You can put something like this in your html
<div onclick="window.location.href='#click#'">...</div>
So a click anywhere inside there would cause a navigation. If you only have a button, you could just use
<a href='#click'>...</a>
Then in your WebView control wire up the Navigating event, and check if the new url contains "#click". If so, do your click handling code and call e.Cancel=true in the event to prevent the browser from completing the navigation.
Note that onclick handlers don't work on body or document elements in Xamarin Webview. At least not on iOS.
I've found the simplest approach is to use a Grid with two controls, the WebView and a Button
<Grid>
<WebView
Grid.Column="0"
Grid.Row="0"
HeightRequest="100"
WidthRequest="1000" />
<Button
Grid.Column="0"
Grid.Row="0"
BackgroundColor="Transparent"
HorizontalOptions="Fill"
VerticalOptions="Fill"
Clicked="OnWebViewTapped"/>
</Grid>
The button covers the WebView and intercepts taps.
Gesture recognizer doesn't work with WebView. You can try using MR.Gestures
To get all the features you will have to purchase a license.
If you forget to configure the license key properly or the key does not match your app name, then all the events will still be raised, but the properties of the EventArgs will be empty. That may be enough for the tap and long press events, but not for the more complicated ones.
An easier workaround is to use the 'Focused' event on your webview. You can implement it as below:
var wb = new WebView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Source = "https://stackoverflow.com/questions/56320611/webview-gesturerecognition-not-working-in-xamarin-forms",
};
wb.Focused += (s, e) =>
{
//Handle your logic here!
wb.Unfocus();
};

Binding the Enabled property of a Android button with the MvvmCross

I have a problem when I try to bind the "Enabled" property of my Android Button to a Boolean of my ViewModel using the MvvmCross framework and I really don't know the origin of it.
So I have a ViewModel which contains the two following properties :
private ProjectDetailDTO _projectDetail;
public ProjectDetailDTO ProjectDetail
{
get { return this._projectDetail; }
set
{
_projectDetail = value;
RaisePropertyChanged(() => ProjectDetail);
RaisePropertyChanged(() => HasPicture);
}
}
private bool _hasPicture;
public bool HasPicture
{
get { return ((this.ProjectDetail != null) && !String.IsNullOrEmpty(this.ProjectDetail.Pictures)); }
set { _hasPicture = value;
RaisePropertyChanged(() => HasPicture);
}
}
As you would understand, my button is bind to the HasPicture property. So I have the following code for my button in my .axml file :
<Button
local:MvxLang="Text LblSeePicturesValue"
local:MvxBind="Enabled HasPicture,Click ShowProjectPicturesCommand"
android:id="#+id/buttonPictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
I don't think it's a ViewModel problem because my WP application works well with this code. In fact, my ProjectDetailDTO is filled by calling a web service, so by an asynchronous method. I think it's why when the binding is realized the HasPicture property has the false value. But with my ViewModel code, the HasPicture property should be updated when the ProjectDetailDTO is filled. Is there anything I did wrong in my Android View?
Thanks for any help !
I think what you are seeing here is some interaction between ICommand.CanExecute and the Enabled property. There's a discussion about this on https://github.com/MvvmCross/MvvmCross/issues/729
To work around this, try switching the binding to:
local:MvxBind="Click ShowProjectPicturesCommand;Enabled HasPicture"
(Also note that the separator in bindings is a ; - not a ,)

Notifying dependency property change to its observers

I have the following in xaml.
<TextBlock Text="{Binding Title}" />
And created the following dependency property,
public string Title
{
get { return (string)GetValue(TitleProperty); }
set
{
SetValue(TitleProperty, value);
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Title"));
}
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ColumnChart), new PropertyMetadata(string.Empty));
Now if I bind the Title property in other xaml, the value is not taken. Because the PropertyChange notification is not called. And always PropertyChanged is null.
How I can notify to the list of observers that this property is changed, so that the value will be updated.
I'm not quite sure what you mean by "How I can notify to the list of observers that this property is changed, so that the value will be updated."
This looks like a user control, as it's not typical to use dependency properties in view models. Therefore, have a look at Routed Events. The Register() method for a dependency property has an override which will take a handler which will be invoked when the property changes. You can invoke a custom routed event from within this handler. Consumers of your user control can subscribe to this routed event using the standard mechanisms.

Resources