I created a simple program with MAUI to load 2 images, specified as Embedded Resources.
This is the MainPage:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Surprise.MainPage">
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="550"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<VerticalStackLayout x:Name="ImageContainer"
Grid.Row="0"
HeightRequest="500"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Grid Grid.Row="1"
HorizontalOptions="Center"
HeightRequest="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Text="Image1"
Grid.Column="0"
Clicked="OnImage1ButtonClicked" />
<Button Text="Image2"
Grid.Column="2"
Clicked="OnImage2ButtonClicked" />
</Grid>
</Grid>
</ScrollView>
</ContentPage>
and this is its code behind:
public partial class MainPage : ContentPage
{
private readonly Image _image1;
private readonly Image _image2;
public MainPage()
{
InitializeComponent();
_image1 = new Image()
{
Source = ImageSource.FromResource("Surprise.Resources.Images.image1.jpeg"),
VerticalOptions = LayoutOptions.Center,
HeightRequest = 500
};
_image2 = new Image()
{
Source = ImageSource.FromResource("Surprise.Resources.Images.image2.jpg"),
VerticalOptions = LayoutOptions.Center,
HeightRequest = 500
};
}
private void OnImage1ButtonClicked(object sender, EventArgs e)
{
ImageContainer.Children.Clear();
ImageContainer.Children.Add(_image1);
}
private void OnImage2ButtonClicked(object sender, EventArgs e)
{
ImageContainer.Children.Clear();
ImageContainer.Children.Add(_image2);
}
}
On Windows it works correctly.
On Android sometimes the images are loaded in wrong order or even the same image is loaded when I press each button.
Is it a MAUI bug or I'm missing something?
Thanks in advance.
Well, there are some changes that were made with Maui projects, we now have Maui-specific build actions that you can use to specify to your project/solution what the type of a certain file is.
In your Scenario, you should use the MauiImage build action then just uses the name of the file as shown here: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/image#load-a-local-image should be enough to fix your issue
Related
Is there a way to show / hide gui when I click on another GUI element? I am doing MAUI GUI and using CollectionView I generate list of buttons. When I click on one I want to show option 1 and 2 for that item, if I click on another I want to show it´s options and hide the old ones.
I use MVVM, that is why I ask for GUI option only.
Code for list:
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MyItem">
<StackLayout>
<Frame Margin="10,5">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="{Binding Name}"/>
<Label Grid.Column="1" Text="{Binding Number}" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Text="{Binding Description}"/>
</Grid>
</Frame>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="2" Text="Option 1" Margin="5"/>
<Button Grid.Row="2" Grid.Column="1" Text="Option 2" Margin="5"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
As i understand, your case needs to implement behavior which could be expressed by phrase "only one of many".
Jason's solution (bool property bound to IsVisible of your control) will work, however it will get messy along with your button count rising - you would need to handle every single button and it's bound property - why would you want to do that?
In addition i'm sure you will eventually need this again in another View, so you would have to repeat that - it's not really DRY, isn't it?
I would suggest to use more reusable approach.
1. Create a converter class:
[ValueConversion(typeof(object), typeof(bool))]
public class IsEqualConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return targetType == typeof(bool)
? Equals(value, parameter)
: throw new InvalidOperationException("Converter can only convert to value of type bool.");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("Invalid call - one way only");
}
}
2.Reference your converter in desired View :
<ContentPage.Resources>
<converters:IsEqualConverter x:Key="IsEqualConverter" />
</ContentPage.Resources>
3. Set the SelectionMode of your CollectionView to Single.
<CollectionView SelectionMode="Single">
4. Bind IsVisible of your Grid, to SelectedItem of your CollectionView using syntax below:
<Grid IsVisible="{Binding Name, Converter={StaticResource IsEqualConverter}, ConverterParameter={Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type CollectionView}}, Path=SelectedItem.Name}}">
Assuming Name is unique of course.
[Optional]
5. Create and implement Interface providing unique identification.
public interface IIdentifyUniquely
{
public Guid UniqueIdentifier { get; set; }
}
public class MyItem : IIdentifyUniquely
{
public string Name { get; set; }
public int Number { get; set; }
public Guid UniqueIdentifier { get; }
public MyItem()
{
UniqueIdentifier = Guid.NewGuid();
}
}
Then bind to implemented UniqueIdentifier instead.
I will point out here few options you have, using "MVVM and UI only"
You can use triggers.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/triggers?view=net-maui-7.0
With this you can make your UI dependent on properties of your Model.
You can use Styling and VisualStates.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/visual-states?view=net-maui-7.0
With this you can change the appearance of your UI Elements, depending on their visual state. For example, if you decide make use of "Selected" property of your item.
You can use alternatives for showing additional UI elements for each item. For example - SwipeView.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/swipeview?view=net-maui-7.0
You can try writing your own, reusable ControlTemplate.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/controltemplate?view=net-maui-7.0
You can get 3rd party controls. Here is one of the CommunityToolkit, that does something very similar:
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/views/expander
There are really so many ways to handle your problem. You can also do what Jason said, and just bind to IsVisible and deal with it manually.
You can add a TapGestureRecognizer attached to Frame:
<ScrollView>
<CollectionView>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MyItem">
<StackLayout>
<Frame Margin="10,5">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</Frame.GestureRecognizers>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="{Binding Name}"/>
<Label Grid.Column="1" Text="{Binding Number}" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Text="{Binding Description}"/>
</Grid>
</Frame>
<Grid IsVisible="false">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="2" Text="Option 1" Margin="5"/>
<Button Grid.Row="2" Grid.Column="1" Text="Option 2" Margin="5"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
And Page.xaml.cs:
public partial class NewPage : ContentPage
{
Grid grid = new Grid();
public NewPage()
{
InitializeComponent();
...
}
private void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
{
grid.IsVisible = false;
Frame frame = (Frame)sender;
StackLayout stacklayout = (StackLayout)frame.Parent;
grid = (Grid)stacklayout.Children[stacklayout.Children.Count - 1];
grid.IsVisible = true;
}
}
I am using ZXing.Net.Mobile in a Xamarin.Forms project. It works fine on iOS, but on Android, the first time I use the scanner, I get the camera permissions popup, and grant them.
There is no image from the camera immediately after the permissions. If I quit the app and re-launch, the camera works fine from that point forward. How can I get it to work right away? Even if I close my view, dispose and re-initialize it, it still doesn't work until the app restarts.
Here is relevant code:
public partial class CEUScanModalPage : ContentPage, INotifyPropertyChanged {
private bool _isScanning;
public CEUScanModalPage() {
InitializeComponent();
IsScanning = false;
BindingContext = this;
Indicator.Start();
}
public bool IsScanning {
get {
return _isScanning;
}
set {
_isScanning = value;
OnPropertyChanged();
}
}
private async void OnCloseButtonClicked(object sender, EventArgs args) {
await Navigation.PopModalAsync();
}
protected override void OnAppearing() {
base.OnAppearing();
IsScanning = true;
}
protected override void OnDisappearing() {
base.OnDisappearing();
_scanView.OnScanResult -= Handle_OnScanResult;
_scanView.IsAnalyzing = false;
_scanView.IsScanning = false;
ParentGrid.Children.Remove(_scanView);
_scanView = null;
}
public async void Handle_OnScanResult(Result result) {
IsScanning = false;
}
}
XAML:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:BehaviorLive" xmlns:grial="clr-namespace:UXDivers.Grial;assembly=UXDivers.Grial" xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms" xmlns:fftransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations" xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
x:Class="BehaviorLive.Views.CEUScanModalPage"
NavigationPage.HasNavigationBar="False"
Style="{ StaticResource MainMenuOrModalBackgroundStyle }">
<ContentPage.Resources>
<ResourceDictionary
Source="/Styles/TabControl/SimpleTabResources.xaml" />
</ContentPage.Resources>
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="145" />
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Grid
RowSpacing="0"
ColumnSpacing="0"
Margin="0,0,0,10"
VerticalOptions="EndAndExpand">
<Grid.RowDefinitions>
<RowDefinition
Height="10" />
<RowDefinition
Height="Auto" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="120" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<!-- LOGO -->
<Label
Grid.Row="0"
Grid.RowSpan="3"
Margin="20,0"
TextColor="{ DynamicResource InverseTextColor }"
FontSize="74"
FontFamily="{StaticResource FontAwesomeSolid}"
Text="{ x:Static local:FontawesomeSolidFont.Qrcode }"
Style="{ StaticResource FontIcon }"
HorizontalOptions="Start"
VerticalTextAlignment="Center" />
<!-- WELCOME -->
<Label
Grid.Row="1"
Grid.Column="1"
Text="CEU Checkin"
FontSize="22"
TextColor="{ DynamicResource InverseTextColor }"
Style="{ StaticResource LabelBoldStyle }"
VerticalOptions="End"
HorizontalOptions="Start" />
<!-- SUB -->
<Label
Grid.Row="2"
Grid.Column="1"
Text="Scan QR Code to check in/out of your event"
FontSize="16"
TextColor="{ DynamicResource InverseTextColor }"
VerticalOptions="Start"
HorizontalOptions="Start" />
</Grid>
<Grid
Grid.Row="1"
BackgroundColor="{ DynamicResource MainWrapperBackgroundColor }"
grial:Effects.ApplyIOSSafeAreaAsPadding="Bottom" x:Name="ParentGrid">
<!-- TAB -->
<StackLayout x:Name="LoadingIndicator" Grid.Row="0" VerticalOptions="CenterAndExpand">
<local:CustomActivityIndicator
Grid.Row="0"
x:Name="Indicator"
HorizontalOptions="Center"
VerticalOptions="Start" />
<Label
Grid.Row="1"
FontSize="Large"
HorizontalTextAlignment="Center"
Text="{ grial:Translate StringLoading }" />
</StackLayout>
<zxing:ZXingScannerView Grid.Row="0" Grid.RowSpan="2" x:Name="_scanView" OnScanResult="Handle_OnScanResult" IsScanning="{Binding IsScanning}"
WidthRequest="200" HeightRequest="200" />
</Grid>
<!-- CLOSE ICON -->
<Label
Style="{StaticResource IconCloseLabelStyle}">
<Label.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnCloseButtonClicked" />
</Label.GestureRecognizers>
</Label>
</Grid>
</ContentPage.Content>
</ContentPage>
MainActivity.cs
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults) {
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
The best way to fix the issue would be to handle the permission part yourself
A generic permission helper:
using System.Threading.Tasks;
using Xamarin.Essentials;
using static Xamarin.Essentials.Permissions;
public static async Task<PermissionStatus> CheckAndRequestPermissionAsync<TPermission>()
where TPermission : BasePermission, new()
{
TPermission permission = new TPermission();
var status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await permission.RequestAsync();
}
return status;
}
Then use it like
var status = await PermissionsHelper.CheckAndRequestPermissionAsync<Permissions.Camera>();
if(status != PermissionStatus.Granted)
{
return;
}
I put the SfTreeView inside an SfExpander, and the TreeView takes up a lot of space under it.[enter image description here][1]
I tried to put in into a grid and set the height to Auto but didn't work.
[1]: https://i.stack.imgur.com/kDP3l.png
<expander:SfExpander DynamicSizeMode="Content" IsExpanded="False">
<expander:SfExpander.Header>
<Label TextColor="#495F6E" Text="TreeView list" FontSize="16"
</expander:SfExpander.Header>
<expander:SfExpander.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<treeview:SfTreeView x:Name="treeView2"
ItemHeight="34"
ItemsSource="{Binding ImageNodeInfo}">
<treeview:SfTreeView.ItemTemplate>
<DataTemplate>
<Grid x:Name="grid" Padding="2,2,2,2" RowSpacing="0" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Level, Converter={StaticResource IndentationConverter}}" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="1" Source="{Binding IsExpanded,Converter={StaticResource ExpanderIconConverter}}"
IsVisible="{Binding HasChildNodes,Converter={StaticResource ExpanderIconVisibilityConverter}}"
VerticalOptions="Center"
HorizontalOptions="Center"
HeightRequest="15"
WidthRequest="15"/>
<Image Grid.Column="2" Source="{Binding Content.ImageIcon}" VerticalOptions="Center" HorizontalOptions="Start" HeightRequest="16" WidthRequest="60"/>
<Grid Grid.Column="3" RowSpacing="1" Padding="1,0,0,0" VerticalOptions="Center">
<Label LineBreakMode="NoWrap" Text="{Binding Content.ItemName}" VerticalTextAlignment="Center" FontSize="Body" FontAttributes="Bold"/>
</Grid>
</Grid>
<BoxView HeightRequest="1" BackgroundColor="Gray" Grid.Row="1"/>
</Grid>
</DataTemplate>
</treeview:SfTreeView.ItemTemplate>
</treeview:SfTreeView>
</Grid>
</expander:SfExpander.Content>
</expander:SfExpander>
We have checked the reported query “How to set the dynamic height” from our end. We would like to inform you that TreeView will be loaded with the view size. If you want to customize the height of TreeView, you can set HeightRequest for TreeView based on the item size. Please refer the following code snippet to achieve your requirement,
Xaml: Use EventToCommand behavior for SfTreeView use command for QueryNodeSize event. Bind HeightRequest of TreeView to update the treeview height based on nodes.
<syncfusion:SfTreeView x:Name="treeView"
ItemHeight="40"
HeightRequest="{Binding TreeViewHeight}"
ChildPropertyName="SubFiles"
ItemTemplateContextType="Node"
AutoExpandMode="AllNodesExpanded"
ItemsSource="{Binding ImageNodeInfo}"
BackgroundColor="Beige">
<syncfusion:SfTreeView.Behaviors>
<local:EventToCommandBehavior EventName="QueryNodeSize" Command="{Binding Path=BindingContext.QueryNodeSizeCommand, Source={x:Reference treeView}}"/>
</syncfusion:SfTreeView.Behaviors>
…
</syncfusion:SfTreeView>
ViewModel: Create Command with QueryNodeSizeEventArgs.
private double treeViewHeight = -1;
//Property to set TreeView height.
public double TreeViewHeight
{
get { return treeViewHeight; }
set
{
treeViewHeight = value;
this.RaisedOnPropertyChanged("TreeViewHeight");
}
}
//Command for QueryNodeSize event.
public Command<QueryNodeSizeEventArgs> QueryNodeSizeCommand { get; set; }
public FileManagerViewModel()
{
//Initialize the command.
QueryNodeSizeCommand = new Command<QueryNodeSizeEventArgs>(QueryNodeSizeMethod);
}
/// <summary>
/// Command method to calculate the TreeView height.
/// </summary>
/// <param name="args">Argument which holds the information of the node.</param>
private void QueryNodeSizeMethod(QueryNodeSizeEventArgs args)
{
var item = args.Node.Content as FileManager;
if (!item.IsHandled)
{
this.TreeViewHeight += args.Height;
item.IsHandled = true;
}
}
We have attached the workable sample based on your requirement. You can find them from the following link,
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/TreeViewXamarin-1814245514
Regards,
SaiGanesh Sakthivel
I am trying to run a timer that updates a label every second on background (async). The timer should run from 0 to 60 min and only show {MIN:SEC} in the label. But I am having a hard time to run the timer in background that updates the label. This is what I had for so for but it won't work:
Timer timer;
Stopwatch sw;
public TimerViewModel()
{
Title = "Tijd";
StartTimer();
}
private async void StartTimer()
{
timer = new Timer();
timer.Interval = (1000);
//timer.Elapsed += new EventHandler(UpdateLabel);
sw = new Stopwatch();
timer.Start();
sw.Start();
}
private async void UpdateLabel()
{
await Task.Run(async () =>
{
}).ConfigureAwait(false);
}
MainPage:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:EscapeHaarlem.Views"
x:Class="EscapeHaarlem.Views.MainPage">
<TabbedPage.Children>
<NavigationPage Title="Tijd">
<NavigationPage.Icon>
<OnPlatform x:TypeArguments="FileImageSource">
<On Platform="iOS" Value="tab_about.png"/>
</OnPlatform>
</NavigationPage.Icon>
<x:Arguments>
<views:AboutPage />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Hints">
<NavigationPage.Icon>
<OnPlatform x:TypeArguments="FileImageSource">
<On Platform="iOS" Value="tab_feed.png"/>
</OnPlatform>
</NavigationPage.Icon>
<x:Arguments>
<views:ItemsPage />
</x:Arguments>
</NavigationPage>
</TabbedPage.Children>
About page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EscapeHaarlem.Views.AboutPage"
xmlns:vm="clr-namespace:EscapeHaarlem.ViewModels"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:TimerViewModel />
</ContentPage.BindingContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackLayout x:Name="RealTimeTimer" Grid.Column="0" VerticalOptions="Center">
<Label Text="{Binding RealTimeTimer}" HorizontalTextAlignment="Center" Rotation="270" FontSize="150" HorizontalOptions="Center"></Label>
</StackLayout>
<StackLayout Grid.Column="1" VerticalOptions="Center">
<Label Text="Straftijd" HorizontalTextAlignment="Center" Rotation="270" FontSize="40" HorizontalOptions="End"></Label>
</StackLayout>
<StackLayout Grid.Column="2" VerticalOptions="Center">
<Label Text="00:00" HorizontalTextAlignment="Center" Rotation="270" FontSize="40" HorizontalOptions="Start" ></Label>
</StackLayout>
</Grid>
thanks in advance
You are wrongly assigning the event handler as seen in the docs.
Change
timer.Elapsed += new EventHandler(UpdateLabel);
to
timer.Elapsed +=UpdateLabel; //or
timer.Elapsed +=System.EventHandler(UpdateLabel)
not sure from the top of my head
And
private async void UpdateLabel()
to
private static void UpdateLabel(Object source, System.Timers.ElapsedEventArgs e)
This should allow you to to trigger the Elapsed event. You problem was your confusing regular functions and event functions. You should read up on it here
Im building a cross platform app. I have just finished my prototype and it works well on android. However, i started testing the prototype on iOS and i am getting some problems that i dont know how to solve and i wish you guys can help me.
So, for now i have 2 problems:
1st > On android button inside listview is recognizing its event handler/command and firing the event. But on iPhone, its being ignored.
2nd > IPhone seems to be adding some icons to my view that didnt apear on android and they have no utility for me. Is there any way to remove them ?
Xaml:
<local:CustomListView.ItemTemplate>
<DataTemplate>
<local:CustomViewCell>
<ContentView Padding="10,10,10,0">
<Frame BackgroundColor="{Binding Cor}" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="2" Image="{Binding ImageSource}" Rotation="90" BackgroundColor="Transparent" HorizontalOptions="EndAndExpand"
CommandParameter="{Binding .}" Command="{Binding BindingContext.CommandoOpcoes, Source={x:Reference tarefas}}"/>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Scale="0.7" Source="{local:ImageResource x.Images.location1.png}"/>
<Label Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Text="{Binding Titulo}" FontAttributes="Bold" FontSize="Medium" VerticalTextAlignment="Center"/>
<Image Grid.Row="2" Grid.Column="0" Scale="0.7" Source="{local:ImageResource x.Images.clock.png}"/>
<Label Text="{Binding Duracao}" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" VerticalTextAlignment="Center"/>
<Image Grid.Row="3" Grid.Column="0" Scale="0.7" Source="{local:ImageResource x.Images.location2.png}"/>
<Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding ObjectivoAno}" VerticalTextAlignment="Center"/>
</Grid>
</Frame>
</ContentView>
View Model :
public Command CommandoOpcoes
{
get;
private set;
}
/*
* Menu de um item da lista clicado
* Apresenta opções de editar, apagar e marcar uma tarefa como concluída
*/
private void MostraOpcoes(object t)
{
var Tarefa = t as Tarefa;
async void apagarTarefa()
{
var res = await App.Current?.MainPage?.DisplayAlert(AppResource.Confirmacao, AppResource.Apagar, AppResource.Nao, AppResource.Sim);
if (res == false)
{
Tarefas.Remove(Tarefa);
NTarefas--;
}
}
async void editarTarefa()
{
var page = new EditarTarefa()
{
BindingContext = Tarefa
};
if (PopupNavigation.Instance.PopupStack.Count > 0)
await PopupNavigation.Instance.PopAllAsync(false);
await PopupNavigation.Instance.PushAsync(page, true);
}
void completarTarefa()
{
if (Tarefa.Cumprido == false)
{
Tarefa.Cumprido = true;
Tarefa.Cor = Color.FromHex("#E5F2E5");
NTarefasConcluidos++;
if (NTarefasNaoConcluidos > 0)
NTarefasNaoConcluidos--;
}
else
{
Tarefa.Cumprido = false;
Tarefa.Cor = Color.FromHex("#FFE5E5");
NTarefasNaoConcluidos++;
if (NTarefasConcluidos > 0)
NTarefasConcluidos--;
}
}
ActionSheetConfig config = new ActionSheetConfig();
if (Tarefa.Cumprido)
config.Add(AppResource.nCompleto, completarTarefa, "completo.png");
else
{
config.Add(AppResource.EditarTarefa, editarTarefa, "edit.png");
config.Add(AppResource.Completo, completarTarefa, "completo.png");
}
config.SetDestructive(AppResource.ApagarTarefa, apagarTarefa, "delete.png");
config.UseBottomSheet = true;
UserDialogs.Instance.ActionSheet(config).Dispose();
UserDialogs.Instance.ActionSheet(config);
}
public TarefasViewModel()
{
Tarefas = new ObservableCollection<Tarefa>(App.tarefas.Where(x => x.Data == DateTime.Today).ToList());
NTarefas = Tarefas.Count;
NTarefasConcluidos = Tarefas.Where(x => x.Cumprido == true).Count();
NTarefasNaoConcluidos = Tarefas.Where(x => x.Cumprido == false).Count();
CommandoOpcoes = new Command(q => MostraOpcoes(q));
}
Image of view on iOS with the undesired icons :
https://imgur.com/VTXRXNh
Image on android :
https://imgur.com/v6dfkEW
So, i found the solution. It was quite easy.On ViewCell, the StyleId property can add disclosure buttons to iOS lists.
To not have any of these disclosure buttons, i just made :
<ViewCell StyeleId="none">...</ViewCell>