diff --git a/Aurora.gtk/Aurora.gtk.csproj b/Aurora.gtk/Aurora.gtk.csproj index f5ecc4a..08f7eb5 100644 --- a/Aurora.gtk/Aurora.gtk.csproj +++ b/Aurora.gtk/Aurora.gtk.csproj @@ -79,9 +79,6 @@ ..\packages\taglib-sharp-netstandard2.0.2.1.0\lib\netstandard2.0\taglib-sharp.dll - - ..\packages\Xamarin.Forms.DataGrid.3.1.0\lib\netstandard2.0\Xamarin.Forms.DataGrid.dll - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll diff --git a/Aurora.gtk/packages.config b/Aurora.gtk/packages.config index bae457d..ec8433b 100644 --- a/Aurora.gtk/packages.config +++ b/Aurora.gtk/packages.config @@ -31,6 +31,5 @@ - \ No newline at end of file diff --git a/Aurora/Aurora.csproj b/Aurora/Aurora.csproj index 9533cee..9a68ccd 100644 --- a/Aurora/Aurora.csproj +++ b/Aurora/Aurora.csproj @@ -10,7 +10,6 @@ - @@ -42,6 +41,7 @@ + @@ -55,9 +55,15 @@ + + + PreserveNewest + + + \ No newline at end of file diff --git a/Aurora/Design/Components/DataGrid/ColumnCollection.cs b/Aurora/Design/Components/DataGrid/ColumnCollection.cs new file mode 100644 index 0000000..1906bcb --- /dev/null +++ b/Aurora/Design/Components/DataGrid/ColumnCollection.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Aurora.Design.Components.DataGrid +{ + public sealed class ColumnCollection : List + { + } +} diff --git a/Aurora/Design/Components/DataGrid/DataGrid.xaml b/Aurora/Design/Components/DataGrid/DataGrid.xaml new file mode 100644 index 0000000..dc825b6 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGrid.xaml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Aurora/Design/Components/DataGrid/DataGrid.xaml.cs b/Aurora/Design/Components/DataGrid/DataGrid.xaml.cs new file mode 100644 index 0000000..aba83ea --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGrid.xaml.cs @@ -0,0 +1,650 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Reflection; +using System.Windows.Input; +using Xamarin.Forms; +using Aurora.Utils; + +namespace Aurora.Design.Components.DataGrid +{ + public partial class DataGrid : Grid + { + + public event EventHandler Refreshing; + public event EventHandler ItemSelected; + + #region Bindable properties + public static readonly BindableProperty ActiveRowColorProperty = + BindableProperty.Create(nameof(ActiveRowColor), typeof(Color), typeof(DataGrid), Color.FromRgb(128, 144, 160), + coerceValue: (b, v) => + { + if (!(b as DataGrid).SelectionEnabled) + throw new InvalidOperationException("Datagrid must be SelectionEnabled=true to set ActiveRowColor"); + return v; + }); + + public static readonly BindableProperty HeaderBackgroundProperty = + BindableProperty.Create(nameof(HeaderBackground), typeof(Color), typeof(DataGrid), Color.White, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (self._headerView != null && !self.HeaderBordersVisible) + self._headerView.BackgroundColor = (Color)n; + }); + + public static readonly BindableProperty BorderColorProperty = + BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(DataGrid), Color.Black, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (self.HeaderBordersVisible) + self._headerView.BackgroundColor = (Color)n; + + if (self.Columns != null && self.ItemsSource != null) + self.Reload(); + }); + + public static readonly BindableProperty RowsBackgroundColorPaletteProperty = + BindableProperty.Create(nameof(RowsBackgroundColorPalette), typeof(IColorProvider), typeof(DataGrid), new PaletteCollection { default(Color) }, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (self.Columns != null && self.ItemsSource != null) + self.Reload(); + }); + + public static readonly BindableProperty RowsTextColorPaletteProperty = + BindableProperty.Create(nameof(RowsTextColorPalette), typeof(IColorProvider), typeof(DataGrid), new PaletteCollection { Color.Black }, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (self.Columns != null && self.ItemsSource != null) + self.Reload(); + }); + + public static readonly BindableProperty ColumnsProperty = + BindableProperty.Create(nameof(Columns), typeof(ColumnCollection), typeof(DataGrid), + propertyChanged: (b, o, n) => (b as DataGrid).InitHeaderView(), + defaultValueCreator: b => { return new ColumnCollection(); } + ); + + public static readonly BindableProperty ItemsSourceProperty = + BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(DataGrid), null, + propertyChanged: (b, o, n) => + { + DataGrid self = b as DataGrid; + //ObservableCollection Tracking + if (o != null && o is INotifyCollectionChanged) + (o as INotifyCollectionChanged).CollectionChanged -= self.HandleItemsSourceCollectionChanged; + + if (n != null) + { + if (n is INotifyCollectionChanged) + (n as INotifyCollectionChanged).CollectionChanged += self.HandleItemsSourceCollectionChanged; + + self.InternalItems = new List(((IEnumerable)n).Cast()); + } + + if (self.SelectedItem != null && !self.InternalItems.Contains(self.SelectedItem)) + self.SelectedItem = null; + + if (self.NoDataView != null) + { + if (self.ItemsSource == null || self.InternalItems.Count() == 0) + self._noDataView.IsVisible = true; + else if (self._noDataView.IsVisible) + self._noDataView.IsVisible = false; + } + }); + + void HandleItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + InternalItems = new List(((IEnumerable)sender).Cast()); + if (SelectedItem != null && !InternalItems.Contains(SelectedItem)) + SelectedItem = null; + } + + public static readonly BindableProperty RowHeightProperty = + BindableProperty.Create(nameof(RowHeight), typeof(int), typeof(DataGrid), 40, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + self._listView.RowHeight = (int)n; + }); + + + public static readonly BindableProperty HeaderHeightProperty = + BindableProperty.Create(nameof(HeaderHeight), typeof(int), typeof(DataGrid), 40, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + self._headerView.HeightRequest = (int)n; + }); + + public static readonly BindableProperty IsSortableProperty = + BindableProperty.Create(nameof(IsSortable), typeof(bool), typeof(DataGrid), true); + + public static readonly BindableProperty FontSizeProperty = + BindableProperty.Create(nameof(FontSize), typeof(double), typeof(DataGrid), 13.0); + + public static readonly BindableProperty FontFamilyProperty = + BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(DataGrid), Font.Default.FontFamily); + + public static readonly BindableProperty SelectedItemProperty = + BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(DataGrid), null, BindingMode.TwoWay, + coerceValue: (b, v) => + { + var self = b as DataGrid; + if (!self.SelectionEnabled && v != null) + throw new InvalidOperationException("Datagrid must be SelectionEnabled=true to set SelectedItem"); + if (self.InternalItems != null && self.InternalItems.Contains(v)) + return v; + else + return null; + }, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (self._listView.SelectedItem != n) + self._listView.SelectedItem = n; + } + ); + + public static readonly BindableProperty SelectionEnabledProperty = + BindableProperty.Create(nameof(SelectionEnabled), typeof(bool), typeof(DataGrid), true, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (!self.SelectionEnabled && self.SelectedItem != null) + self.SelectedItem = null; + }); + + public static readonly BindableProperty PullToRefreshCommandProperty = + BindableProperty.Create(nameof(PullToRefreshCommand), typeof(ICommand), typeof(DataGrid), null, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (n == null) + { + self._listView.IsPullToRefreshEnabled = false; + self._listView.RefreshCommand = null; + } + else + { + self._listView.IsPullToRefreshEnabled = true; + self._listView.RefreshCommand = n as ICommand; + } + }); + + public static readonly BindableProperty IsRefreshingProperty = + BindableProperty.Create(nameof(IsRefreshing), typeof(bool), typeof(DataGrid), false, BindingMode.TwoWay, + propertyChanged: (b, o, n) => (b as DataGrid)._listView.IsRefreshing = (bool)n); + + public static readonly BindableProperty BorderThicknessProperty = + BindableProperty.Create(nameof(BorderThickness), typeof(Thickness), typeof(DataGrid), new Thickness(1), + propertyChanged: (b, o, n) => + { + (b as DataGrid)._headerView.ColumnSpacing = ((Thickness)n).HorizontalThickness / 2; + (b as DataGrid)._headerView.Padding = ((Thickness)n).HorizontalThickness / 2; + }); + + public static readonly BindableProperty HeaderBordersVisibleProperty = + BindableProperty.Create(nameof(HeaderBordersVisible), typeof(bool), typeof(DataGrid), true, + propertyChanged: (b, o, n) => (b as DataGrid)._headerView.BackgroundColor = (bool)n ? (b as DataGrid).BorderColor : (b as DataGrid).HeaderBackground); + + public static readonly BindableProperty SortedColumnIndexProperty = + BindableProperty.Create(nameof(SortedColumnIndex), typeof(SortData), typeof(DataGrid), null, BindingMode.TwoWay, + validateValue: (b, v) => + { + var self = b as DataGrid; + var sData = (SortData)v; + + return + sData == null || //setted to null + self.Columns == null || // Columns binded but not setted + self.Columns.Count == 0 || //columns not setted yet + (sData.Index < self.Columns.Count && self.Columns.ElementAt(sData.Index).SortingEnabled); + }, + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if (o != n) + self.SortItems((SortData)n); + }); + + + public static readonly BindableProperty HeaderLabelStyleProperty = + BindableProperty.Create(nameof(HeaderLabelStyle), typeof(Style), typeof(DataGrid)); + + public static readonly BindableProperty AscendingIconProperty = + BindableProperty.Create(nameof(AscendingIcon), typeof(ImageSource), typeof(DataGrid), ImageSource.FromResource("Xamarin.Forms.DataGrid.up.png", typeof(DataGrid).GetTypeInfo().Assembly)); + + public static readonly BindableProperty DescendingIconProperty = + BindableProperty.Create(nameof(DescendingIcon), typeof(ImageSource), typeof(DataGrid), ImageSource.FromResource("Xamarin.Forms.DataGrid.down.png", typeof(DataGrid).GetTypeInfo().Assembly)); + + public static readonly BindableProperty DescendingIconStyleProperty = + BindableProperty.Create(nameof(DescendingIconStyle), typeof(Style), typeof(DataGrid), null, + + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + var style = (n as Style).Setters.FirstOrDefault(x => x.Property == Image.SourceProperty); + if (style != null) + { + if (style.Value is string vs) + self.DescendingIcon = ImageSource.FromFile(vs); + else + self.DescendingIcon = (ImageSource)style.Value; + } + }); + + public static readonly BindableProperty AscendingIconStyleProperty = + BindableProperty.Create(nameof(AscendingIconStyle), typeof(Style), typeof(DataGrid), null, + coerceValue: (b, v) => + { + var self = b as DataGrid; + + return v; + }, + + propertyChanged: (b, o, n) => + { + var self = b as DataGrid; + if ((n as Style).Setters.Any(x => x.Property == Image.SourceProperty)) + { + var style = (n as Style).Setters.FirstOrDefault(x => x.Property == Image.SourceProperty); + if (style != null) + { + if (style.Value is string vs) + self.AscendingIcon = ImageSource.FromFile(vs); + else + self.AscendingIcon = (ImageSource)style.Value; + } + } + }); + + public static readonly BindableProperty NoDataViewProperty = + BindableProperty.Create(nameof(NoDataView), typeof(View), typeof(DataGrid), + propertyChanged: (b, o, n) => + { + if (o != n) + (b as DataGrid)._noDataView.Content = n as View; + }); + #endregion + + #region Properties + public Color ActiveRowColor + { + get { return (Color)GetValue(ActiveRowColorProperty); } + set { SetValue(ActiveRowColorProperty, value); } + } + + public Color HeaderBackground + { + get { return (Color)GetValue(HeaderBackgroundProperty); } + set { SetValue(HeaderBackgroundProperty, value); } + } + + [Obsolete("Please use HeaderLabelStyle", true)] + public Color HeaderTextColor + { + get; set; + } + + public Color BorderColor + { + get { return (Color)GetValue(BorderColorProperty); } + set { SetValue(BorderColorProperty, value); } + } + + public IColorProvider RowsBackgroundColorPalette + { + get { return (IColorProvider)GetValue(RowsBackgroundColorPaletteProperty); } + set { SetValue(RowsBackgroundColorPaletteProperty, value); } + } + + public IColorProvider RowsTextColorPalette + { + get { return (IColorProvider)GetValue(RowsTextColorPaletteProperty); } + set { SetValue(RowsTextColorPaletteProperty, value); } + } + + public IEnumerable ItemsSource + { + get { return (IEnumerable)GetValue(ItemsSourceProperty); } + set { SetValue(ItemsSourceProperty, value); } + } + + IList _internalItems; + + internal IList InternalItems + { + get { return _internalItems; } + set + { + _internalItems = value; + + if (IsSortable && SortedColumnIndex != null) + SortItems(SortedColumnIndex); + else + _listView.ItemsSource = _internalItems; + } + } + + public ColumnCollection Columns + { + get { return (ColumnCollection)GetValue(ColumnsProperty); } + set { SetValue(ColumnsProperty, value); } + } + + public double FontSize + { + get { return (double)GetValue(FontSizeProperty); } + set { SetValue(FontSizeProperty, value); } + } + + [Obsolete("Please use HeaderLabelStyle", true)] + public double HeaderFontSize + { + get; set; + } + + public string FontFamily + { + get { return (string)GetValue(FontFamilyProperty); } + set { SetValue(FontFamilyProperty, value); } + } + + public int RowHeight + { + get { return (int)GetValue(RowHeightProperty); } + set { SetValue(RowHeightProperty, value); } + } + + public int HeaderHeight + { + get { return (int)GetValue(HeaderHeightProperty); } + set { SetValue(HeaderHeightProperty, value); } + } + + public bool IsSortable + { + get { return (bool)GetValue(IsSortableProperty); } + set { SetValue(IsSortableProperty, value); } + } + + public bool SelectionEnabled + { + get { return (bool)GetValue(SelectionEnabledProperty); } + set { SetValue(SelectionEnabledProperty, value); } + } + + public object SelectedItem + { + get { return GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + + public ICommand PullToRefreshCommand + { + get { return (ICommand)GetValue(PullToRefreshCommandProperty); } + set { SetValue(PullToRefreshCommandProperty, value); } + } + + public bool IsRefreshing + { + get { return (bool)GetValue(IsRefreshingProperty); } + set { SetValue(IsRefreshingProperty, value); } + } + + public Thickness BorderThickness + { + get { return (Thickness)GetValue(BorderThicknessProperty); } + set { SetValue(BorderThicknessProperty, value); } + } + + public bool HeaderBordersVisible + { + get { return (bool)GetValue(HeaderBordersVisibleProperty); } + set { SetValue(HeaderBordersVisibleProperty, value); } + } + + public SortData SortedColumnIndex + { + get { return (SortData)GetValue(SortedColumnIndexProperty); } + set { SetValue(SortedColumnIndexProperty, value); } + } + + public Style HeaderLabelStyle + { + get { return (Style)GetValue(HeaderLabelStyleProperty); } + set { SetValue(HeaderLabelStyleProperty, value); } + } + + public ImageSource AscendingIcon + { + get { return (ImageSource)GetValue(AscendingIconProperty); } + set { SetValue(AscendingIconProperty, value); } + } + + public ImageSource DescendingIcon + { + get { return (ImageSource)GetValue(DescendingIconProperty); } + set { SetValue(DescendingIconProperty, value); } + } + + public Style AscendingIconStyle + { + get { return (Style)GetValue(AscendingIconStyleProperty); } + set { SetValue(AscendingIconStyleProperty, value); } + } + + public Style DescendingIconStyle + { + get { return (Style)GetValue(DescendingIconStyleProperty); } + set { SetValue(DescendingIconStyleProperty, value); } + } + + public View NoDataView + { + get { return (View)GetValue(NoDataViewProperty); } + set { SetValue(NoDataViewProperty, value); } + } + #endregion + + #region Fields + Dictionary _sortingOrders; + ListView _listView; + #endregion + + #region ctor + + public DataGrid() : this(ListViewCachingStrategy.RecycleElement) + { + } + + public DataGrid(ListViewCachingStrategy cachingStrategy) + { + InitializeComponent(); + BackgroundColor = Color.Transparent; + + _sortingOrders = new Dictionary(); + + _listView = new ListView(cachingStrategy) + { + BackgroundColor = Color.FromHex("#222222"), + ItemTemplate = new DataGridRowTemplateSelector(), + SeparatorVisibility = SeparatorVisibility.Default, + }; + + _listView.ItemSelected += (s, e) => + { + if (SelectionEnabled) + SelectedItem = _listView.SelectedItem; + else + _listView.SelectedItem = null; + + ItemSelected?.Invoke(this, e); + }; + + _listView.Refreshing += (s, e) => + { + Refreshing?.Invoke(this, e); + }; + + _listView.SetBinding(ListView.RowHeightProperty, new Binding("RowHeight", source: this)); + Grid.SetRow(_listView, 1); + Children.Add(_listView); + } + #endregion + + #region UI Methods + protected override void OnParentSet() + { + base.OnParentSet(); + InitHeaderView(); + } + + protected override void OnBindingContextChanged() + { + base.OnBindingContextChanged(); + SetColumnsBindingContext(); + } + + private void Reload() + { + InternalItems = new List(_internalItems); + } + #endregion + + #region Header Creation Methods + + private View GetHeaderViewForColumn(DataGridColumn column) + { + column.HeaderLabel.Style = column.HeaderLabelStyle ?? this.HeaderLabelStyle ?? (Style)_headerView.Resources["HeaderDefaultStyle"]; + + Grid grid = new Grid + { + ColumnSpacing = 0, + }; + + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }); + + if (IsSortable) + { + column.SortingIcon.Style = (Style)_headerView.Resources["ImageStyleBase"]; + + grid.Children.Add(column.SortingIcon); + Grid.SetColumn(column.SortingIcon, 1); + + TapGestureRecognizer tgr = new TapGestureRecognizer(); + tgr.Tapped += (s, e) => + { + int index = Columns.IndexOf(column); + SortingOrder order = _sortingOrders[index] == SortingOrder.Ascendant ? SortingOrder.Descendant : SortingOrder.Ascendant; + + if (Columns.ElementAt(index).SortingEnabled) + SortedColumnIndex = new SortData(index, order); + }; + grid.GestureRecognizers.Add(tgr); + } + + grid.Children.Add(column.HeaderLabel); + + return grid; + } + + private void InitHeaderView() + { + SetColumnsBindingContext(); + _headerView.Children.Clear(); + _headerView.ColumnDefinitions.Clear(); + _sortingOrders.Clear(); + + _headerView.Padding = new Thickness(BorderThickness.Left, BorderThickness.Top, BorderThickness.Right, 0); + _headerView.ColumnSpacing = BorderThickness.HorizontalThickness / 2; + + if (Columns != null) + { + foreach (var col in Columns) + { + _headerView.ColumnDefinitions.Add(new ColumnDefinition { Width = col.Width }); + + var cell = GetHeaderViewForColumn(col); + + _headerView.Children.Add(cell); + Grid.SetColumn(cell, Columns.IndexOf(col)); + + _sortingOrders.Add(Columns.IndexOf(col), SortingOrder.None); + } + } + } + + private void SetColumnsBindingContext() + { + if (Columns != null) + foreach (var c in Columns) + c.BindingContext = BindingContext; + } + #endregion + + #region Sorting methods + internal void SortItems(SortData sData) + { + if (InternalItems == null || sData.Index >= Columns.Count || !Columns[sData.Index].SortingEnabled) + return; + + var items = InternalItems; + var column = Columns[sData.Index]; + SortingOrder order = sData.Order; + + if (!IsSortable) + throw new InvalidOperationException("This DataGrid is not sortable"); + else if (column.PropertyName == null) + throw new InvalidOperationException("Please set the PropertyName property of Column"); + + //Sort + if (order == SortingOrder.Descendant) + items = items.OrderByDescending(x => ReflectionUtils.GetValueByPath(x, column.PropertyName)).ToList(); + else + items = items.OrderBy(x => ReflectionUtils.GetValueByPath(x, column.PropertyName)).ToList(); + + column.SortingIcon.Style = (order == SortingOrder.Descendant) ? + AscendingIconStyle ?? (Style)_headerView.Resources["DescendingIconStyle"] : + DescendingIconStyle ?? (Style)_headerView.Resources["AscendingIconStyle"]; + + //Support DescendingIcon property (if setted) + if (!column.SortingIcon.Style.Setters.Any(x => x.Property == Image.SourceProperty)) + { + if (order == SortingOrder.Descendant && DescendingIconProperty.DefaultValue != DescendingIcon) + column.SortingIcon.Source = DescendingIcon; + if (order == SortingOrder.Ascendant && AscendingIconProperty.DefaultValue != AscendingIcon) + column.SortingIcon.Source = AscendingIcon; + } + + for (int i = 0; i < Columns.Count; i++) + { + if (i != sData.Index) + { + if (Columns[i].SortingIcon.Style != null) + Columns[i].SortingIcon.Style = null; + if (Columns[i].SortingIcon.Source != null) + Columns[i].SortingIcon.Source = null; + _sortingOrders[i] = SortingOrder.None; + } + } + + _internalItems = items; + + _sortingOrders[sData.Index] = order; + SortedColumnIndex = sData; + + _listView.ItemsSource = _internalItems; + } + #endregion + } +} diff --git a/Aurora/Design/Components/DataGrid/DataGridColumn.cs b/Aurora/Design/Components/DataGrid/DataGridColumn.cs new file mode 100644 index 0000000..31b0826 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGridColumn.cs @@ -0,0 +1,129 @@ +using System; +using Xamarin.Forms; + +namespace Aurora.Design.Components.DataGrid +{ + public class DataGridColumn : BindableObject, IDefinition + { + #region bindable properties + public static readonly BindableProperty WidthProperty = + BindableProperty.Create(nameof(Width), typeof(GridLength), typeof(DataGridColumn), new GridLength(1, GridUnitType.Star), + propertyChanged: (b, o, n) => { if (o != n) (b as DataGridColumn).OnSizeChanged(); }); + + public static readonly BindableProperty TitleProperty = + BindableProperty.Create(nameof(Title), typeof(string), typeof(DataGridColumn), string.Empty, + propertyChanged: (b, o, n) => (b as DataGridColumn).HeaderLabel.Text = (string)n); + + public static readonly BindableProperty FormattedTitleProperty = + BindableProperty.Create(nameof(FormattedTitle), typeof(FormattedString), typeof(DataGridColumn), + propertyChanged: (b, o, n) => (b as DataGridColumn).HeaderLabel.FormattedText = (FormattedString)n); + + public static readonly BindableProperty PropertyNameProperty = + BindableProperty.Create(nameof(PropertyName), typeof(string), typeof(DataGridColumn), null); + + public static readonly BindableProperty StringFormatProperty = + BindableProperty.Create(nameof(StringFormat), typeof(string), typeof(DataGridColumn), null); + + public static readonly BindableProperty CellTemplateProperty = + BindableProperty.Create(nameof(CellTemplate), typeof(DataTemplate), typeof(DataGridColumn), null); + + public static readonly BindableProperty HorizontalContentAlignmentProperty = + BindableProperty.Create(nameof(HorizontalContentAlignment), typeof(LayoutOptions), typeof(DataGridColumn), LayoutOptions.Center); + + public static readonly BindableProperty VerticalContentAlignmentProperty = + BindableProperty.Create(nameof(VerticalContentAlignment), typeof(LayoutOptions), typeof(DataGridColumn), LayoutOptions.Center); + + public static readonly BindableProperty SortingEnabledProperty = + BindableProperty.Create(nameof(SortingEnabled), typeof(bool), typeof(DataGridColumn), true); + + public static readonly BindableProperty HeaderLabelStyleProperty = + BindableProperty.Create(nameof(HeaderLabelStyle), typeof(Style), typeof(DataGridColumn), + propertyChanged: (b, o, n) => + { + if ((b as DataGridColumn).HeaderLabel != null && (o != n)) + (b as DataGridColumn).HeaderLabel.Style = n as Style; + }); + + #endregion + + #region properties + + public GridLength Width + { + get { return (GridLength)GetValue(WidthProperty); } + set { SetValue(WidthProperty, value); } + } + + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public FormattedString FormattedTitle + { + get { return (string)GetValue(FormattedTitleProperty); } + set { SetValue(FormattedTitleProperty, value); } + } + public string PropertyName + { + get { return (string)GetValue(PropertyNameProperty); } + set { SetValue(PropertyNameProperty, value); } + } + + public string StringFormat + { + get { return (string)GetValue(StringFormatProperty); } + set { SetValue(StringFormatProperty, value); } + } + + public DataTemplate CellTemplate + { + get { return (DataTemplate)GetValue(CellTemplateProperty); } + set { SetValue(CellTemplateProperty, value); } + } + + internal Image SortingIcon { get; set; } + internal Label HeaderLabel { get; set; } + + public LayoutOptions HorizontalContentAlignment + { + get { return (LayoutOptions)GetValue(HorizontalContentAlignmentProperty); } + set { SetValue(HorizontalContentAlignmentProperty, value); } + } + + public LayoutOptions VerticalContentAlignment + { + get { return (LayoutOptions)GetValue(VerticalContentAlignmentProperty); } + set { SetValue(VerticalContentAlignmentProperty, value); } + } + + public bool SortingEnabled + { + get { return (bool)GetValue(SortingEnabledProperty); } + set { SetValue(SortingEnabledProperty, value); } + } + + public Style HeaderLabelStyle + { + get { return (Style)GetValue(HeaderLabelStyleProperty); } + set { SetValue(HeaderLabelStyleProperty, value); } + } + + #endregion + + public event EventHandler SizeChanged; + + public DataGridColumn() + { + HeaderLabel = new Label(); + SortingIcon = new Image(); + } + + void OnSizeChanged() + { + SizeChanged?.Invoke(this, EventArgs.Empty); + } + } + +} diff --git a/Aurora/Design/Components/DataGrid/DataGridComponent.cs b/Aurora/Design/Components/DataGrid/DataGridComponent.cs new file mode 100644 index 0000000..fc54ac5 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGridComponent.cs @@ -0,0 +1,12 @@ +namespace Aurora.Design.Components.DataGrid +{ + public static class DataGridComponent + { + /// + /// You should call this method for loding the assembly + /// + public static void Init() + { + } + } +} diff --git a/Aurora/Design/Components/DataGrid/DataGridRowTemplateSelector.cs b/Aurora/Design/Components/DataGrid/DataGridRowTemplateSelector.cs new file mode 100644 index 0000000..f83e06c --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGridRowTemplateSelector.cs @@ -0,0 +1,28 @@ +using Xamarin.Forms; +namespace Aurora.Design.Components.DataGrid +{ + internal class DataGridRowTemplateSelector : DataTemplateSelector + { + private static DataTemplate _dataGridRowTemplate; + + public DataGridRowTemplateSelector() + { + _dataGridRowTemplate = new DataTemplate(typeof(DataGridViewCell)); + } + + protected override DataTemplate OnSelectTemplate(object item, BindableObject container) + { + var listView = container as ListView; + var dataGrid = listView.Parent as DataGrid; + var items = dataGrid.InternalItems; + + _dataGridRowTemplate.SetValue(DataGridViewCell.DataGridProperty, dataGrid); + _dataGridRowTemplate.SetValue(DataGridViewCell.RowContextProperty, item); + + if (items != null) + _dataGridRowTemplate.SetValue(DataGridViewCell.IndexProperty, items.IndexOf(item)); + + return _dataGridRowTemplate; + } + } +} diff --git a/Aurora/Design/Components/DataGrid/DataGridViewCell.cs b/Aurora/Design/Components/DataGrid/DataGridViewCell.cs new file mode 100644 index 0000000..f8208f5 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/DataGridViewCell.cs @@ -0,0 +1,152 @@ +using Xamarin.Forms; +namespace Aurora.Design.Components.DataGrid +{ + internal sealed class DataGridViewCell : ViewCell + { + #region Fields + Grid _mainLayout; + Color _bgColor; + Color _textColor; + bool _hasSelected; + #endregion + + #region properties + public DataGrid DataGrid + { + get { return (DataGrid)GetValue(DataGridProperty); } + set { SetValue(DataGridProperty, value); } + } + + public int Index + { + get { return (int)GetValue(IndexProperty); } + set { SetValue(IndexProperty, value); } + } + + public object RowContext + { + get { return GetValue(RowContextProperty); } + set { SetValue(RowContextProperty, value); } + } + #endregion + + #region Bindable Properties + public static readonly BindableProperty DataGridProperty = + BindableProperty.Create(nameof(DataGrid), typeof(DataGrid), typeof(DataGridViewCell), null, + propertyChanged: (b, o, n) => (b as DataGridViewCell).CreateView()); + + public static readonly BindableProperty IndexProperty = + BindableProperty.Create(nameof(Index), typeof(int), typeof(DataGridViewCell), 0, + propertyChanged: (b, o, n) => (b as DataGridViewCell).UpdateBackgroundColor()); + + public static readonly BindableProperty RowContextProperty = + BindableProperty.Create(nameof(RowContext), typeof(object), typeof(DataGridViewCell), + propertyChanged: (b, o, n) => (b as DataGridViewCell).UpdateBackgroundColor()); + #endregion + + #region Methods + private void CreateView() + { + UpdateBackgroundColor(); + + _mainLayout = new Grid() + { + BackgroundColor = DataGrid.BorderColor, + RowSpacing = 0, + ColumnSpacing = DataGrid.BorderThickness.HorizontalThickness / 2, + Padding = new Thickness(DataGrid.BorderThickness.HorizontalThickness / 2, + DataGrid.BorderThickness.VerticalThickness / 2), + }; + + foreach (var col in DataGrid.Columns) + { + _mainLayout.ColumnDefinitions.Add(new ColumnDefinition() { Width = col.Width }); + View cell; + + if (col.CellTemplate != null) + { + cell = new ContentView() { Content = col.CellTemplate.CreateContent() as View }; + if (col.PropertyName != null) + { + cell.SetBinding(BindingContextProperty, + new Binding(col.PropertyName, source: RowContext)); + } + } + else + { + var text = new Label + { + TextColor = _textColor, + HorizontalOptions = col.HorizontalContentAlignment, + VerticalOptions = col.VerticalContentAlignment, + LineBreakMode = LineBreakMode.WordWrap, + }; + text.SetBinding(Label.TextProperty, new Binding(col.PropertyName, BindingMode.Default, stringFormat: col.StringFormat)); + text.SetBinding(Label.FontSizeProperty, new Binding(DataGrid.FontSizeProperty.PropertyName, BindingMode.Default, source: DataGrid)); + text.SetBinding(Label.FontFamilyProperty, new Binding(DataGrid.FontFamilyProperty.PropertyName, BindingMode.Default, source: DataGrid)); + + cell = new ContentView + { + Padding = 0, + BackgroundColor = _bgColor, + Content = text, + }; + } + + _mainLayout.Children.Add(cell); + Grid.SetColumn(cell, DataGrid.Columns.IndexOf(col)); + } + + View = _mainLayout; + } + + private void UpdateBackgroundColor() + { + _hasSelected = DataGrid.SelectedItem == RowContext; + int actualIndex = DataGrid?.InternalItems?.IndexOf(BindingContext) ?? -1; + if (actualIndex > -1) + { + _bgColor = (DataGrid.SelectionEnabled && DataGrid.SelectedItem != null && DataGrid.SelectedItem == RowContext) ? + DataGrid.ActiveRowColor : DataGrid.RowsBackgroundColorPalette.GetColor(Index, BindingContext); + _textColor = DataGrid.RowsTextColorPalette.GetColor(actualIndex, BindingContext); + + ChangeColor(_bgColor); + } + } + + private void ChangeColor(Color color) + { + foreach (var v in _mainLayout.Children) + { + v.BackgroundColor = color; + var contentView = v as ContentView; + if (contentView?.Content is Label) + ((Label)contentView.Content).TextColor = _textColor; + } + } + + protected override void OnBindingContextChanged() + { + base.OnBindingContextChanged(); + UpdateBackgroundColor(); + } + + protected override void OnParentSet() + { + base.OnParentSet(); + if (Parent != null) + DataGrid.ItemSelected += DataGrid_ItemSelected; + else + DataGrid.ItemSelected -= DataGrid_ItemSelected; + } + + private void DataGrid_ItemSelected(object sender, SelectedItemChangedEventArgs e) + { + if (DataGrid.SelectionEnabled && (e.SelectedItem == RowContext || _hasSelected)) + { + UpdateBackgroundColor(); + } + } + #endregion + } +} diff --git a/Aurora/Design/Components/DataGrid/IColorProvider.cs b/Aurora/Design/Components/DataGrid/IColorProvider.cs new file mode 100644 index 0000000..3cc9825 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/IColorProvider.cs @@ -0,0 +1,8 @@ +using Xamarin.Forms; +namespace Aurora.Design.Components.DataGrid +{ + public interface IColorProvider + { + Color GetColor(int rowIndex, object item); + } +} diff --git a/Aurora/Design/Components/DataGrid/PaletteCollection.cs b/Aurora/Design/Components/DataGrid/PaletteCollection.cs new file mode 100644 index 0000000..770c8d9 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/PaletteCollection.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; +using Xamarin.Forms; + +namespace Aurora.Design.Components.DataGrid +{ + public sealed class PaletteCollection : List, IColorProvider + { + public Color GetColor(int rowIndex, object item) + { + if (Count > 0) + return this.ElementAt(rowIndex % Count); + else + return default(Color); + } + } +} diff --git a/Aurora/Design/Components/DataGrid/SortData.cs b/Aurora/Design/Components/DataGrid/SortData.cs new file mode 100644 index 0000000..1552d09 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/SortData.cs @@ -0,0 +1,51 @@ +using System; +using Xamarin.Forms; + +namespace Aurora.Design.Components.DataGrid +{ + + [TypeConverter(typeof(SortDataTypeConverter))] + public class SortData + { + + #region ctor + public SortData() + { + } + + public SortData(int index, SortingOrder order) + { + Index = index; + Order = order; + } + + #endregion + + #region Properties + public SortingOrder Order { get; set; } + + public int Index { get; set; } + #endregion + + public static implicit operator SortData(int index) + { + return new SortData + { + Index = Math.Abs(index), + Order = index < 0 ? SortingOrder.Descendant : SortingOrder.Ascendant + }; + } + + public override bool Equals(object obj) + { + if (obj is SortData) + { + SortData other = obj as SortData; + return other.Index == Index && other.Order == Order; + } + else + return false; + } + + } +} diff --git a/Aurora/Design/Components/DataGrid/SortDataTypeConverter.cs b/Aurora/Design/Components/DataGrid/SortDataTypeConverter.cs new file mode 100644 index 0000000..baf0d0d --- /dev/null +++ b/Aurora/Design/Components/DataGrid/SortDataTypeConverter.cs @@ -0,0 +1,24 @@ +using System; +using Xamarin.Forms; + +namespace Aurora.Design.Components.DataGrid +{ + public class SortDataTypeConverter : TypeConverter + { + + public override bool CanConvertFrom(Type sourceType) + { + return base.CanConvertFrom(sourceType); + } + + public override object ConvertFromInvariantString(string value) + { + int index = 0; + + if (int.TryParse(value, out index)) + return (SortData)index; + else + return null; + } + } +} diff --git a/Aurora/Design/Components/DataGrid/SortingOrder.cs b/Aurora/Design/Components/DataGrid/SortingOrder.cs new file mode 100644 index 0000000..78c9520 --- /dev/null +++ b/Aurora/Design/Components/DataGrid/SortingOrder.cs @@ -0,0 +1,9 @@ +namespace Aurora.Design.Components.DataGrid +{ + public enum SortingOrder + { + None = 0, + Ascendant = 1, + Descendant = 2, + } +} diff --git a/Aurora/Design/Components/DataGrid/down.png b/Aurora/Design/Components/DataGrid/down.png new file mode 100644 index 0000000..3a359e8 Binary files /dev/null and b/Aurora/Design/Components/DataGrid/down.png differ diff --git a/Aurora/Design/Components/DataGrid/up.png b/Aurora/Design/Components/DataGrid/up.png new file mode 100644 index 0000000..bd675ae Binary files /dev/null and b/Aurora/Design/Components/DataGrid/up.png differ diff --git a/Aurora/Design/Components/Library/Library.css b/Aurora/Design/Components/Library/Library.css index 2c71ccc..a5bbdb6 100644 --- a/Aurora/Design/Components/Library/Library.css +++ b/Aurora/Design/Components/Library/Library.css @@ -1,3 +1,3 @@ -#LibraryDataGrid { - background-color: black +Label { + margin-left: 25; } diff --git a/Aurora/Design/Components/Library/Library.xaml b/Aurora/Design/Components/Library/Library.xaml index 4cde537..805c643 100644 --- a/Aurora/Design/Components/Library/Library.xaml +++ b/Aurora/Design/Components/Library/Library.xaml @@ -2,49 +2,92 @@ + + + + RowHeight="30" + BorderColor="#3a3a3a" + BorderThickness="0" + HeaderFontSize="14" + HeaderHeight="45" + HeaderTextColor="White" + HeaderBackground="#222222"> - - - 15 - 13 - 20 - - + + + + + + + + + Width="2*"> + + + + + + + Width="0.95*"> + + + + + - + Width="1*"> + + + + + + + + White + + - #F2F2F2 - #FFFFFF + Transparent diff --git a/Aurora/Design/Components/Library/Library.xaml.cs b/Aurora/Design/Components/Library/Library.xaml.cs index f093141..dde5a28 100644 --- a/Aurora/Design/Components/Library/Library.xaml.cs +++ b/Aurora/Design/Components/Library/Library.xaml.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Xamarin.Forms; -using Xamarin.Forms.DataGrid; using Aurora.Models.Media; namespace Aurora.Design.Components.Library diff --git a/Aurora/Design/Components/NavigationMenu/NavigationMenu.css b/Aurora/Design/Components/NavigationMenu/NavigationMenu.css index c75df32..a320db9 100644 --- a/Aurora/Design/Components/NavigationMenu/NavigationMenu.css +++ b/Aurora/Design/Components/NavigationMenu/NavigationMenu.css @@ -14,7 +14,6 @@ ListView { #GroupCell Label { color: lightgray; - background-color: #232323; font-size: 12; font-family: Courier New, Courier, monospace; font-style: italic; @@ -23,7 +22,6 @@ ListView { #ItemCell label { color: white; - background-color: #232323; font-size: 15; font-family: Courier New, Courier, monospace; font-style: normal; diff --git a/Aurora/Design/Components/NavigationMenu/NavigationMenu.xaml b/Aurora/Design/Components/NavigationMenu/NavigationMenu.xaml index c401b23..de69dae 100644 --- a/Aurora/Design/Components/NavigationMenu/NavigationMenu.xaml +++ b/Aurora/Design/Components/NavigationMenu/NavigationMenu.xaml @@ -11,7 +11,6 @@