Serious styling. Added data grid src for future modifications in performance

This commit is contained in:
watsonb8 2019-12-01 15:26:13 -08:00
parent 061adabff8
commit b60f2a4f47
26 changed files with 1332 additions and 34 deletions

View File

@ -79,9 +79,6 @@
<Reference Include="taglib-sharp">
<HintPath>..\packages\taglib-sharp-netstandard2.0.2.1.0\lib\netstandard2.0\taglib-sharp.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.DataGrid">
<HintPath>..\packages\Xamarin.Forms.DataGrid.3.1.0\lib\netstandard2.0\Xamarin.Forms.DataGrid.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>

View File

@ -31,6 +31,5 @@
<package id="VideoLAN.LibVLC.Mac" version="3.1.3.1" targetFramework="net47" />
<package id="Xam.Plugins.Settings" version="3.1.1" targetFramework="net47" />
<package id="Xamarin.Forms" version="4.3.0.991211" targetFramework="net47" />
<package id="Xamarin.Forms.DataGrid" version="3.1.0" targetFramework="net47" />
<package id="Xamarin.Forms.Platform.GTK" version="4.3.0.991211" targetFramework="net47" />
</packages>

View File

@ -10,7 +10,6 @@
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.991211" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
<PackageReference Include="Xamarin.Forms.DataGrid" Version="3.1.0" />
<PackageReference Include="taglib-sharp-netstandard2.0" Version="2.1.0" />
<PackageReference Include="LibVLCSharp.Forms" Version="3.3.1" />
<PackageReference Include="VideoLAN.LibVLC.Mac" Version="3.1.3.1" />
@ -42,6 +41,7 @@
<Folder Include="Design\Components\MemberList\" />
<Folder Include="Design\Components\Library\" />
<Folder Include="Design\Views\Profile\" />
<Folder Include="Design\Components\DataGrid\" />
</ItemGroup>
<ItemGroup>
<Compile Update="Design\Components\MusicPlayer\Player.xaml.cs">
@ -55,9 +55,15 @@
<Protobuf Include="Proto\playback.proto" />
<Protobuf Include="Proto\sync.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Design\Resources\unselected.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Design\Components\NavigationMenu\NavigationMenu.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Remove="Design\Resources\unselected.png" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Aurora.Design.Components.DataGrid
{
public sealed class ColumnCollection : List<DataGridColumn>
{
}
}

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<Grid
x:Name="self"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Aurora.Design.Components.DataGrid.DataGrid"
Padding="0"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto"/>
<RowDefinition
Height="*"/>
</Grid.RowDefinitions>
<Grid
x:Name="_headerView"
RowSpacing="0">
<Grid.Resources>
<ResourceDictionary><!--Default Header Style-->
<Style
x:Key="HeaderDefaultStyle"
TargetType="Label">
<Setter
Property="FontSize"
Value="{Binding HeaderFontSize, Source={x:Reference self}}"/>
<Setter
Property="FontAttributes"
Value="Bold"/>
<Setter
Property="HorizontalOptions"
Value="Center"/>
<Setter
Property="VerticalOptions"
Value="Center"/>
<Setter
Property="TextColor"
Value="{Binding HeaderTextColor,Source={x:Reference self}}"/>
<Setter
Property="LineBreakMode"
Value="WordWrap"/>
</Style>
<Style
TargetType="Grid">
<Setter
Property="BackgroundColor"
Value="{Binding HeaderBackground,Source={x:Reference self}}"/>
</Style>
<Style
x:Key="ImageStyleBase"
TargetType="Image">
<Setter
Property="Aspect"
Value="AspectFill"/>
<Setter
Property="VerticalOptions"
Value="Center"/>
<Setter
Property="HorizontalOptions"
Value="Center"/>
<Setter
Property="HeightRequest"
Value="5"/>
<Setter
Property="WidthRequest"
Value="9"/>
<Setter
Property="Margin"
Value="0,0,4,0"/>
</Style>
<Style
x:Key="AscendingIconStyle"
TargetType="Image"
BasedOn="{StaticResource ImageStyleBase}">
<Setter
Property="Source"
Value="{Binding AscendingIcon, Source={x:Reference self}}"/>
</Style>
<Style
x:Key="DescendingIconStyle"
TargetType="Image"
BasedOn="{StaticResource ImageStyleBase}">
<Setter
Property="Source"
Value="{Binding DescendingIcon, Source={x:Reference self}}"/>
</Style>
</ResourceDictionary>
</Grid.Resources>
</Grid>
<ContentView
x:Name="_noDataView"
Grid.RowSpan="2"
IsVisible="False"/>
</Grid>

