Xamarin.Forms - Binding on ContextMenu - c#

I have a list with songs as items. A long press on the element should display a context menu.
AllSongsViewModel.xaml:
<DataTemplate x:Key="SongTemplate">
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Edit" />
<MenuItem Text="Delete"/>
</ViewCell.ContextActions>
<StackLayout Padding="15,5" VerticalOptions="Center">
<Label Text="{Binding Title}"
FontSize="16"/>
<Label Text="{Binding Performer}"
FontSize="12"/>
</StackLayout>
</ViewCell>
</DataTemplate>
This works well, but I need to bind now so that the context menu opens depending on bool IsAdmin, which lies in AllSongsViewModel
AllSongsViewModel.cs:
public bool IsAdmin => _authService.LoggedUser.Role == "Admin";
But I don’t know how to bind this property to the context menu

Unfortunately you can't do this on your ViewModel. But you can set a BindingContextChange event on your View Cell and change it there like this:
XAML:
<DataTemplate x:Key="SongTemplate">
<ViewCell BindingContextChanged="OnBindingContextChanged">
<StackLayout Padding="15,5" VerticalOptions="Center">
<Label Text="{Binding Title}"
FontSize="16"/>
<Label Text="{Binding Performer}"
FontSize="12"/>
</StackLayout>
</ViewCell>
In your code behind:
private void OnBindingContextChanged(object sender, EventArgs e)
{
base.OnBindingContextChanged();
if (BindingContext == null)
return;
ViewCell theViewCell = ((ViewCell)sender);
var viewModel = this.BindingContext.DataContext as AllSongsViewModel;
theViewCell.ContextActions.Clear();
if (viewModel.IsAdmin)
{
theViewCell.ContextActions.Add(new MenuItem()
{
Text = "Delete",
});
theViewCell.ContextActions.Add(new MenuItem()
{
Text = "Edit",
});
}
}

Related

in Xamarin form for collection view items how to set button base on item's value? (conditional binfing)

I am doing a Xamarin form project, I have a list of tasks that can be completed or in completed. I want to add a button to display "Mark as completed" for in completed tasks and "Mark as in completed" for completed tasks. of course, it needs to refresh when the user clicks once.
I have a "CompletedDate" field that defines whether the task is completed when it has value.
TextColor="{StaticResource PrimaryBlack}" />
<CollectionView Grid.Row="1" ItemsSource="{Binding Tasks}" x:Name="List3">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame
Margin="0,10"
Padding="10"
BackgroundColor="{StaticResource PrimaryWhite}"
BorderColor="{StaticResource PrimaryLightGray}"
CornerRadius="10"
HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout Orientation="Horizontal">
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="Completed"
IsVisible="{Binding CompletedTaskVisibility}}"
TextColor="{Binding PrimaryPersianGreen}" />
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image
HeightRequest="20"
Source="iconCalender.png"
WidthRequest="20" />
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}"
TextColor="{StaticResource PrimaryBlack}"
/>
</StackLayout>
</StackLayout>
<BoxView
Grid.Row="1"
HeightRequest="1"
Color="{StaticResource PrimaryLightGray}" />
<Label
Grid.Row="2"
Style="{StaticResource LabelMedium}"
Text="{Binding Name}"
TextColor="{StaticResource PrimaryBlack}" />
<Label
Grid.Row="3"
Style="{StaticResource LabelMedium}"
Text="{Binding Description}"
TextColor="{StaticResource PrimaryBlack}" />
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed" />
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I have a "CompletedDate" field that defines whether the task is completed when it has value.
There are two solutions:
Create a boolean variable which checks the CompletedDate in your viewmodel and binding the Text to it. And then use a converter to convert the bool to string. About the clicked event, you can try to do the same thing as the Text or do a check in the click event in the page.cs.
Create a boolean variable in the view model too, but you need to create two button(one is "Mark as completed" and the other is "Mark as in completed") in the xaml and binding their IsVisible property to the boolean variable in the view model.
I suggest you to try the second solution, because the first one need to do a lot and convert the clicked event to a command. And the second one reduce much work.
Finally, you may try to use some other controls instead of the button. Such as:
CheckBox:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/checkbox
Switch:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/switch
Edit:
in the view model:
public ViewModel()
{
CompletedDate = ""; //set the value with the model data
if (CompletedDate != null)
{
isCompleted = true;
}
else isCompleted = false;
}
public event PropertyChangedEventHandler PropertyChanged;
bool isCompleted;
public bool IsCompleted {
get { return isCompleted; }
set {
isCompleted = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
}
}
}
in the xaml:
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed"
IsVisible="{Binding IsCompleted}"/>
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsInCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as in Completed"
IsVisible="{Binding IsCompleted}/>
You can also use the DataTrigger just like Ibrennan208 sayed, and then check the Text value in the page.cs:
private async void Button_Clicked(object sender, EventArgs e)
{
Button button = sender as Button;
if(button.Text == "Mark as Completed")
{
.........
}
else
{
.........
}
}
Update:
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding CompletedDate}" Value="{x:Null}">
<Setter Property="Text" Value="Mark Task as Completed"/>
</DataTrigger>
</Button.Triggers>
You could try adding a DataTrigger to your Button. They can be used to apply conditional bindings or values to properties such as the Button.Text property.
For a button you could implement it similarly to the following:
<Button Test="Mark Complete">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding IsCompleted}" Value="True">
<Setter Property="Text" Value="Mark Incomplete"/>
</DataTrigger>
</Button.Triggers>
</Button>
And then you could also add the IsCompleted field to your viewmodel:
public bool IsCompleted
get { return CompletedDate.HasValue; }
which would act as the switch for your button.
You would also need to add notification for the IsCompleted variable.
Calling OnPropertyChanged(nameof(IsCompleted)) when you set CompletedDate will do that.

