How to pass parameter to command - xamarin

I have Xamarin Forms solution and on one page there is list with images. When image is clicked, I would like to trigger procedure witch has image path as parameter. In xaml I define image with:
<Image Source="{Binding ImagePath}"
WidthRequest="30" HeightRequest="30"
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" >
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapImageCommand}" CommandParameter="{Binding ImagePath}" />
</Image.GestureRecognizers>
</Image>
and TapImageCommand is defined in view model's constructor as:
TapImageCommand = new Command<string>(ImagePath =>
{
OnImageTapped(ImagePath);
});
and TapImageCommand is defined with:
public ICommand TapImageCommand { get; set; }
Problem is OnImageTapped is never triggered. What am I doing wrong?

The problem here is that you think you are binding the ImagePath of the object behind the list, but you are not. Look at the Command you are binding, this is part of the PageModel, not the object in the list, so neither is the CommandParameter.
Therefore, ImagePath is probably null, and it does not match the signature you have for the Command which expects a string.
In this particular case it is probably easiest to supply the whole object as a parameter and get out the property yourself. I will assume the object in the list is of type Foo, then edit your code like underneath.
In your view, edit the TapGestureRecognizer to this:
TapGestureRecognizer Command="{Binding TapImageCommand}" CommandParameter="{Binding .}" />
The dot points to itself, in this case the specific instance of Foo in your list. Then edit the Command like this:
TapImageCommand = new Command<Foo>(fooObject =>
{
OnImageTapped(fooObject);
});
Now in your OnImageTapped method you can extract the ImagePath property.

In my case I was looking for selected Index of stack layout or grid and it was done as below.
Command Added as below
public ICommand SelectedItem
{
get
{
return new Command<string>((x) => OpenChild(x));
}
}
public void OpenChild(string x)
{
//handle parameter x to say "Hello " + x
}
Added command on XAML
SelectionChangedCommand="{Binding SelectedItem}" SelectionChangedCommandParameter="{Binding SelectedItem, Source={RelativeSource Self}}

Related

Xamarin Forms - Binding a function to StackLayout TapGesture

In my view I have:
<ContentView.BindingContext>
<vm:HomeViewModel />
</ContentView.BindingContext>
In that viewmodel I have:
public void test(object sender, EventArgs e)
{
var x = 0;
}
That's just for testing so I can hit a breakpoint, but I can't seem to Bind the function to the View:
<StackLayout BackgroundColor="White">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding test }" />
</StackLayout.GestureRecognizers>
I've tried to hook it up with Tapped property as well, adding parentheses, I keep getting:
Severity Code Description Project File Line Suppression State
Error Position 57:51. No method OnTapGestureRecognizerTapped with correct signature found on type
I know I'm missing something here, can anyone shed some light on my problem?
You need a command in your viewmodel binding to:
public ICommand cmdTest { get { return new Command(() => test())
Now in your view you are able to bind to cmdTest:
<TapGestureRecognizer Command="{Binding cmdTest}" />

Set focus of an entry from ViewModel

I've read about messaging, triggers, behaviors, etc. ... all seems a bit overkill for what I am trying to accomplish.
I have a repeating data entry screen in xaml that has 1 picker, 2 entries, and 1 button. The picker, once a value is selected, keeps that selection. The 1st entry does the same as the picker. The 2nd entry is the one that is always getting new values.
I want to collect the filled in values on click of the button and then clear the last entry field of its data and put focus back on that entry so the user can enter a new value and hit save. repeat repeat repeat etc.
I understand the MVVM model and theory - but I just want to put the focus on an entry field in the xaml view and am completely stumped.
EDIT to add code samples
view.xaml:
<StackLayout Spacing="5"
Padding="10,10,10,0">
<Picker x:Name="Direction"
Title="Select Direction"
ItemsSource="{Binding Directions}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedDirection}"/>
<Label Text="Order"/>
<Entry Text="{Binding Order}"
x:Name="Order" />
<Label Text="Rack"/>
<Entry Text="{Binding Rack}"
x:Name="Rack" />
<Button Text="Save"
Style="{StaticResource Button_Primary}"
Command="{Binding SaveCommand}"
CommandParameter="x:Reference Rack" />
<Label Text="{Binding Summary}"/>
</StackLayout>
viewmodel.cs
public ICommand SaveCommand => new DelegateCommand<View>(PerformSave);
private async void PerformSave(View view)
{
var scan = new Scan()
{
ScanType = "Rack",
Direction = SelectedDirection.Name,
AreaId = 0,
InsertDateTime = DateTime.Now,
ReasonId = 0,
ScanItem = Rack,
OrderNumber = Order,
ScanQty = SelectedDirection.Value,
IsUploaded = false
};
var retVal = _scanService.Insert(scan);
if (!retVal)
{
await _pageDialogService.DisplayAlertAsync("Error", "Something went wrong.", "OK");
}
else
{
view?.Focus();
Rack = string.Empty;
Summary = "last scan was great";
}
}
Error shows up in this section:
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(RackPage));
Direction = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Picker>(this, "Direction");
Order = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Order");
Rack = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Rack");
}
You can send the Entry as a View parameter to your view model's command. Like that:
public YourViewModel()
{
ButtonCommand = new Command<View>((view) =>
{
// ... Your button clicked stuff ...
view?.Focus();
});
}
And from XAML you call this way:
<Entry x:Name="SecondEntry"
... Entry properties ...
/>
<Button Text="Click Me"
Command="{Binding ButtonCommand}"
CommandParameter="{Reference SecondEntry}"/>
I hope it helps.
You can set the CommandParameter in the XAML and use that in the viewmodel.
In Xaml:
<ContentView Grid.Row="0" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Image Source="downarrow" HeightRequest="15" WidthRequest="15" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" />
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SecurityPopupCommand}" CommandParameter="{x:Reference AnswerEntry}"/>
</ContentView.GestureRecognizers>
</ContentView>
In view model:
public ICommand SecurityPopupCommand { get { return new Command(OpenSecurityQuestionPopup); } }
private void OpenSecurityQuestionPopup(object obj)
{
var view = obj as Xamarin.Forms.Entry;
view?.Focus();
}
Not sure you can set the focus of an entry from the XAML, but from the page, you could just use the function Focus of the Entry on the Clicked Event:
saveButton.Clicked += async (s, args) =>
{
//save the data you need here
//clear the entries/picker
yourEntry.Focus();
};

