using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Threading.Tasks; using Syncfusion.Data.Extensions; using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Forms.Xaml; using XF.Material.Forms; namespace InABox.Mobile { public class ToolGridLayoutChangedEventArgs : EventArgs { public int RowCount { get; private set; } public int ColCount { get; private set; } public ToolGridLayoutChangedEventArgs(int rowcount, int colcount) { RowCount = rowcount; ColCount = colcount; } } public delegate void ToolGridLayoutChangedEvent(object sender, ToolGridLayoutChangedEventArgs args); public class ToolGridViewModel : BindableObject { public CoreObservableCollection Items { get; private set; } public CoreObservableCollection VisibleItems { get; private set; } public int Columns { get; set; } private bool _disableUpdate = false; public void BeginUpdate() => _disableUpdate = true; public void EndUpdate() { _disableUpdate = false; Refresh(); } public ToolGridViewModel() { Columns = Device.Idiom == TargetIdiom.Tablet ? 6 : 4; VisibleItems = new CoreObservableCollection(); Items = new CoreObservableCollection(); Items.CollectionChanged += (sender, args) => { if (args.OldItems != null) { foreach (var item in args.OldItems?.OfType()) item.VisibleChanged -= ItemChanged; } if (args.NewItems != null) { foreach (var item in args.NewItems.OfType()) item.VisibleChanged += ItemChanged; } Refresh(); }; } private void ItemChanged(object sender, EventArgs e) => Refresh(); public void Refresh() { if (_disableUpdate) return; VisibleItems.Clear(); VisibleItems.AddRange(Items.Where(x => x.IsVisible).ToArray()); int iRow = 0; int iCol = 0; bool bDirty = false; foreach (var item in VisibleItems) { bDirty = iRow != item.Row || iCol != item.Column; item.Row = iRow; item.Column = iCol; iCol++; if (iCol == Columns) { iRow++; iCol = 0; } } if (bDirty) LayoutChanged?.Invoke(this,new ToolGridLayoutChangedEventArgs(iRow,Columns)); } public Color BackgroundColor { get; set; } public event ToolGridLayoutChangedEvent LayoutChanged; } [XamlCompilation(XamlCompilationOptions.Compile)] public partial class MobileToolGrid { public IList Items => _viewModel.Items; public event EventHandler BeforeTap; public event EventHandler AfterTap; private ToolGridViewModel _viewModel = new(); public void BeginUpdate() { _viewModel?.BeginUpdate(); } public void EndUpdate() { _viewModel.EndUpdate(); } public MobileToolGrid() { _viewModel.LayoutChanged += _viewModel_OnLayoutChanged; BeginUpdate(); InitializeComponent(); EndUpdate(); } public void Refresh() { _viewModel.Refresh(); } private readonly BindableProperty BorderColorProperty = BindableProperty.Create( nameof(BorderColor), typeof(Color), typeof(MobileToolGrid), Material.Color.SecondaryVariant ); public Color BorderColor { get => (Color)GetValue(BorderColorProperty); set => SetValue(BorderColorProperty, value); } private bool debounce = false; private async void ToolItem_Tapped(object sender, EventArgs e) { if (debounce) return; debounce = true; BeforeTap?.Invoke(this, EventArgs.Empty); try { if ((sender is Frame frame) && (frame.BindingContext is MobileToolItem item) && item.IsEnabled) { frame.Scale = 0.5; await frame.ScaleTo(1, 150); item.DoTap(); } } catch (Exception err) { MobileLogging.Log(err,"MobileToolGrid"); } AfterTap?.Invoke(this, EventArgs.Empty); debounce = false; } private void _viewModel_OnLayoutChanged(object sender, ToolGridLayoutChangedEventArgs args) { _flexgrid.RowDefinitions.Clear(); for (int i=0; i< args.RowCount; i++) _flexgrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto}); _flexgrid.ColumnDefinitions.Clear(); for (int i=0; i< args.ColCount; i++) _flexgrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star}); BindableLayout.SetItemsSource(_flexgrid, null); BindableLayout.SetItemsSource(_flexgrid, _viewModel.VisibleItems); } } }