I have:
<Grid.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Source={RelativeSource AncestorType={x:Type locals:OneViewModel}},
Path=OneTappedView}" CommandParameter="{Binding .}" />
</Grid.GestureRecognizers>
<Button x:Name="bt_one" Clicked="bt_one_Clicked"/>
When I do Grid Tap, Command and bt_one_Clicked execute concurrently? Thank you
When I do Grid Tap, Command and bt_one_Clicked execute concurrently?
Yes, you can add the button's clicked code in your grid tap event when tapping your grid.
You can refer to the following code:
OnePage.xml
<?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:formapp1="clr-namespace:FormApp1"
x:Class="FormApp1.OnePage">
<ContentPage.BindingContext>
<formapp1:OneViewModel></formapp1:OneViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Grid WidthRequest="300" HeightRequest="600" BackgroundColor="Yellow">
<Grid.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding OneTappedViewCommand}" />
</Grid.GestureRecognizers>
</Grid>
<Button x:Name="bt_one" Text="one button" Command="{Binding BtnOneClickedCommand}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
OneViewModel.cs
public class OneViewModel: INotifyPropertyChanged
{
public ICommand OneTappedViewCommand { private set; get; }
public ICommand BtnOneClickedCommand { private set; get; }
public OneViewModel() {
OneTappedViewCommand = new Command(GridTapped);
BtnOneClickedCommand= new Command(btnOneClickedMthod);
}
private void GridTapped()
{
System.Diagnostics.Debug.WriteLine("----111------> GridTapped is triggered......");
//add one clicked method here
btnOneClickedMthod();
}
private void btnOneClickedMthod()
{
System.Diagnostics.Debug.WriteLine("----222------> GridTapped is triggered......");
}
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;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note:
In model OneViewModel, you can add bt_one event(btnOneClickedMthod) in grid tap function GridTapped.
Related
I made a very simple .NET MAUI App based on the ClickMe Code thats generated by VS2022.
The "ClickMe" Button should add a entry to a CollectionView which is binded to a ObservableCollection, but it don't populate the view if click the button although "monkeys" are added to the ObservableCollection maybe somebody can help what I'm missing.
public class Monkey
{
public string Name { get;set; }
public Monkey(string name) {Name = name; }
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiObservableList.MainPage">
<ScrollView>
<VerticalStackLayout Spacing="25" Padding="30,0" >
<Button x:Name="CounterBtn" Text="Click me" Clicked="OnCounterClicked" HorizontalOptions="Center" />
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs:
int count = 0;
public ObservableCollection<Monkey> Monkeys { get; set; } = new ObservableCollection<Monkey>();
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
Monkeys.Add(new Monkey(CounterBtn.Text));
}
Replace TextCell with a Label. TextCell only works with ListView
i want set name for check box and use in code for post method for api
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<controls:CheckBox DefaultText="{Binding Name}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Since you had used MVVM . I suggest that you should handle all logic in your ViewModel .You can get the value and index of CheckBox in ViewModel.
I used the CheckBox plugin from https://github.com/enisn/Xamarin.Forms.InputKit .
in your xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:App12"
xmlns:input="clr-namespace:Plugin.InputKit.Shared.Controls;assembly=Plugin.InputKit"
mc:Ignorable="d"
x:Name="contentPage" // set the name of content page
x:Class="xxx.MainPage">
<ListView x:Name="listview" ItemsSource="{Binding MyItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<input:CheckBox Text="{Binding Name}" Type="Check" IsChecked="{Binding IsCheck,Mode=TwoWay}" CheckChangedCommand="{Binding Source={x:Reference contentPage}, Path=BindingContext.CheckCommand}" CommandParameter="{Binding }"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in your model
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name { get; set; }
private bool isCheck;
public bool IsCheck
{
get
{
return isCheck;
}
set
{
if (isCheck != value)
{
isCheck = value;
NotifyPropertyChanged();
}
}
}
}
in Viewmodel or Code behind
public ObservableCollection<Model> MyItems { get; set; }
public ICommand CheckCommand { get; private set; }
public YourViewModel()
{
MyItems = new ObservableCollection<Model>() {
new Model(){Name="xxx",IsCheck=true },
//...
};
CheckCommand = new Command((arg)=> {
var model = arg as Model;
for(int i=0;i<MyItems.Count;i++)
{
if (model == MyItems[i])
{
// i is the index that you checked
bool ischeck = MyItems[i].IsCheck;
// do some thing you want
}
}
});
}
I would suggest adding a Binding for the CheckBox State:
<controls:CheckBox x:Name="chechBox" DefaultText="{Binding Name}" IsChecked="{Binding IsChecked}" />
And then, in the ListView ItemTapped event:
void OnSelection (object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) {
return; //ItemSelected is called on deselection, which results in SelectedItem being set to null
}
var item = (YourModel)e.SelectedItem;
if(item != null)
{
var checkBoxState = item.IsChecked;
}
}
I have a custom control that I supply a List<string> parameter and it draws a box for each string as a label.
Here is a version that is working. Clicking 'Add Tab' adds one tab at a time for each click.
I want to change the code so the List is converted into a different type of object and that is what the control displays.
First I show the code that is working for the image above. Then I show a changed version of the code that I am unable to get working. Hopefully for anyone answering this question, seeing the before code that works and the after code that doesn't work, you can easily spot the issue.
MainPage.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:App7"
x:Class="App7.MainPage">
<StackLayout>
<BoxView HeightRequest="100" />
<local:CustomControl
SideTabs="{Binding MainNavigationTabs}"
/>
<Button Text="Add Tab" Command="{Binding AddTab}" />
</StackLayout>
</ContentPage>
MainPageModel.cs
public class MainPageModel : FreshBasePageModel
{
public MainPageModel() { }
public List<string> MainNavigationTabs { get; set; }
private int _index = 0;
public Command AddTab
{
get
{
return new Command(() =>
{
_index++;
var tabs = new List<string>();
for (var index = 1; index <= _index; index++)
{
tabs.Add($"Tab {index}");
}
MainNavigationTabs = tabs;
});
}
}
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding .}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<string>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
I changed the CustomControl to transform the List<string> to a List<SideTab> object and have the control bind to that. Here's the code...
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get
{
var tabs = new List<string>();
foreach (var tab in _SideTabs)
{
tabs.Add(tab.Text);
}
return tabs;
}
set
{
var tabs = new List<SideTab>();
foreach (var tab in value)
{
tabs.Add(new SideTab() { Text = tab });
}
_SideTabs = tabs;
}
}
public List<SideTab> _SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<SideTab>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
public class SideTab
{
public string Text { get; set; }
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=_SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding Text}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
Notice the addition of a property _SideTabs. When SideTabs is set, it transforms the List<string> into a List<SideTab>.
How can I make this work? Here is the result from the above code changes...
Try like this,
public static readonly BindableProperty TabsListProperty = BindableProperty.Create(nameof(TabsList), typeof(List<TabItem>), typeof(ScrollableTabs), null, propertyChanged: (bindable, oldValue, newValue) =>
{
((ScrollableTabs)bindable).InitializeTabs();
});
private void InitializeTabs()
{
//Write your logic here
}
public List<TabItem> TabsList
{
get
{
return (List<TabItem>)GetValue(TabsListProperty);
}
set
{
SetValue(TabsListProperty, value);
}
}
I'm trying to get working the MediaManager library playing a couple videos from youtube using MVVM approach.
My idea is to have a single view where initially is loaded a video, once the user watched the first loaded video he can click a button to see another video in the same view.
I can't find many examples on the internet related about how to accomplish this using MVVM, every example I've found uses the codebehind approach but my app is created in full MVVM so I need to get it working like that.
This is what I've done by now.
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:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:mediamanager="clr-namespace:Plugin.MediaManager.Forms;assembly=Plugin.MediaManager.Forms"
x:Class="VideoView"
BindingContext="{Binding VideoViewModel, Source={StaticResource ServiceLocator}}">
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
<StackLayout Margin="10,60,10,0">
<Label x:Name="LblMsg"/>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<mediamanager:VideoView x:Name="TrainingVideoPlayer"
AspectMode="AspectFill"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
<StackLayout VerticalOptions="End" HorizontalOptions="FillAndExpand">
<ProgressBar x:Name="progress" HeightRequest="10" Progress="{Binding ProgressStatus, Mode=TwoWay}" />
<StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand" Spacing="10" VerticalOptions="End">
<Image x:Name="ImgPlay"
Source="video_play.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PlayTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgPause"
Source="video_pause.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PauseTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
<Image x:Name="ImgStop"
Source="video_stop.png"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StopTappedCommand}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</Grid>
<Button x:Name="BtnNext" Command="{Binding NextCommand}"/>
</StackLayout>
</ContentPage>
ViewModel
public class VideoViewModel : AppBaseViewModel
{
//Commands
public ICommand PageAppearingCommand { get; set; }
public ICommand PlayTappedCommand { get; set; }
public ICommand PauseTappedCommand { get; set; }
public ICommand StopTappedCommand { get; set; }
public ICommand NextCommand { get; set; }
//Fields
private double _progressStatus;
private string youtubevideourl;
//Properties
public double ProgressStatus
{
get => _progressStatus;
set
{
if (Set(ref _progressStatus, value))
{
RaisePropertyChanged(() => ProgressStatus);
}
}
}
public VideoViewModel()
{
PageAppearingCommand = new Command(OnPageAppearing);
PlayTappedCommand = new Command(OnImgPlay_Tapped);
PauseTappedCommand = new Command(OnImgPause_Tapped);
StopTappedCommand = new Command(OnImgStop_Tapped);
NextCommand = new Command(OnBtnNext_Click);
youtubevideourl = "https://urloftheyoutubevideo";
}
private async void OnPageAppearing()
{
await CrossMediaManager.Current.Stop();
//Sets the videoplayer events to control playback status
CrossMediaManager.Current.PlayingChanged += VideoPlayer_PlayingChanged;
CrossMediaManager.Current.MediaFinished += VideoPlayer_MediaFinished;
}
private async void OnImgPlay_Tapped()
{
await CrossMediaManager.Current.Play(youtubevideourl, MediaFileType.Video);
}
private async void OnImgPause_Tapped()
{
await CrossMediaManager.Current.Pause();
}
private async void OnImgStop_Tapped()
{
await CrossMediaManager.Current.Stop();
}
private void VideoPlayer_PlayingChanged(object sender, PlayingChangedEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
ProgressStatus = e.Progress;
});
}
private void VideoPlayer_MediaFinished(object sender, MediaFinishedEventArgs e)
{
//Some logic
}
private async void OnBtnNext_Click()
{
//logic to load the next video url
}
}
Using this code I can execute play/pause/stop methods of the MediaManager but nothing happens in the View, I cant even see 1 second of the youtube video.
I will appreciate your help
Note: I installed the MediaManager nuget package in my three projects: Android, iOS and NetStandard library
Note2: One of my requirements is to guarantee as much as possible the user watched the whole video before moving to the next one (the value will be stored in an external DB), so any solution different to this one must follow this requirement
I code that I use often in my application so I created this template. With a lot of help from the user gannaway the code I have so far is this:
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCellTemplate"
x:Name="this">
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Here's the code behind:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Japanese
{
public partial class SwitchViewCellTemplate : ViewCell
{
public event EventHandler SelectAction;
public SwitchViewCellTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SwitchViewCellTemplate));
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(SwitchViewCellTemplate));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsVisible
{
get { return (bool)GetValue(IsVisibleProperty); }
set { SetValue(IsVisibleProperty, value); }
}
protected override void OnTapped()
{
base.OnTapped();
this.SelectAction?.Invoke(this, new EventArgs());
}
}
}
and the way I would like to use it:
<template:SwitchViewCellTemplate Text="{Binding [1].Name}"
IsVisible="{Binding [1].IsSelected}"
SelectAction="selectValue" />
Here is the method used to handle toggled:
void Handle_SelectAction(object sender, System.EventArgs e)
{
var viewCell = sender as ViewCell;
if (viewCell == null)
return;
}
Where selectValue is a function in the CS code behind of the pages where I use the template.
The code gives an error:
The type initializer for 'Japanese.SwitchViewCellTemplate' threw an exception.
Can anyone give me advice on what might be wrong that is causing this error.
To solve, an event can be added to the template.
XAML Template
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCellTemplate"
x:Name="this">
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Add an event to your template's code-behind:
public partial class SwitchViewCellTemplate : ViewCell
{
public event EventHandler SelectAction;
public SwitchViewCellTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(SwitchViewCellTemplate),
default(string));
public static readonly BindableProperty IsVisibleProperty =
BindableProperty.Create(
nameof(IsVisible),
typeof(bool),
typeof(SwitchViewCellTemplate),
default(bool));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsVisible
{
get { return (bool)GetValue(IsVisibleProperty); }
set { SetValue(IsVisibleProperty, value); }
}
protected override void OnTapped()
{
base.OnTapped();
this.SelectAction?.Invoke(this, new EventArgs());
}
}
Page XAML
<template:SwitchViewCellTemplate Text="{Binding [1].Name}"
IsVisible="{Binding [1].IsSelected}"
SelectAction="Handle_SelectAction" />
Page Code-Behind
void Handle_SelectAction(object sender, System.EventArgs e)
{
}