windows phone 8 longlistselector show and hide button based on recordid

I have a books list displayed in my long list selector like this
<DataTemplate x:Key="BooksItemTemplate">
<StackPanel Grid.Column="1" Grid.Row="0" VerticalAlignment="Top">
<TextBlock Name="booktitle" Text="{Binding BookTitle,Mode=OneWay}" Style="{StaticResource PhoneTextNormalStyle}" TextWrapping="Wrap" FontFamily="{StaticResource PhoneFontFamilySemiBold}"/>
<TextBlock Text="{Binding AuthorName,Mode=OneWay}" Style="{StaticResource PhoneTextNormalStyle}" TextWrapping="Wrap" FontFamily="{StaticResource PhoneFontFamilySemiLight}"/>
<Button Content="Add To Favourites" Tag="{Binding BookId,Mode=OneWay}" Click="Button_Click_1" ></Button>
</StackPanel>
</Grid>
</DataTemplate>
<phone:LongListSelector x:Name="bookslist" Grid.Row="1"
ListFooter="{Binding}"
ItemsSource="{Binding BooksList}"
Background="Transparent"
IsGroupingEnabled="False"
ListFooterTemplate ="{StaticResource booksListFooter}"
ItemTemplate="{StaticResource BooksItemTemplate}"/>
so there is an add to favourites button beside every book in the list . pressing that button i am entering the pressed book's id in my isolatedstoragesetting like this
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Button bt = (Button)sender;
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
List<long> ListFavourites;
if (settings.Contains("ListFavourites"))
{
ListFavourites = settings["ListFavourites"] as List<long>;
}
else
{
ListFavourites = new List<long>();
}
if(!ListFavourites.Contains(Convert.ToInt64(bt.Tag)))
{
ListFavourites.Add(Convert.ToInt64(bt.Tag));
}
settings["ListFavourites"] = ListFavourites;
settings.Save();
}
problem:
now when loading the above book list(longlistselector) when the page loads i want to show or hide the add to favorites button based on whether the bookid is present in the isolatedstoragesetting or not. what i have tried here is that i have tried to bind the converter to the add to favourite button and also bind the convertparameter with bookid. but the convertparameter doesn't support binding.
so what is the technique i can use to show or hide the add to favourite button based on the book id presence in the favourite list in the isolatedstoragesetting?
how can i hide the button based when clicking it based on the bookid?
You are almost there in thinking to use a converter. The actual idea, when materialized, should look something like this.
First, you will need to implement a converter, in this case, you will need to convert the bookid to a Visibility value.
public class BookIdToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
//value is booking id here, which means that you just need to check against the isolatedStorageSettings
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
In your DataTemplate, the binding should take place like this:
<Button Content="Add To Favourites" Tag="{Binding BookId,Mode=OneWay}"
Click="Button_Click_1" Visibility={Binding BookId,Converter={StaticResource TheConverterCreatedAbove}} >
</Button>
That should do the trick.
The MVVM way would be to expand your ViewModel. It would be much better to add an AddToFavoritesCommand to BookViewModel instead of putting the logic in code behind. If the button is bound to that Command it would automatically go disabled when the Command would properly (with CanExecuteChanged) switch CanExecute to false.
In your case, you can add a property IsFavourite or CanAddToFavoirtes and then use a standard BoolToVisibility converter (the Command execution would set this property and the BookViewModel would be initialized with the correct value read from IsolatedStorage).
All logic behind the presentation of Book and functionalities related to Book belong to BookViewModel.