Swipeitem doesnt work when open the app and press the button

The swipeview doesnt work when I open the app and press accept button but it works after when I open and close the detail page.
xaml shows list of events from sql and show swipeitem
<CollectionView x:Name="eventview"
BackgroundColor="Transparent"
ItemSizingStrategy="MeasureAllItems"
SelectionMode="Single"
SelectionChanged="eventview_SelectionChanged"
EmptyView="No Pending events">
<SwipeView.RightItems>
<SwipeItems SwipeBehaviorOnInvoked="Close">
<SwipeItem Text="Decline" BackgroundColor="red"/>
<SwipeItem Text="Accept" BackgroundColor="Green" Invoked="SwipeItem_Invoked"/>
</SwipeItems>
</SwipeView.RightItems>
<Grid Padding="10">
<StackLayout>
<Label Text="{Binding Name}" VerticalOptions="Center" FontSize="Small"/>
<Label Text="{Binding Date}" VerticalOptions="Center" FontSize="Small"/>
<Label Text="{Binding stats}" VerticalOptions="Center" FontSize="Small"/>
</StackLayout>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
cs shows click on the item that will open detail page and when click on the swipeitem
private async void eventview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
lastSelection = e.CurrentSelection[0] as Event;
var eventss = e.CurrentSelection.FirstOrDefault() as Event;
var eventpage = new Eventdetailpage();
eventpage.BindingContext = eventss;
await Navigation.PushAsync(eventpage);
}
private async void SwipeItem_Invoked(object sender, EventArgs e)
{
if (lastSelection != null)
{
lastSelection.stats = "Accepted";
await App.Database.UpdateventAysnc(lastSelection);
eventview.ItemsSource = await App.Database.querypendingevent();
accpetedevents.ItemsSource = await App.Database.queryacceptevent();
}
}
if lastSelection is null, then your if won't execute and nothing will happen
you should be able to get the current item from the sender's BindingContext
var item = (SwipeItem)sender;
lastSelection = (Event)item.BindingContext;

Passing data From ListView Item to next Form

