mirror of
https://github.com/bitwarden/mobile
synced 2025-12-29 14:43:50 +00:00
BoxedView with LabelCell
This commit is contained in:
85
src/App/Controls/BoxedView/BoxedModel.cs
Normal file
85
src/App/Controls/BoxedView/BoxedModel.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
public class BoxedModel : TableModel
|
||||
{
|
||||
private static readonly BindableProperty PathProperty = BindableProperty.Create(
|
||||
"Path", typeof(Tuple<int, int>), typeof(Cell), null);
|
||||
|
||||
private BoxedRoot _root;
|
||||
private IEnumerable<BoxedSection> _visibleSections;
|
||||
|
||||
public BoxedModel(BoxedRoot root)
|
||||
{
|
||||
_root = root;
|
||||
_visibleSections = _root.Where(x => x.IsVisible);
|
||||
}
|
||||
|
||||
public override Cell GetCell(int section, int row)
|
||||
{
|
||||
var cell = (Cell)GetItem(section, row);
|
||||
SetPath(cell, new Tuple<int, int>(section, row));
|
||||
return cell;
|
||||
}
|
||||
|
||||
public override object GetItem(int section, int row)
|
||||
{
|
||||
return _visibleSections.ElementAt(section)[row];
|
||||
}
|
||||
|
||||
public override int GetRowCount(int section)
|
||||
{
|
||||
return _visibleSections.ElementAt(section).Count;
|
||||
}
|
||||
|
||||
public override int GetSectionCount()
|
||||
{
|
||||
return _visibleSections.Count();
|
||||
}
|
||||
|
||||
public virtual BoxedSection GetSection(int section)
|
||||
{
|
||||
return _visibleSections.ElementAtOrDefault(section);
|
||||
}
|
||||
|
||||
public override string GetSectionTitle(int section)
|
||||
{
|
||||
return _visibleSections.ElementAt(section).Title;
|
||||
}
|
||||
|
||||
public virtual string GetFooterText(int section)
|
||||
{
|
||||
return _visibleSections.ElementAt(section).FooterText;
|
||||
}
|
||||
|
||||
protected override void OnRowSelected(object item)
|
||||
{
|
||||
base.OnRowSelected(item);
|
||||
(item as BaseCell)?.OnTapped();
|
||||
}
|
||||
|
||||
public virtual double GetHeaderHeight(int section)
|
||||
{
|
||||
return _visibleSections.ElementAt(section).HeaderHeight;
|
||||
}
|
||||
|
||||
public static Tuple<int, int> GetPath(Cell item)
|
||||
{
|
||||
if(item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
return item.GetValue(PathProperty) as Tuple<int, int>;
|
||||
}
|
||||
|
||||
private static void SetPath(Cell item, Tuple<int, int> index)
|
||||
{
|
||||
item?.SetValue(PathProperty, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
src/App/Controls/BoxedView/BoxedRoot.cs
Normal file
61
src/App/Controls/BoxedView/BoxedRoot.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
public class BoxedRoot : TableSectionBase<BoxedSection>
|
||||
{
|
||||
public BoxedRoot()
|
||||
{
|
||||
SetupEvents();
|
||||
}
|
||||
|
||||
public event EventHandler<EventArgs> SectionCollectionChanged;
|
||||
|
||||
private void ChildCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
SectionCollectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if(e.PropertyName == TitleProperty.PropertyName)
|
||||
{
|
||||
OnPropertyChanged(TitleProperty.PropertyName);
|
||||
}
|
||||
else if(e.PropertyName == BoxedSection.FooterTextProperty.PropertyName)
|
||||
{
|
||||
OnPropertyChanged(BoxedSection.FooterTextProperty.PropertyName);
|
||||
}
|
||||
else if(e.PropertyName == BoxedSection.IsVisibleProperty.PropertyName)
|
||||
{
|
||||
OnPropertyChanged(BoxedSection.IsVisibleProperty.PropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupEvents()
|
||||
{
|
||||
CollectionChanged += (sender, args) =>
|
||||
{
|
||||
if(args.NewItems != null)
|
||||
{
|
||||
foreach(BoxedSection section in args.NewItems)
|
||||
{
|
||||
section.CollectionChanged += ChildCollectionChanged;
|
||||
section.PropertyChanged += ChildPropertyChanged;
|
||||
}
|
||||
}
|
||||
if(args.OldItems != null)
|
||||
{
|
||||
foreach(BoxedSection section in args.OldItems)
|
||||
{
|
||||
section.CollectionChanged -= ChildCollectionChanged;
|
||||
section.PropertyChanged -= ChildPropertyChanged;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
147
src/App/Controls/BoxedView/BoxedSection.cs
Normal file
147
src/App/Controls/BoxedView/BoxedSection.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
public class BoxedSection : TableSectionBase<Cell>
|
||||
{
|
||||
public static BindableProperty IsVisibleProperty = BindableProperty.Create(
|
||||
nameof(IsVisible), typeof(bool), typeof(BoxedSection), true, defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty FooterTextProperty = BindableProperty.Create(
|
||||
nameof(FooterText), typeof(string), typeof(BoxedSection), default(string),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty ItemTemplateProperty = BindableProperty.Create(
|
||||
nameof(ItemTemplate), typeof(DataTemplate), typeof(BoxedSection), default(DataTemplate),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty ItemsSourceProperty = BindableProperty.Create(
|
||||
nameof(ItemsSource), typeof(IList), typeof(BoxedSection), default(IList),
|
||||
defaultBindingMode: BindingMode.OneWay, propertyChanged: ItemsChanged);
|
||||
|
||||
public static BindableProperty HeaderHeightProperty = BindableProperty.Create(
|
||||
nameof(HeaderHeight), typeof(double), typeof(BoxedSection), -1d, defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty UseDragSortProperty = BindableProperty.Create(
|
||||
nameof(UseDragSort), typeof(bool), typeof(BoxedSection), false, defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public BoxedSection()
|
||||
{ }
|
||||
|
||||
public BoxedSection(string title)
|
||||
: base(title)
|
||||
{ }
|
||||
|
||||
public bool IsVisible
|
||||
{
|
||||
get => (bool)GetValue(IsVisibleProperty);
|
||||
set => SetValue(IsVisibleProperty, value);
|
||||
}
|
||||
|
||||
public string FooterText
|
||||
{
|
||||
get => (string)GetValue(FooterTextProperty);
|
||||
set => SetValue(FooterTextProperty, value);
|
||||
}
|
||||
|
||||
public DataTemplate ItemTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(ItemTemplateProperty);
|
||||
set => SetValue(ItemTemplateProperty, value);
|
||||
}
|
||||
|
||||
public IList ItemsSource
|
||||
{
|
||||
get => (IList)GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
public double HeaderHeight
|
||||
{
|
||||
get => (double)GetValue(HeaderHeightProperty);
|
||||
set => SetValue(HeaderHeightProperty, value);
|
||||
}
|
||||
|
||||
public bool UseDragSort
|
||||
{
|
||||
get => (bool)GetValue(UseDragSortProperty);
|
||||
set => SetValue(UseDragSortProperty, value);
|
||||
}
|
||||
|
||||
private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var section = bindable as BoxedSection;
|
||||
if(section.ItemTemplate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(oldValue is INotifyCollectionChanged oldObservableCollection)
|
||||
{
|
||||
oldObservableCollection.CollectionChanged -= section.OnItemsSourceCollectionChanged;
|
||||
}
|
||||
if(newValue is INotifyCollectionChanged newObservableCollection)
|
||||
{
|
||||
newObservableCollection.CollectionChanged += section.OnItemsSourceCollectionChanged;
|
||||
}
|
||||
|
||||
section.Clear();
|
||||
|
||||
if(newValue is IList newValueAsEnumerable)
|
||||
{
|
||||
foreach(var item in newValueAsEnumerable)
|
||||
{
|
||||
var view = CreateChildViewFor(section.ItemTemplate, item, section);
|
||||
section.Add(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if(e.Action == NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
RemoveAt(e.OldStartingIndex);
|
||||
var item = e.NewItems[e.NewStartingIndex];
|
||||
var view = CreateChildViewFor(ItemTemplate, item, this);
|
||||
Insert(e.NewStartingIndex, view);
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if(e.NewItems != null)
|
||||
{
|
||||
for(var i = 0; i < e.NewItems.Count; ++i)
|
||||
{
|
||||
var item = e.NewItems[i];
|
||||
var view = CreateChildViewFor(ItemTemplate, item, this);
|
||||
Insert(i + e.NewStartingIndex, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
if(e.OldItems != null)
|
||||
{
|
||||
RemoveAt(e.OldStartingIndex);
|
||||
}
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static Cell CreateChildViewFor(DataTemplate template, object item, BindableObject container)
|
||||
{
|
||||
if(template is DataTemplateSelector selector)
|
||||
{
|
||||
template = selector.SelectTemplate(item, container);
|
||||
}
|
||||
// Binding context
|
||||
template.SetValue(BindingContextProperty, item);
|
||||
return template.CreateContent() as Cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
602
src/App/Controls/BoxedView/BoxedView.cs
Normal file
602
src/App/Controls/BoxedView/BoxedView.cs
Normal file
@@ -0,0 +1,602 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
[ContentProperty("Root")]
|
||||
public class BoxedView : TableView
|
||||
{
|
||||
private BoxedRoot _root;
|
||||
|
||||
public new event EventHandler ModelChanged;
|
||||
|
||||
public BoxedView()
|
||||
{
|
||||
VerticalOptions = HorizontalOptions = LayoutOptions.FillAndExpand;
|
||||
Root = new BoxedRoot();
|
||||
Model = new BoxedModel(Root);
|
||||
}
|
||||
|
||||
public new BoxedModel Model { get; set; }
|
||||
|
||||
public new BoxedRoot Root
|
||||
{
|
||||
get => _root;
|
||||
set
|
||||
{
|
||||
if(_root != null)
|
||||
{
|
||||
_root.PropertyChanged -= RootOnPropertyChanged;
|
||||
_root.CollectionChanged -= OnCollectionChanged;
|
||||
_root.SectionCollectionChanged -= OnSectionCollectionChanged;
|
||||
}
|
||||
|
||||
_root = value;
|
||||
|
||||
// Transfer binding context to the children (maybe...)
|
||||
SetInheritedBindingContext(_root, BindingContext);
|
||||
|
||||
_root.PropertyChanged += RootOnPropertyChanged;
|
||||
_root.CollectionChanged += OnCollectionChanged;
|
||||
_root.SectionCollectionChanged += OnSectionCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnBindingContextChanged()
|
||||
{
|
||||
base.OnBindingContextChanged();
|
||||
if(Root != null)
|
||||
{
|
||||
SetInheritedBindingContext(Root, BindingContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void RootOnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if(e.PropertyName == TableSectionBase.TitleProperty.PropertyName ||
|
||||
e.PropertyName == BoxedSection.FooterTextProperty.PropertyName ||
|
||||
e.PropertyName == BoxedSection.IsVisibleProperty.PropertyName)
|
||||
{
|
||||
OnModelChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
base.OnPropertyChanged(propertyName);
|
||||
var changed = propertyName == HasUnevenRowsProperty.PropertyName ||
|
||||
propertyName == HeaderHeightProperty.PropertyName ||
|
||||
propertyName == HeaderFontSizeProperty.PropertyName ||
|
||||
propertyName == HeaderTextColorProperty.PropertyName ||
|
||||
propertyName == HeaderBackgroundColorProperty.PropertyName ||
|
||||
propertyName == HeaderTextVerticalAlignProperty.PropertyName ||
|
||||
propertyName == HeaderPaddingProperty.PropertyName ||
|
||||
propertyName == FooterFontSizeProperty.PropertyName ||
|
||||
propertyName == FooterTextColorProperty.PropertyName ||
|
||||
propertyName == FooterBackgroundColorProperty.PropertyName ||
|
||||
propertyName == FooterPaddingProperty.PropertyName;
|
||||
if(changed)
|
||||
{
|
||||
OnModelChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnModelChanged();
|
||||
}
|
||||
|
||||
public void OnSectionCollectionChanged(object sender, EventArgs childCollectionChangedEventArgs)
|
||||
{
|
||||
OnModelChanged();
|
||||
}
|
||||
|
||||
protected new void OnModelChanged()
|
||||
{
|
||||
var cells = Root?.SelectMany(r => r);
|
||||
if(cells == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach(var cell in cells)
|
||||
{
|
||||
cell.Parent = this;
|
||||
}
|
||||
ModelChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
// Make the unnecessary property existing at TableView sealed.
|
||||
private new int Intent { get; set; }
|
||||
|
||||
public static new BindableProperty BackgroundColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(BackgroundColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public new Color BackgroundColor
|
||||
{
|
||||
get { return (Color)GetValue(BackgroundColorProperty); }
|
||||
set { SetValue(BackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty SeparatorColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(SeparatorColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
Color.FromRgb(199, 199, 204),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color SeparatorColor
|
||||
{
|
||||
get { return (Color)GetValue(SeparatorColorProperty); }
|
||||
set { SetValue(SeparatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty SelectedColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(SelectedColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color SelectedColor
|
||||
{
|
||||
get { return (Color)GetValue(SelectedColorProperty); }
|
||||
set { SetValue(SelectedColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderPaddingProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderPadding),
|
||||
typeof(Thickness),
|
||||
typeof(BoxedView),
|
||||
new Thickness(14, 8, 8, 8),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Thickness HeaderPadding
|
||||
{
|
||||
get { return (Thickness)GetValue(HeaderPaddingProperty); }
|
||||
set { SetValue(HeaderPaddingProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderTextColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderTextColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color HeaderTextColor
|
||||
{
|
||||
get { return (Color)GetValue(HeaderTextColorProperty); }
|
||||
set { SetValue(HeaderTextColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderFontSizeProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderFontSize),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay,
|
||||
defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Small, (BoxedView)bindable)
|
||||
);
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double HeaderFontSize
|
||||
{
|
||||
get { return (double)GetValue(HeaderFontSizeProperty); }
|
||||
set { SetValue(HeaderFontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderTextVerticalAlignProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderTextVerticalAlign),
|
||||
typeof(LayoutAlignment),
|
||||
typeof(BoxedView),
|
||||
LayoutAlignment.End,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public LayoutAlignment HeaderTextVerticalAlign
|
||||
{
|
||||
get { return (LayoutAlignment)GetValue(HeaderTextVerticalAlignProperty); }
|
||||
set { SetValue(HeaderTextVerticalAlignProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderBackgroundColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderBackgroundColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color HeaderBackgroundColor
|
||||
{
|
||||
get { return (Color)GetValue(HeaderBackgroundColorProperty); }
|
||||
set { SetValue(HeaderBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty HeaderHeightProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(HeaderHeight),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1d,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public double HeaderHeight
|
||||
{
|
||||
get { return (double)GetValue(HeaderHeightProperty); }
|
||||
set { SetValue(HeaderHeightProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty FooterTextColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(FooterTextColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color FooterTextColor
|
||||
{
|
||||
get { return (Color)GetValue(FooterTextColorProperty); }
|
||||
set { SetValue(FooterTextColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty FooterFontSizeProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(FooterFontSize),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay,
|
||||
defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Small, (BoxedView)bindable)
|
||||
);
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double FooterFontSize
|
||||
{
|
||||
get { return (double)GetValue(FooterFontSizeProperty); }
|
||||
set { SetValue(FooterFontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty FooterBackgroundColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(FooterBackgroundColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color FooterBackgroundColor
|
||||
{
|
||||
get { return (Color)GetValue(FooterBackgroundColorProperty); }
|
||||
set { SetValue(FooterBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty FooterPaddingProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(FooterPadding),
|
||||
typeof(Thickness),
|
||||
typeof(BoxedView),
|
||||
new Thickness(14, 8, 14, 8),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Thickness FooterPadding
|
||||
{
|
||||
get { return (Thickness)GetValue(FooterPaddingProperty); }
|
||||
set { SetValue(FooterPaddingProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellTitleColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellTitleColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color CellTitleColor
|
||||
{
|
||||
get { return (Color)GetValue(CellTitleColorProperty); }
|
||||
set { SetValue(CellTitleColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellTitleFontSizeProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellTitleFontSize),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1.0,
|
||||
defaultBindingMode: BindingMode.OneWay,
|
||||
defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Default, (BoxedView)bindable)
|
||||
);
|
||||
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double CellTitleFontSize
|
||||
{
|
||||
get { return (double)GetValue(CellTitleFontSizeProperty); }
|
||||
set { SetValue(CellTitleFontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellValueTextColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellValueTextColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color CellValueTextColor
|
||||
{
|
||||
get { return (Color)GetValue(CellValueTextColorProperty); }
|
||||
set { SetValue(CellValueTextColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellValueTextFontSizeProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellValueTextFontSize),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double CellValueTextFontSize
|
||||
{
|
||||
get { return (double)GetValue(CellValueTextFontSizeProperty); }
|
||||
set { SetValue(CellValueTextFontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellDescriptionColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellDescriptionColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color CellDescriptionColor
|
||||
{
|
||||
get { return (Color)GetValue(CellDescriptionColorProperty); }
|
||||
set { SetValue(CellDescriptionColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellDescriptionFontSizeProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellDescriptionFontSize),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double CellDescriptionFontSize
|
||||
{
|
||||
get { return (double)GetValue(CellDescriptionFontSizeProperty); }
|
||||
set { SetValue(CellDescriptionFontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellBackgroundColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellBackgroundColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color CellBackgroundColor
|
||||
{
|
||||
get { return (Color)GetValue(CellBackgroundColorProperty); }
|
||||
set { SetValue(CellBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty CellAccentColorProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(CellAccentColor),
|
||||
typeof(Color),
|
||||
typeof(BoxedView),
|
||||
Color.Accent,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public Color CellAccentColor
|
||||
{
|
||||
get { return (Color)GetValue(CellAccentColorProperty); }
|
||||
set { SetValue(CellAccentColorProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty ShowSectionTopBottomBorderProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(ShowSectionTopBottomBorder),
|
||||
typeof(bool),
|
||||
typeof(BoxedView),
|
||||
true,
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public bool ShowSectionTopBottomBorder
|
||||
{
|
||||
get { return (bool)GetValue(ShowSectionTopBottomBorderProperty); }
|
||||
set { SetValue(ShowSectionTopBottomBorderProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty ScrollToBottomProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(ScrollToBottom),
|
||||
typeof(bool),
|
||||
typeof(BoxedView),
|
||||
default(bool),
|
||||
defaultBindingMode: BindingMode.TwoWay
|
||||
);
|
||||
|
||||
public bool ScrollToBottom
|
||||
{
|
||||
get { return (bool)GetValue(ScrollToBottomProperty); }
|
||||
set { SetValue(ScrollToBottomProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty ScrollToTopProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(ScrollToTop),
|
||||
typeof(bool),
|
||||
typeof(BoxedView),
|
||||
default(bool),
|
||||
defaultBindingMode: BindingMode.TwoWay
|
||||
);
|
||||
|
||||
public bool ScrollToTop
|
||||
{
|
||||
get { return (bool)GetValue(ScrollToTopProperty); }
|
||||
set { SetValue(ScrollToTopProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty VisibleContentHeightProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(VisibleContentHeight),
|
||||
typeof(double),
|
||||
typeof(BoxedView),
|
||||
-1d,
|
||||
defaultBindingMode: BindingMode.OneWayToSource
|
||||
);
|
||||
|
||||
public double VisibleContentHeight
|
||||
{
|
||||
get { return (double)GetValue(VisibleContentHeightProperty); }
|
||||
set { SetValue(VisibleContentHeightProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty ItemsSourceProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(ItemsSource),
|
||||
typeof(IEnumerable),
|
||||
typeof(BoxedView),
|
||||
default(IEnumerable),
|
||||
defaultBindingMode: BindingMode.OneWay,
|
||||
propertyChanged: ItemsChanged
|
||||
);
|
||||
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
|
||||
set { SetValue(ItemsSourceProperty, value); }
|
||||
}
|
||||
|
||||
public static BindableProperty ItemTemplateProperty =
|
||||
BindableProperty.Create(
|
||||
nameof(ItemTemplate),
|
||||
typeof(DataTemplate),
|
||||
typeof(BoxedView),
|
||||
default(DataTemplate),
|
||||
defaultBindingMode: BindingMode.OneWay
|
||||
);
|
||||
|
||||
public DataTemplate ItemTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
|
||||
set { SetValue(ItemTemplateProperty, value); }
|
||||
}
|
||||
|
||||
private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var boxedView = bindable as BoxedView;
|
||||
if(boxedView.ItemTemplate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(oldValue is INotifyCollectionChanged oldObservableCollection)
|
||||
{
|
||||
oldObservableCollection.CollectionChanged -= boxedView.OnItemsSourceCollectionChanged;
|
||||
}
|
||||
if(newValue is INotifyCollectionChanged newObservableCollection)
|
||||
{
|
||||
newObservableCollection.CollectionChanged += boxedView.OnItemsSourceCollectionChanged;
|
||||
}
|
||||
|
||||
boxedView.Root.Clear();
|
||||
|
||||
if(newValue is IList newValueAsEnumerable)
|
||||
{
|
||||
foreach(var item in newValueAsEnumerable)
|
||||
{
|
||||
var view = CreateChildViewFor(boxedView.ItemTemplate, item, boxedView);
|
||||
boxedView.Root.Add(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if(e.Action == NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
Root.RemoveAt(e.OldStartingIndex);
|
||||
var item = e.NewItems[e.NewStartingIndex];
|
||||
var view = CreateChildViewFor(ItemTemplate, item, this);
|
||||
Root.Insert(e.NewStartingIndex, view);
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if(e.NewItems != null)
|
||||
{
|
||||
for(var i = 0; i < e.NewItems.Count; ++i)
|
||||
{
|
||||
var item = e.NewItems[i];
|
||||
var view = CreateChildViewFor(ItemTemplate, item, this);
|
||||
Root.Insert(i + e.NewStartingIndex, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
if(e.OldItems != null)
|
||||
{
|
||||
Root.RemoveAt(e.OldStartingIndex);
|
||||
}
|
||||
}
|
||||
else if(e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
Root.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static BoxedSection CreateChildViewFor(DataTemplate template, object item, BindableObject container)
|
||||
{
|
||||
if(template is DataTemplateSelector selector)
|
||||
{
|
||||
template = selector.SelectTemplate(item, container);
|
||||
}
|
||||
template.SetValue(BindingContextProperty, item);
|
||||
return template.CreateContent() as BoxedSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/App/Controls/BoxedView/Cells/BaseCell.cs
Normal file
87
src/App/Controls/BoxedView/Cells/BaseCell.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
public class BaseCell : Cell
|
||||
{
|
||||
public new event EventHandler Tapped;
|
||||
|
||||
internal new void OnTapped()
|
||||
{
|
||||
Tapped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public static BindableProperty TitleProperty = BindableProperty.Create(
|
||||
nameof(Title), typeof(string), typeof(BaseCell), default(string), defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty TitleColorProperty = BindableProperty.Create(
|
||||
nameof(TitleColor), typeof(Color), typeof(BaseCell), default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty TitleFontSizeProperty = BindableProperty.Create(
|
||||
nameof(TitleFontSize), typeof(double), typeof(BaseCell), -1.0, defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty DescriptionProperty = BindableProperty.Create(
|
||||
nameof(Description), typeof(string), typeof(BaseCell), default(string),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty DescriptionColorProperty = BindableProperty.Create(
|
||||
nameof(DescriptionColor), typeof(Color), typeof(BaseCell), default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty DescriptionFontSizeProperty = BindableProperty.Create(
|
||||
nameof(DescriptionFontSize), typeof(double), typeof(BaseCell), -1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty BackgroundColorProperty = BindableProperty.Create(
|
||||
nameof(BackgroundColor), typeof(Color), typeof(BaseCell), default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => (string)GetValue(TitleProperty);
|
||||
set => SetValue(TitleProperty, value);
|
||||
}
|
||||
|
||||
public Color TitleColor
|
||||
{
|
||||
get => (Color)GetValue(TitleColorProperty);
|
||||
set => SetValue(TitleColorProperty, value);
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double TitleFontSize
|
||||
{
|
||||
get => (double)GetValue(TitleFontSizeProperty);
|
||||
set => SetValue(TitleFontSizeProperty, value);
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => (string)GetValue(DescriptionProperty);
|
||||
set => SetValue(DescriptionProperty, value);
|
||||
}
|
||||
|
||||
public Color DescriptionColor
|
||||
{
|
||||
get => (Color)GetValue(DescriptionColorProperty);
|
||||
set => SetValue(DescriptionColorProperty, value);
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double DescriptionFontSize
|
||||
{
|
||||
get => (double)GetValue(DescriptionFontSizeProperty);
|
||||
set => SetValue(DescriptionFontSizeProperty, value);
|
||||
}
|
||||
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => (Color)GetValue(BackgroundColorProperty);
|
||||
set => SetValue(BackgroundColorProperty, value);
|
||||
}
|
||||
|
||||
public BoxedSection Section { get; set; }
|
||||
}
|
||||
}
|
||||
38
src/App/Controls/BoxedView/Cells/LabelCell.cs
Normal file
38
src/App/Controls/BoxedView/Cells/LabelCell.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls.BoxedView
|
||||
{
|
||||
public class LabelCell : BaseCell
|
||||
{
|
||||
public static BindableProperty ValueTextProperty = BindableProperty.Create(
|
||||
nameof(ValueText), typeof(string), typeof(LabelCell), default(string),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty ValueTextColorProperty = BindableProperty.Create(
|
||||
nameof(ValueTextColor), typeof(Color), typeof(LabelCell), default(Color),
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public static BindableProperty ValueTextFontSizeProperty = BindableProperty.Create(
|
||||
nameof(ValueTextFontSize), typeof(double), typeof(LabelCell), -1.0d,
|
||||
defaultBindingMode: BindingMode.OneWay);
|
||||
|
||||
public string ValueText
|
||||
{
|
||||
get => (string)GetValue(ValueTextProperty);
|
||||
set => SetValue(ValueTextProperty, value);
|
||||
}
|
||||
|
||||
public Color ValueTextColor
|
||||
{
|
||||
get => (Color)GetValue(ValueTextColorProperty);
|
||||
set => SetValue(ValueTextColorProperty, value);
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public double ValueTextFontSize
|
||||
{
|
||||
get => (double)GetValue(ValueTextFontSizeProperty);
|
||||
set => SetValue(ValueTextFontSizeProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user