View File

@ -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<SelectedItemChangedEventArgs> 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<object>(((IEnumerable)n).Cast<object>());
}
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<object>(((IEnumerable)sender).Cast<object>());
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<object> _internalItems;
internal IList<object> 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<int, SortingOrder> _sortingOrders;
ListView _listView;
#endregion
#region ctor
public DataGrid() : this(ListViewCachingStrategy.RecycleElement)
{
}
public DataGrid(ListViewCachingStrategy cachingStrategy)
{
InitializeComponent();
BackgroundColor = Color.Transparent;
_sortingOrders = new Dictionary<int, SortingOrder>();
_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<object>(_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
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
namespace Aurora.Design.Components.DataGrid
{
public static class DataGridComponent
{
/// <summary>
/// You should call this method for loding the assembly
/// </summary>
public static void Init()
{
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public interface IColorProvider
{
Color GetColor(int rowIndex, object item);
}
}

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
namespace Aurora.Design.Components.DataGrid
{
public sealed class PaletteCollection : List<Color>, IColorProvider
{
public Color GetColor(int rowIndex, object item)
{
if (Count > 0)
return this.ElementAt(rowIndex % Count);
else
return default(Color);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,9 @@
namespace Aurora.Design.Components.DataGrid
{
public enum SortingOrder
{
None = 0,
Ascendant = 1,
Descendant = 2,
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -1,3 +1,3 @@
#LibraryDataGrid {
background-color: black
Label {
margin-left: 25;
}

View File

@ -2,49 +2,92 @@
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dg="clr-namespace:Xamarin.Forms.DataGrid;assembly=Xamarin.Forms.DataGrid"
xmlns:dg="clr-namespace:Aurora.Design.Components.DataGrid"
x:Class="Aurora.Design.Components.Library.Library">
<ContentView.Resources>
<StyleSheet
Source="Library.css"/>
</ContentView.Resources>
<ContentView.Content>
<dg:DataGrid
x:Name="LibraryDataGrid"
SelectionEnabled="True"
RowHeight="25"
HeaderHeight="40"
BorderColor="#CCCCCC"
HeaderBackground="#E0E6F8">
RowHeight="30"
BorderColor="#3a3a3a"
BorderThickness="0"
HeaderFontSize="14"
HeaderHeight="45"
HeaderTextColor="White"
HeaderBackground="#222222">
<dg:DataGrid.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="2"/>
</dg:DataGrid.GestureRecognizers>
<dg:DataGrid.HeaderFontSize>
<OnIdiom
x:TypeArguments="x:Double">
<OnIdiom.Tablet>15</OnIdiom.Tablet>
<OnIdiom.Phone>13</OnIdiom.Phone>
<OnIdiom.Desktop>20</OnIdiom.Desktop>
</OnIdiom>
</dg:DataGrid.HeaderFontSize>
<dg:DataGrid.Columns>
<dg:DataGridColumn
Title=""
PropertyName="Icon"
Width="5">
<dg:DataGridColumn.CellTemplate>
<DataTemplate>
<Image Source="../../Resources/unselected.png" />
</DataTemplate>
</dg:DataGridColumn.CellTemplate>
</dg:DataGridColumn>
<dg:DataGridColumn
Title="Title"
PropertyName="Metadata.Title"
Width="2*"/>
Width="2*">
<dg:DataGridColumn.CellTemplate>
<DataTemplate>
<Label
LineBreakMode="TailTruncation"
Text="{Binding .}"
HorizontalOptions="Start"
VerticalOptions="Center"/>
</DataTemplate>
</dg:DataGridColumn.CellTemplate>
</dg:DataGridColumn>
<dg:DataGridColumn
Title="Album"
PropertyName="Metadata.Album"
Width="0.95*"/>
Width="0.95*">
<dg:DataGridColumn.CellTemplate>
<DataTemplate>
<Label
LineBreakMode="TailTruncation"
Text="{Binding .}"
HorizontalOptions="Start"
VerticalOptions="Center"/>
</DataTemplate>
</dg:DataGridColumn.CellTemplate>
</dg:DataGridColumn>
<dg:DataGridColumn
Title="Artist"
PropertyName="Metadata.Artist"
Width="1*"/>
<dg:DataGridColumn
Title="Duration"
PropertyName="Metadata.Duration"/>
Width="1*">
<dg:DataGridColumn.CellTemplate>
<DataTemplate>
<Label
LineBreakMode="TailTruncation"
Text="{Binding .}"
HorizontalOptions="Start"
VerticalOptions="Center"
TextColor="White"/>
</DataTemplate>
</dg:DataGridColumn.CellTemplate>
</dg:DataGridColumn>
</dg:DataGrid.Columns>
<dg:DataGrid.RowsTextColorPalette>
<dg:PaletteCollection>
<Color>White</Color>
</dg:PaletteCollection>
</dg:DataGrid.RowsTextColorPalette>
<dg:DataGrid.RowsBackgroundColorPalette>
<dg:PaletteCollection>
<Color>#F2F2F2</Color>
<Color>#FFFFFF</Color>
<Color>Transparent</Color>
</dg:PaletteCollection>
</dg:DataGrid.RowsBackgroundColorPalette>
</dg:DataGrid>

View File

@ -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

View File

@ -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;

View File

@ -11,7 +11,6 @@
<StackLayout x:Name="Layout">
<ListView
x:Name="MenuItemsListView"
SeparatorVisibility="None"
HasUnevenRows="true"
IsGroupingEnabled="true"
RowHeight="35"

View File

@ -11,7 +11,6 @@ namespace Aurora.Design.Components.NavigationMenu
{
InitializeComponent();
ListView = MenuItemsListView;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -4,7 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Aurora.Design.Views.MainView"
xmlns:navigation="clr-namespace:Aurora.Design.Components.NavigationMenu"
xmlns:mp="clr-namespace:Aurora.Design.Components.MediaPlayer"
xmlns:mp="clr-namespace:Aurora.Design.Components.MediaPlayer"
x:Class="Aurora.Design.Views.Main.MainView">
<ContentPage.Resources>
<StyleSheet

View File

@ -0,0 +1,77 @@
using System;
using System.Linq;
using System.Reflection;
namespace Aurora.Utils
{
public static class ReflectionUtils
{
private const char IndexBeginOp = '[';
private const char IndexEndOp = ']';
private const char PropertyOfOp = '.';
public static object GetValueByPath(object obj, string path)
{
object result = obj;
var tokens = path?.Split(IndexBeginOp, PropertyOfOp).ToList();
foreach (var token in tokens)
{
if (result == null)
break;
// Property
if (!token.Contains(IndexEndOp.ToString()))
{
result = GetPropertyValue(result, token);
}
// Index
else
{
result = GetIndexValue(result, token.Replace(IndexEndOp.ToString(), ""));
}
}
return result;
}
private static object GetPropertyValue(object obj, string propertyName)
{
try
{
return obj?.GetType().GetRuntimeProperty(propertyName)?.GetValue(obj);
}
catch
{
return null;
}
}
private static object GetIndexValue(object obj, string index)
{
object result = null;
var indexOperator = obj?.GetType().GetRuntimeProperty("Item");
if (indexOperator != null)
{
var indexParameters = indexOperator.GetIndexParameters();
// Looking up suitable index operator
foreach (var parameter in indexParameters)
{
bool isIndexOpWorked = true;
try
{
var indexVal = Convert.ChangeType(index, parameter.ParameterType);
result = indexOperator.GetValue(obj, new object[] { indexVal });
}
catch
{
isIndexOpWorked = false;
}
// If the index operator worked, skip looking up others
if (isIndexOpWorked)
break;
}
}
return result;
}
}
}