I know how to send data from an Entry, Label to the next Form , but how can i do this with a ListView.
I want a simple direct approach.
My XAML code :
<ListView x:Name="MyClientslistView" HasUnevenRows="true" ItemTapped="OnSalesReport">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" VerticalOptions="Center" Margin="5" >
<Label Text="{Binding client_name}" TextColor="#A7A392" />
<Label Text="{Binding client_address}" TextColor="Black" />
<Label Text="{Binding client_phone}" TextColor="Black" />
<Label Text="{Binding client_email}" TextColor="Black" />
<Label Text="{Binding AssignStatus}" TextColor="Black" />
<Label Text="{Binding assignmentComments}" TextColor="Black" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I already have loaded data from JSON to the ListView , now currently i want to move each ID on he Item to the Next Form , how can i tackle that ?
You can get the item from ItemTapped listener. In your case use below solution.
void OnSalesReport(object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
YourItemModel item = (YourItemModel)((ListView)sender).SelectedItem;
((ListView)sender).SelectedItem = null;
Navigation.PushAsync(new NextPage(item));
}
Sorry for the poor english
You can have your entire item at your event handler private void OnSalesReport(object sender, ItemTappedEventArgs e) through e.Item.
You must do var item = ((YourTypeClassItem)e.Item); then you get things like item.client_name and probably item.client_id.
I hope it helps you.

How to get the value from Listview Xamarin.form

I want to get the value from my clubid that I web service and pass it down to the next page so I would have a dynamic club detail. However, when I tried to do it, it shows me some errors. This are my codes.
ListClubs.xaml.cs
ClubApiClient service = new ClubApiClient();
public ListClubs ()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
await setClubs(Clublistview);
}
private async Task setClubs(ListView listview)
{
var clublist = await service.getClubList(2);
listview.ItemsSource = clublist.ToList();
}
async void OnListClubsClicked(object sender, EventArgs e)
{
int clubid = int.Parse(lbId.Text); //Error msg: The name 'lbId' does not exist in the context
await Navigation.PushAsync(new DetailedClub());
}
ListClubs.xaml
<ListView x:Name="Clublistview">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="White"
Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Image Source="{Binding Logo}" IsVisible="true" WidthRequest="42" HeightRequest="42"/>
<Button Text="{Binding Name}" x:Name="BtnClub" AbsoluteLayout.LayoutBounds="100, 25, 100, 25" Clicked="OnListClubsClicked" TextColor="Black" VerticalOptions="FillAndExpand"/>
<Label Text="{Binding Id}" x:Name="lbId" IsVisible="false"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
using the name "lbId" isn't useful because every item in your list has a Label named "lbId".
You can use CommandParameter to add the Id to your Button
<Button Text="{Binding Name}" x:Name="BtnClub" AbsoluteLayout.LayoutBounds="100, 25, 100, 25" Clicked="OnListClubsClicked" TextColor="Black" VerticalOptions="FillAndExpand" CommandParameter="{Binding Id}" />
async void OnListClubsClicked(object sender, EventArgs e)
{
Button btn = (Button) sender;
int clubid = (int) btn.CommandParameter;
// pass the clubid to your Detail page
await Navigation.PushAsync(new DetailedClub(clubid));
}

Xamarin.Forms.Xaml.XamlParseException: Position 16:13. No Property of name Cliked found

I'm following a tutorial to make an app that receives and sends data to a web service.
But the time to create a button on the MainPage to bring the registration screen of the Visual Studio this error
Xamarin.Forms.Xaml.XamlParseException:. Position 16:13 In the Property of name Cliked found
and I lost because my this code like the tutorial.
Xaml:
<StackLayout Orientation="Vertical">
<Button Clicked="Btn_Clicked" Text = "test button" />
<ListView ItemsSource="{Binding ContactList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" Padding="12,6">
<Label Text="{Binding Name}" FontSize="24"/>
<Label Text="{Binding Email}" FontSize="24"/>
<Label Text="{Binding City}" FontSize="24"/>
<Label Text="{Binding State}" FontSize="24"/>
<Label Text="{Binding Zip}" FontSize="24"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Xaml.cs
using System;
using Xamarin.Forms;
namespace ContactManagerApp.Views
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void Btn_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new NewContactPage());
}
}
}
Clicked is an event not a property so you cannot bind it. You should Bind Command property of Button. You should add a method into page.xaml.cs if you want to use Clicked event.
Xaml:
< Button Clicked="Btn_Clicked" Text = "test button" />
Xaml.cs
private void Btn_Clicked(object sender, EventArgs e)
{
//Do stuff
}

Categories