Mvvm TextBox KeyDown Event

I'd like to know how I can handle a KeyDown Event with MVVM in my ViewModel.
I have a TextBox and when the user hits a key, which is not a number, the input shouldn't be allowed. I'd normally do it with Code behind like this (not full code, just an easy example):
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
e.Handled = true;
}
}
Now I want to put this somehow in my ViewModel with a Command. I'm new to MVVM and I'm only using Bindings right now (which works fine :) ), but I don't know how to use a Command at all...
My TextBox looks i.e. like this:
<TextBox Text="{Binding MyField, Mode=TwoWay}"/>
ViewModel:
private string _myfield;
public string MyField{
get { return _myfield; }
set {
_myfield= value;
RaisePropertyChanged( ()=>MyField)
}
}
But the setter will only be called, when I leave the TextBox + I don't have access to the entered Key.
I know my answer is late but if someone has a similar problem. You must just set your textbox like this:
<TextBox Text="{Binding MyField, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The following works for handling the "Enter" key in a TextBox:
<TextBox Text="{Binding UploadNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding
Key="Enter"
Command="{Binding FindUploadCommand}" />
</TextBox.InputBindings>
</TextBox>
I do this by using the interaction triggers. (this example uses the MVVM_Light framework for the command binding)
here is an example:
<textBox Text="{Binding MyField}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{Binding MyCommandName}" CommandParameter="YouCommandParameter"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox/>
Create a ICommand object in your view model with the name MyCommandName and add these to the top of your xaml:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
You don't have to use the mvvm-light command. This is just what I use and I like it because it allows me to use the CanExecute method of the ICommand interface
hope this helps

WP7: Binding to an element outside pivot.itemtemplate

I've been struggling for a while on this. I'm a bit of a newbie, but i lurked a lot and still couldn't find a solution to my problem, so I decided to post my question here.
I'm binding a pivot to a collection of objects I want to display to create a gallery. Here is my pivot control bound to a list called gallery, where each object contains 2 strings (url and description).
<controls:Pivot ItemsSource="{Binding gallery}" Grid.Row="0" x:Name="galleryPivot">
<controls:Pivot.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Source="{Binding url}" />
<Grid Visibility="{Binding ElementName=galleryPivot, Path=DataContext.ShowDetail}">
<ListBox>
<ListBoxItem>
<StackPanel>
<TextBlock Text="{Binding description}" />
</StackPanel>
</ListBoxItem>
</ListBox>
</Grid>
</Grid>
</StackPanel>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
The datacontext is the viewmodel and initialized in the constructor of the page.
Here is my ViewModel:
public class GalleryViewModel : INotifyPropertyChanged
{
public List<Gallery> gallery
{
get { return Globals.pictures; }
}
private Visibility _showDetail = Visibility.Collapsed;
public Visibility ShowDetail
{
get { return _showDetail; }
set {
_showDetail = value;
RaisePropertyChanged("ShowDetail");
}
}
public GalleryViewModel()
{ }
public event PropertyChangedEventHandler PropertyChanged = delegate { return; };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The gallery object is a list in my ViewModel, as the ShowDetail property. As ShowDetail is outside the scope, I tried to set ElementName as explained here.
The pivot binds well to the gallery list, but when I change the value of ShowDetail, the grid won't hide. I also tried to set ElementName to LayoutRoot but it still won't work.
My question, how can I bind the visibility when it is outside the scope of the itemtemplate?
Within a DataTemplate the ElementName binding refers only to the names of elements that are within that DataTemplate. The data context within your data template is the Gallery instance, not the GalleryViewModel. You could move the ShowDetail property down to the Gallery class instead.
If you'd rather not do that, then an alternative would be to use a proxy for the data context, which you add as a resource to the page and bind to the page's data context (a GalleryViewModel instance presumably). You can then reference that resource as you would any other resource to get at the parent data context.
If you're not familiar with this proxy concept, then Dan Wahlin's post on the subject should help.

Resources