using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Tango.Core.Commands; using Tango.Core.EventArguments; using Tango.Core.ExtensionMethods; namespace Tango.Touch.Controls { [ContentProperty(nameof(ItemsSource))] public class TouchListBox : Control { private ItemsControl _items_control; private bool _isLoaded; private List _awaitingSelectionItems; private TouchListBoxItem _firstMultiSelectionItem; static TouchListBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TouchListBox), new FrameworkPropertyMetadata(typeof(TouchListBox))); } public Object SelectedItem { get { return (Object)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(Object), typeof(TouchListBox), new PropertyMetadata(null, (d, e) => (d as TouchListBox).OnSelectedItemChanged())); public IList SelectedItems { get { return (IList)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IList), typeof(TouchListBox), new PropertyMetadata(null)); public IList ItemsSource { get { return (IList)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IList), typeof(TouchListBox), new PropertyMetadata(null, (d, e) => (d as TouchListBox).OnItemsSourceChanged())); public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty, value); } } public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(TouchListBox), new PropertyMetadata(null)); public Style ItemContainerStyle { get { return (Style)GetValue(ItemContainerStyleProperty); } set { SetValue(ItemContainerStyleProperty, value); } } public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(TouchListBox), new PropertyMetadata(null)); public SelectionMode SelectionMode { get { return (SelectionMode)GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register("SelectionMode", typeof(SelectionMode), typeof(TouchListBox), new PropertyMetadata(SelectionMode.Single)); public bool IsMultiSelecting { get { return (bool)GetValue(IsMultiSelectingProperty); } set { SetValue(IsMultiSelectingProperty, value); } } public static readonly DependencyProperty IsMultiSelectingProperty = DependencyProperty.Register("IsMultiSelecting", typeof(bool), typeof(TouchListBox), new PropertyMetadata(false)); public LightTouchScrollViewer ScrollViewer { get { return (LightTouchScrollViewer)GetValue(ScrollViewerProperty); } set { SetValue(ScrollViewerProperty, value); } } public static readonly DependencyProperty ScrollViewerProperty = DependencyProperty.Register("ScrollViewer", typeof(LightTouchScrollViewer), typeof(TouchListBox), new PropertyMetadata(null)); public ICommand ItemSelectedCommand { get { return (ICommand)GetValue(ItemSelectedCommandProperty); } set { SetValue(ItemSelectedCommandProperty, value); } } public static readonly DependencyProperty ItemSelectedCommandProperty = DependencyProperty.Register("ItemSelectedCommand", typeof(ICommand), typeof(TouchListBox), new PropertyMetadata(null)); public RelayCommand ListBoxItemLoadedCommand { get { return (RelayCommand)GetValue(ListBoxItemLoadedCommandProperty); } set { SetValue(ListBoxItemLoadedCommandProperty, value); } } public static readonly DependencyProperty ListBoxItemLoadedCommandProperty = DependencyProperty.Register("ListBoxItemLoadedCommand", typeof(RelayCommand), typeof(TouchListBox), new PropertyMetadata(null)); public bool DisableScrolling { get { return (bool)GetValue(DisableScrollingProperty); } set { SetValue(DisableScrollingProperty, value); } } public static readonly DependencyProperty DisableScrollingProperty = DependencyProperty.Register("DisableScrolling", typeof(bool), typeof(TouchListBox), new PropertyMetadata(false)); public bool DisableRipple { get { return (bool)GetValue(DisableRippleProperty); } set { SetValue(DisableRippleProperty, value); } } public static readonly DependencyProperty DisableRippleProperty = DependencyProperty.Register("DisableRipple", typeof(bool), typeof(TouchListBox), new PropertyMetadata(false)); public Visibility ScrollBarVisibility { get { return (Visibility)GetValue(ScrollBarVisibilityProperty); } set { SetValue(ScrollBarVisibilityProperty, value); } } public static readonly DependencyProperty ScrollBarVisibilityProperty = DependencyProperty.Register("ScrollBarVisibility", typeof(Visibility), typeof(TouchListBox), new PropertyMetadata(Visibility.Visible)); public String ValuePath { get { return (String)GetValue(ValuePathProperty); } set { SetValue(ValuePathProperty, value); } } public static readonly DependencyProperty ValuePathProperty = DependencyProperty.Register("ValuePath", typeof(String), typeof(TouchListBox), new PropertyMetadata(null)); public TouchListBox() { _awaitingSelectionItems = new List(); Loaded += TouchListBox_Loaded; ListBoxItemLoadedCommand = new RelayCommand(RegisterListBoxItemEvents); } private void RegisterListBoxItemEvents(TouchListBoxItem item) { if (item.Tag == null) { item.Tag = 1; item.RegisterForPreviewMouseOrTouchDown(OnItemMouseDown); item.RegisterForPreviewMouseOrTouchUp(OnItemMouseUp); } } private void TouchListBox_Loaded(object sender, RoutedEventArgs e) { if (!_isLoaded) { var stack = _items_control.FindChild(); if (stack != null) { _items_control.Bind(ItemsControl.HeightProperty, stack, StackPanel.ActualHeightProperty, BindingMode.OneWay); _isLoaded = true; RegisterItemsEvents(); OnSelectedItemChanged(); } } } public override void OnApplyTemplate() { base.OnApplyTemplate(); _items_control = GetTemplateChild("PART_ItemsControl") as ItemsControl; ScrollViewer = GetTemplateChild("PART_ScrollViewer") as LightTouchScrollViewer; _items_control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; OnSelectedItemChanged(); } private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (_items_control.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { try { _items_control.UpdateLayout(); } catch { } RegisterItemsEvents(); OnSelectedItemChanged(); } } private void UpdateSelectedItems() { var selectedItem = GetItems().FirstOrDefault(x => x.IsSelected); if (selectedItem != null) { SelectedItem = selectedItem.DataContext; } if (SelectedItems != null) { SelectedItems.Clear(); foreach (var item in GetItems().Where(x => x.IsSelected)) { SelectedItems.Add(item.DataContext); } } } public List GetItems() { return _items_control.FindVisualChildren().ToList(); } private void RegisterItemsEvents() { var items = GetItems(); foreach (var item in items) { if (item.Tag == null) { item.Tag = 1; item.RegisterForPreviewMouseOrTouchDown(OnItemMouseDown); item.RegisterForPreviewMouseOrTouchUp(OnItemMouseUp); } } } private async void OnItemMouseDown(object sender, MouseOrTouchEventArgs e) { var item = (e.Source is TouchListBoxItem) ? e.Source as TouchListBoxItem : (e.Source as DependencyObject).FindAncestor(); var otherItems = GetItems().Where(x => x != item).ToList(); if (SelectionMode == SelectionMode.Multiple) { if (!IsMultiSelecting) { _awaitingSelectionItems.Add(item); await Task.Delay(500); if (_awaitingSelectionItems.Contains(item)) { _awaitingSelectionItems.Remove(item); if (ScrollViewer.IsMouseTouchDown && !ScrollViewer.IsScrolling) { _firstMultiSelectionItem = item; otherItems.ForEach(x => x.IsSelected = false); item.IsSelected = true; IsMultiSelecting = true; UpdateSelectedItems(); } } } } } private void OnItemMouseUp(object sender, MouseOrTouchEventArgs e) { if (ScrollViewer.IsAfterScrolling) return; var item = (e.Source is TouchListBoxItem) ? e.Source as TouchListBoxItem : (e.Source as DependencyObject).FindAncestor(); var otherItems = GetItems().Where(x => x != item).ToList(); if (!IsMultiSelecting) { _awaitingSelectionItems.Remove(item); if (SelectionMode != SelectionMode.Multiple) { otherItems.ForEach(x => x.IsSelected = false); item.IsSelected = true; ItemSelectedCommand?.Execute(item.DataContext); } else { SelectedItem = item.DataContext; ItemSelectedCommand?.Execute(item.DataContext); } } else if (SelectionMode == SelectionMode.Multiple && IsMultiSelecting) { if (!ScrollViewer.IsScrolling) { if (item != _firstMultiSelectionItem) { item.IsSelected = !item.IsSelected; } _firstMultiSelectionItem = null; if (!item.IsSelected && otherItems.All(x => !x.IsSelected)) { IsMultiSelecting = false; } } } UpdateSelectedItems(); } private void OnSelectedItemChanged() { var items = GetItems(); items.ForEach(x => x.IsSelected = false); TouchListBoxItem item = null; if (String.IsNullOrWhiteSpace(ValuePath) || SelectedItem == null) { item = items.FirstOrDefault(x => x.DataContext.Equals(SelectedItem)); } else { item = items.FirstOrDefault(x => x.DataContext.GetPropertyValueByPath(ValuePath).Equals(SelectedItem.GetPropertyValueByPath(ValuePath))); } if (item != null) { item.IsSelected = true; UpdateSelectedItems(); } } private void OnItemsSourceChanged() { if (ScrollViewer != null) { ScrollViewer.ScrollToTop(); } } public void ScrollToItem(Object item) { var ee = ScrollViewer.FindVisualChildren(); var element = ScrollViewer.FindVisualChildren().FirstOrDefault(x => x.DataContext == item); ScrollViewer.ScrollToElement(element); } } }