I have a Grid of Six Rows and Five Columns. In each cell of the grid, I have placed a button using Observation Collection. I also have a ButtonCommand which implements ICommand interface.
Objective:
- On Load: Enable only One Button: Left button in First Row
- When user clicks on the enabled button:
(1) Disable the Clicked button
(2) Change Background Color of Clicked Button
(3) Enable next Button
I am so far able to disable all buttons and keep the left button in the first row as intended. However, I am unable to figure out how to disable it when Command Executes and enable the next button.
All buttons in the grid share a single ButtonCommand PrayerCommand - Line 78 to 162 in View Model.
Code is posted below:
View
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Navkar.Views.MatrixPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="1,20,1,1"
Android="1"
WinPhone="1"/>
</ContentPage.Padding>
<ContentPage.Resources>
<Style TargetType="Label">
<Setter Property="BackgroundColor" Value="LightBlue"/>
<Setter Property="FontSize" Value="Medium"/>
<Setter Property="FontAttributes" Value="Bold"/>
<Setter Property="Padding" Value="8,0,8,0"/>
</Style>
</ContentPage.Resources>
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="1" CornerRadius="0">
<StackLayout>
<Image x:Name="matrixBanner" Source="apbanner" VerticalOptions="Center" HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" NumberOfTapsRequired="2"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</Frame>
<StackLayout x:Name="matrixLabels" Orientation="Horizontal">
<Label Text="{Binding MatrixNumeral}" TextColor="Black" HorizontalTextAlignment="Start"/>
<Label Text="{Binding NavkarPrayer}" TextColor="{Binding PrayerColor}" HorizontalOptions="FillAndExpand"/>
</StackLayout>
<ScrollView VerticalOptions="FillAndExpand">
<StackLayout x:Name="matrixStack" HorizontalOptions="FillAndExpand">
<StackLayout>
<Grid x:Name="matrixGrid"
BindableLayout.ItemsSource="{Binding Matrix}"
BindableLayout.ItemTemplate="{StaticResource ButtonTemplate}"
RowSpacing="1" ColumnSpacing="1" Padding="1" HorizontalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</StackLayout>
<StackLayout x:Name="matrixNav" Orientation="Horizontal" HorizontalOptions="CenterAndExpand">
<Button Text="{Binding StyleCommandText}" StyleId="0" CommandParameter="1" Command="{Binding StyleCommand}"/>
<Button Text="Prev" StyleId="0" CommandParameter="-1" Command="{Binding PrevCommand}"/>
<Button Text="Reset" StyleId="1" CommandParameter="0" Command="{Binding ResetCommand}"/>
<Button Text="Next" StyleId="2" CommandParameter="1" Command="{Binding NextCommand}"/>
</StackLayout>
<StackLayout x:Name="matrixFooter">
<Label BackgroundColor="White" TextColor="Black" FontAttributes="None" FontSize="16" Padding="8,10,8,0">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Starting in the first row from left to right press each numeral/symbol and recite corresponding Navkar Line displayed above matrix. "/>
<Span Text="www.mynarada.ca" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</StackLayout>
</ScrollView>
</StackLayout>
</ContentPage>
View Code Behind
using Navkar.Models;
using Navkar.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Navkar.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MatrixPage : ContentPage
{
private readonly MatrixModelObj _MatrixModelObj = new MatrixModelObj();
private readonly TapViewModel _TapViewModel = new TapViewModel();
public MatrixPage()
{
InitializeComponent();
matrixBanner.BindingContext = _TapViewModel;
MatrixViewModel _MatrixViewModel = new MatrixViewModel(_MatrixModelObj);
BindingContext = _MatrixViewModel;
}
}
}
Bindable Layout - App.Xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Navkar.App">
<Application.Resources>
<DataTemplate x:Key="ButtonTemplate">
<Button
Text="{Binding Text}" TextColor="{Binding TextColor}"
FontSize="Title" FontAttributes="Bold"
BackgroundColor="{Binding BackgroundColor}" CornerRadius="10"
Grid.Row="{Binding GridRow}" Grid.Column="{Binding GridColumn}"
CommandParameter="{Binding CommandParam}" Command="{Binding CommandAction}"/>
</DataTemplate>
<DataTemplate x:Key="ImageButtonTemplate">
<ImageButton
Source="{Binding StyleId, StringFormat='ap{0}'}"
BackgroundColor="{Binding BackgroundColor}" CornerRadius="10"
Grid.Row="{Binding GridRow}" Grid.Column="{Binding GridColumn}"
CommandParameter="{Binding CommandParam}" Command="{Binding CommandAction}"/>
</DataTemplate>
</Application.Resources>
</Application>
Model
using Navkar.ViewModels.Commands;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace Navkar.Models
{
public class MatrixCell
{
public string Text { get; set; }
public Color TextColor { get; set; }
public Color BackgroundColor { get; set; }
public int GridRow { get; set; }
public int GridColumn { get; set; }
public string StyleId { get; set; }
public int TabIndex { get; set; }
public string CommandParam { get; set; }
public ButtonCommand CommandAction { get; set; }
}
public class Matrix : ObservableCollection<MatrixCell> { }
public class MatrixModelObj
{
public Matrix MatrixCellItems { get; set; }
public MatrixModelObj()
{
MatrixCellItems = new Matrix();
}
}
}
ViewModel
using Navkar.Models;
using Navkar.ViewModels.Commands;
using Navkar.ViewModels.Helpers;
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
namespace Navkar.ViewModels
{
public class MatrixViewModel : INotifyPropertyChanged
{
private readonly App _App = Application.Current as App;
private readonly MatrixModelObj MatrixData;
private int _MatrixStyle;
public MatrixViewModel(MatrixModelObj _MatrixModel)
{
SetPropertyValues();
MatrixData = _MatrixModel;
_MatrixCell = 0;
GetMatrix();
_TabIndex = MatrixData.MatrixCellItems[_MatrixCell].TabIndex;
_ = GetNavkarPrayer("0");
}
private void SetPropertyValues()
{
_MatrixNumber = (_App.MatrixNumber < 1 || _App.MatrixNumber > 20) ? 1 : _App.MatrixNumber;
_MatrixStyle = (_App.MatrixStyle < 0 || _App.MatrixStyle > 3) ? 0 : _App.MatrixStyle;
_StyleCommandText = _MatrixStyle == 3 ? Helper.MatrixStyleText[0] :
Helper.MatrixStyleText[_MatrixStyle + 1];
MatrixNumeral = Helper.GetNumeral(_MatrixStyle, _MatrixNumber);
_App.MatrixStyle = _MatrixStyle;
_App.MatrixNumber = _MatrixNumber;
}
public Matrix Matrix
{
get => this.MatrixData.MatrixCellItems;
private set
{
this.MatrixData.MatrixCellItems = value;
}
}
#region Matrix
private void GetMatrix()
{
int navkarLine;
int matrixRow;
int matrixCol;
if (MatrixData.MatrixCellItems.Count > 0) { MatrixData.MatrixCellItems.Clear(); }
for (matrixRow = 0; matrixRow < 6; matrixRow++)
{
for (matrixCol = 0; matrixCol < 5; matrixCol++)
{
_TabIndex = (matrixRow * 5) + matrixCol + 1;
navkarLine = Helper.GetNavkarLine(_MatrixNumber, matrixRow, matrixCol);
MatrixData.MatrixCellItems.Add(new MatrixCell()
{
Text = Helper.GetNumeral(_MatrixStyle, navkarLine),
TextColor = Helper.PrayerColor[navkarLine],
BackgroundColor = Helper.BackgroundColor[_MatrixStyle],
GridRow = matrixRow,
GridColumn = matrixCol,
StyleId = navkarLine.ToString(),
TabIndex = _TabIndex,
CommandParam = navkarLine.ToString(),
CommandAction = PrayerCommand
});
}
}
}
#endregion
#region Matrix PrayerCommand
int _MatrixCell;
int _TabIndex;
bool _boolBinding;
public ButtonCommand PrayerCommand
{
get
{
_PrayerCommand = new ButtonCommand(
async(param) => await GetNavkarPrayer(param as string),
(param) => PrayerCommandCanExecute());
return _PrayerCommand;
}
private set
{
if (_PrayerCommand == value) return; _PrayerCommand = value;
}
}
private ButtonCommand _PrayerCommand;
public string NavkarPrayer
{
get { return _NavkarPrayer; }
private set
{
if (_NavkarPrayer == value) return; _NavkarPrayer = value;
}
}
private string _NavkarPrayer;
public Color PrayerColor
{
get { return _PrayerColor; }
private set
{
if (_PrayerColor == value) return; _PrayerColor = value;
}
}
private Color _PrayerColor;
private async Task GetNavkarPrayer(string param)
{
int prayerLine;
prayerLine = Convert.ToInt32(param);
await Task.Run(() => DateTime.Now.AddSeconds(1.0));
_NavkarPrayer = Helper.GetNavkarPrayer(_MatrixStyle, prayerLine);
_PrayerColor = Helper.PrayerColor[prayerLine];
RefreshProperties("Prayer");
//***Change canExecute state of clicked button and next button***
//***Change BackgroundColor of clicked button***
if (prayerLine > 0) ChangeMatrixCellView();
}
private void ChangeMatrixCellView()
{
//_boolBinding = true;
MatrixData.MatrixCellItems[_MatrixCell].BackgroundColor = Color.Accent;
Matrix.CollectionChanged += OnCollectionChanged;
_MatrixCell++;
//How to do the following?
//Disable clicked Button (Change canExecute=false)
//Enable Next Button (Change canExecute=true)
//Change background Color of the clicked Button
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
throw new NotImplementedException();
}
private bool PrayerCommandCanExecute(object param=null)
{
//***code below sets _canExecute = true for all buttons***
bool _canExecute;
_canExecute = (param == null) || (bool)param;
return _canExecute;
//***Code below sets _canExecute = true for first button only***
//bool _canExecute = false;
//if (MatrixData.MatrixCellItems[_MatrixCell].TabIndex == _TabIndex)
//{
// _canExecute = true;
//}
//if (_boolBinding == false) _MatrixCell += (_MatrixCell == 29) ? -29 : 1;
//return _canExecute;
}
#endregion
#region Matrix StyleCommand
public ButtonCommand StyleCommand
{
get
{
_StyleCommand = new ButtonCommand(async(param) => await GetMatrixStyle());
return _StyleCommand;
}
private set
{
if (_StyleCommand == value) return; _StyleCommand = value;
}
}
private ButtonCommand _StyleCommand;
public string StyleCommandText
{
get { return _StyleCommandText; }
private set
{
if (_StyleCommandText == value) return; _StyleCommandText = value;
}
}
private string _StyleCommandText;
private async Task GetMatrixStyle()
{
int styleIndex;
_MatrixStyle += (_MatrixStyle == 3) ? -3 : 1 ;
_App.MatrixStyle = _MatrixStyle;
styleIndex = (_MatrixStyle == 3) ? 0 : (_MatrixStyle + 1);
_StyleCommandText = Helper.MatrixStyleText[styleIndex];
RefreshProperties("Style");
MatrixNumeral = Helper.GetNumeral(_MatrixStyle, _MatrixNumber);
GetMatrix();
await Task.Run(() => DateTime.Now.AddSeconds(1.0));
_ = GetNavkarPrayer("0");
}
#endregion
#region Matrix Navigation ButtonCommands
public string MatrixNumeral { get; private set; }
public int MatrixNumber
{
get { return _MatrixNumber; }
private set
{
if (_MatrixNumber == value) return; _MatrixNumber = value;
}
}
private int _MatrixNumber;
public ButtonCommand ResetCommand
{
get
{
_ResetCommand = new ButtonCommand(
async(param) => await ChangeMatrix(param as string),
(param) => NavigationCommandCanExecute(param as string));
return _ResetCommand;
}
private set { if (_ResetCommand == value) return; _ResetCommand = value; }
}
private ButtonCommand _ResetCommand;
public ButtonCommand PrevCommand
{
get
{
_PrevCommand = new ButtonCommand(
async (param) => await ChangeMatrix(param as string),
param => NavigationCommandCanExecute(param as string));
return _PrevCommand;
}
private set { if (_PrevCommand == value) return; _PrevCommand = value; }
}
private ButtonCommand _PrevCommand;
public ButtonCommand NextCommand
{
get
{
_NextCommand = new ButtonCommand(
async (param) => await ChangeMatrix(param as string),
(param) => NavigationCommandCanExecute(param as string));
return _NextCommand;
}
private set { if (_NextCommand == value) return; _NextCommand = value; }
}
private ButtonCommand _NextCommand;
private async Task ChangeMatrix(string param)
{
switch (param)
{
case "0":
_MatrixNumber = 1;
break;
case "1":
_MatrixNumber += _MatrixNumber == 20 ? -19 : 1;
break;
case "-1":
_MatrixNumber -= _MatrixNumber > 1 ? 1 : 0;
break;
}
MatrixNumeral = Helper.GetNumeral(_MatrixStyle, _MatrixNumber);
RefreshProperties("Matrix");
RefreshCanExecutes();
_App.MatrixNumber = _MatrixNumber;
await Task.Run(() => DateTime.Now.AddSeconds(1.0));
GetMatrix();
_ = GetNavkarPrayer("0");
}
private bool NavigationCommandCanExecute(string param)
{
bool _canExecute = true;
int _matrixNumber = (_MatrixNumber + Convert.ToInt32(param));
switch (param)
{
case "0":
_canExecute = _matrixNumber > 1;
break;
case "1":
_canExecute = _matrixNumber <= 20;
break;
case "-1":
_canExecute = _matrixNumber >= 1;
break;
}
return _canExecute;
}
#endregion
#region PropertyChanged Methods and Handlers
private void RefreshProperties(string propertyGroup)
{
switch (propertyGroup)
{
case "Prayer":
OnPropertyChanged(nameof(NavkarPrayer));
OnPropertyChanged(nameof(PrayerColor));
break;
case "Matrix":
OnPropertyChanged(nameof(MatrixNumber));
OnPropertyChanged(nameof(MatrixNumeral));
break;
case "Style":
OnPropertyChanged(nameof(StyleCommandText));
break;
}
}
private void RefreshCanExecutes()
{
OnPropertyChanged(nameof(ResetCommand));
OnPropertyChanged(nameof(PrevCommand));
OnPropertyChanged(nameof(NextCommand));
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
ButtonCommand:ICommand
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace Navkar.ViewModels.Commands
{
public class ButtonCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public ButtonCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute ?? throw new NullReferenceException("execute");
_canExecute = canExecute;
}
public ButtonCommand(Action<object> execute) : this(execute, null)
{
}
public ButtonCommand(Action execute) : this(O => execute(), null)
{
}
public ButtonCommand(Action execute, Func<bool> canExecute) : this(O => execute(), O => canExecute())
{
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
}
I have tried many different options but unable to achieve the objective as mentioned above. I appreciate all suggestions. Thanks.