diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-06-12 12:47:24 +0300 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-06-12 12:47:24 +0300 |
| commit | af480467b881106ea03428e16def2b7600a5b75e (patch) | |
| tree | 7dbee6bab9b0641265d2d1bac6973dbbe13462fd /Software/Visual_Studio | |
| parent | 04379edd1bf2835b163c3db786c2273c1ef1bfdc (diff) | |
| download | Tango-af480467b881106ea03428e16def2b7600a5b75e.tar.gz Tango-af480467b881106ea03428e16def2b7600a5b75e.zip | |
Implemented LightTouch ScrollViewer !!
Diffstat (limited to 'Software/Visual_Studio')
8 files changed, 374 insertions, 299 deletions
diff --git a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.cs b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.cs index 1015dee80..eaaa76d79 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.cs +++ b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.cs @@ -26,13 +26,7 @@ namespace Tango.Touch.Controls public class LightTouchDataGrid : Control { private ItemsControl _items_control_rows; - private bool _mouse_down; - private Point _mouse_down_location; - private Grid _grid_rows; - private List<double> _last_manipulation_deltas; private Random _rnd = new Random(); - private const double touch_inertia_coefficiant = 10; - private const double bounce_offset_max = 100; private bool _isLoaded; private List<LightTouchDataGridRow> _awaitingSelectionRows; private LightTouchDataGridRow _firstMultiSelectionRow; @@ -95,17 +89,6 @@ namespace Tango.Touch.Controls DependencyProperty.Register("Columns", typeof(ObservableCollection<LightTouchDataGridColumn>), typeof(LightTouchDataGrid), new PropertyMetadata(new ObservableCollection<LightTouchDataGridColumn>())); /// <summary> - /// Gets or sets a value indicating whether the data grid is currently scrolling. - /// </summary> - public bool IsScrolling - { - get { return (bool)GetValue(IsScrollingProperty); } - set { SetValue(IsScrollingProperty, value); } - } - public static readonly DependencyProperty IsScrollingProperty = - DependencyProperty.Register("IsScrolling", typeof(bool), typeof(LightTouchDataGrid), new PropertyMetadata(false)); - - /// <summary> /// Gets or sets the selection mode. /// </summary> public LightTouchDataGridSelectionMode SelectionMode @@ -138,6 +121,17 @@ namespace Tango.Touch.Controls public static readonly DependencyProperty ItemSelectedCommandProperty = DependencyProperty.Register("ItemSelectedCommand", typeof(RelayCommand<Object>), typeof(LightTouchDataGrid), new PropertyMetadata(null)); + /// <summary> + /// Gets or sets the scroll viewer. + /// </summary> + public LightTouchScrollViewer ScrollViewer + { + get { return (LightTouchScrollViewer)GetValue(ScrollViewerProperty); } + set { SetValue(ScrollViewerProperty, value); } + } + public static readonly DependencyProperty ScrollViewerProperty = + DependencyProperty.Register("ScrollViewer", typeof(LightTouchScrollViewer), typeof(LightTouchDataGrid), new PropertyMetadata(null)); + #endregion @@ -184,7 +178,6 @@ namespace Tango.Touch.Controls public LightTouchDataGrid() { _awaitingSelectionRows = new List<LightTouchDataGridRow>(); - _last_manipulation_deltas = new List<double>(); SortCommand = new RelayCommand<LightTouchDataGridColumn>(HandleSortCommand); Loaded += LightTouchDataGrid_Loaded; } @@ -200,159 +193,17 @@ namespace Tango.Touch.Controls { base.OnApplyTemplate(); _items_control_rows = GetTemplateChild("PART_ItemsControl_Rows") as ItemsControl; - _grid_rows = GetTemplateChild("PART_Grid_Rows") as Grid; + ScrollViewer = GetTemplateChild("PART_ScrollViewer") as LightTouchScrollViewer; DraggingSurface = GetTemplateChild("PART_DraggingSurface") as DraggingSurface; - _grid_rows.IsManipulationEnabled = true; - _grid_rows.RegisterForMouseOrTouchDown(OnMouseTouchDown); - _grid_rows.RegisterForMouseOrTouchMove(OnMouseTouchMove); - _grid_rows.RegisterForPreviewMouseOrTouchUp(OnMouseTouchUp); - - _grid_rows.ManipulationDelta += _grid_rows_ManipulationDelta; - _grid_rows.ManipulationCompleted += _grid_rows_ManipulationCompleted; - _items_control_rows.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; } #endregion - #region Touch Manipulation Handlers - - /// <summary> - /// Handles the ManipulationCompleted event of the _grid_rows control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ManipulationCompletedEventArgs"/> instance containing the event data.</param> - private void _grid_rows_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) - { - if (_last_manipulation_deltas.Count == 0) return; - - var last_manipulation_delta = _last_manipulation_deltas.Last(); - - if (last_manipulation_delta == 0 && _last_manipulation_deltas.Count > 1) - { - last_manipulation_delta = _last_manipulation_deltas[_last_manipulation_deltas.Count - 2]; - } - - if (last_manipulation_delta != 0) - { - var to = (_items_control_rows.Margin.Top + (last_manipulation_delta * touch_inertia_coefficiant)); - - if (to > 0) - { - to = Math.Min(to, bounce_offset_max); - } - else - { - to = Math.Max(to, bounce_offset_max + -(_items_control_rows.ActualHeight - _grid_rows.ActualHeight)); - } - - bool bounced = false; - - ThicknessAnimation ani = new ThicknessAnimation(); - ani.Duration = TimeSpan.FromSeconds(1); - ani.To = new Thickness(0, to, 0, 0); - ani.CurrentTimeInvalidated += (_, __) => - { - if (!bounced) - { - if (_items_control_rows.Margin.Top > 0 || (_items_control_rows.Margin.Top - _grid_rows.ActualHeight < -_items_control_rows.ActualHeight)) - { - bounced = true; - SnapRowsToBounds(); - } - } - }; - ani.DecelerationRatio = 1.0; - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); - _last_manipulation_deltas.Clear(); - } - - ReleaseAllTouchCaptures(); - } - - /// <summary> - /// Handles the ManipulationDelta event of the _grid_rows control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ManipulationDeltaEventArgs"/> instance containing the event data.</param> - private void _grid_rows_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) - { - _last_manipulation_deltas.Add(e.DeltaManipulation.Translation.Y); - } - - #endregion - #region Touch / Mouse Handlers /// <summary> - /// Called when the mouse touch has been down - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The <see cref="MouseOrTouchEventArgs"/> instance containing the event data.</param> - protected virtual void OnMouseTouchDown(object sender, MouseOrTouchEventArgs e) - { - if (e.OriginalSource.GetType() == typeof(DragThumb)) - { - return; - } - - _mouse_down_location = new Point(e.Location.X, e.Location.Y - _items_control_rows.Margin.Top); - _mouse_down = true; - } - - /// <summary> - /// Called when the mouse touch has been move - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The <see cref="MouseOrTouchEventArgs"/> instance containing the event data.</param> - protected virtual void OnMouseTouchMove(object sender, MouseOrTouchEventArgs e) - { - var a = _mouse_down_location.Y + _items_control_rows.Margin.Top; - - if (_mouse_down && Math.Abs((e.Location.Y - a)) > 5) - { - IsScrolling = true; - Mouse.Capture(_grid_rows); - - if (e.TouchDevice != null) - { - e.TouchDevice.Capture(_grid_rows); - } - - _items_control_rows.Margin = new Thickness(0, e.Location.Y - _mouse_down_location.Y, 0, 0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, null); - } - } - - /// <summary> - /// Called when the mouse touch has been up - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The <see cref="MouseOrTouchEventArgs"/> instance containing the event data.</param> - protected virtual void OnMouseTouchUp(object sender, MouseOrTouchEventArgs e) - { - if (_mouse_down) - { - _mouse_down = false; - Mouse.Capture(null); - - if (e.TouchDevice != null) - { - e.TouchDevice.Capture(null); - } - - if (IsScrolling) - { - e.Handled = true; - SnapRowsToBounds(); - } - - IsScrolling = false; - } - } - - /// <summary> /// Called when the row mouse touch has been up /// </summary> /// <param name="sender">The sender.</param> @@ -375,7 +226,7 @@ namespace Tango.Touch.Controls } else if (SelectionMode == LightTouchDataGridSelectionMode.Multiple && IsMultiSelecting) { - if (!IsScrolling) + if (!ScrollViewer.IsScrolling) { if (row != _firstMultiSelectionRow) { @@ -411,7 +262,7 @@ namespace Tango.Touch.Controls { _awaitingSelectionRows.Remove(row); - if (_mouse_down && !IsScrolling) + if (ScrollViewer.IsMouseTouchDown && !ScrollViewer.IsScrolling) { _firstMultiSelectionRow = row; otherRows.ForEach(x => x.IsSelected = false); @@ -488,31 +339,6 @@ namespace Tango.Touch.Controls #endregion - #region Private Methods - - /// <summary> - /// Snaps the items control if they are out of bounds. - /// </summary> - private void SnapRowsToBounds() - { - ThicknessAnimation ani = new ThicknessAnimation(); - ani.Duration = TimeSpan.FromSeconds(0.2); - ani.AccelerationRatio = 1; - - if (_items_control_rows.Margin.Top > 0 || _items_control_rows.ActualHeight < _grid_rows.ActualHeight) - { - ani.To = new Thickness(0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); - } - else if (_items_control_rows.Margin.Top - _grid_rows.ActualHeight < -_items_control_rows.ActualHeight) - { - ani.To = new Thickness(0, -(_items_control_rows.ActualHeight - _grid_rows.ActualHeight), 0, 0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); - } - } - - #endregion - #region Public Methods /// <summary> diff --git a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.xaml b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.xaml index faf1ba3de..ac1be1e62 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.xaml +++ b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchDataGrid.xaml @@ -160,86 +160,87 @@ </local:LightTouchDataGridHeaderRow> <!--Rows--> - <Grid x:Name="PART_Grid_Rows" ClipToBounds="True" Background="Transparent"> - <ItemsControl ItemsSource="{TemplateBinding ItemsSource}" x:Name="PART_ItemsControl_Rows" ClipToBounds="True" VerticalAlignment="Top"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <Canvas ClipToBounds="True" IsItemsHost="True" VirtualizingPanel.IsVirtualizing="True"> - </Canvas> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemContainerStyle> - <Style TargetType="FrameworkElement"> - <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=ActualWidth}"></Setter> - </Style> - </ItemsControl.ItemContainerStyle> - <ItemsControl.ItemTemplate> - <DataTemplate> - <local:LightTouchDataGridRow RenderTransformOrigin="0.5,0.5" + <Grid> + <local:LightTouchScrollViewer x:Name="PART_ScrollViewer"> + <ItemsControl ItemsSource="{TemplateBinding ItemsSource}" x:Name="PART_ItemsControl_Rows" ClipToBounds="True" VerticalAlignment="Top"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <Canvas ClipToBounds="True" IsItemsHost="True" VirtualizingPanel.IsVirtualizing="True"> + </Canvas> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemContainerStyle> + <Style TargetType="FrameworkElement"> + <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=ActualWidth}"></Setter> + </Style> + </ItemsControl.ItemContainerStyle> + <ItemsControl.ItemTemplate> + <DataTemplate> + <local:LightTouchDataGridRow RenderTransformOrigin="0.5,0.5" dragAndDrop:DragAndDropService.Draggable="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=EnableDragAndDrop}" dragAndDrop:DragAndDropService.Droppable="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=EnableDragAndDrop}" dragAndDrop:DragAndDropService.DraggingSurface="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=DraggingSurface}" dragAndDrop:DragAndDropService.MinDragOffset="2" Padding="0" Canvas.Top="{Binding RelativeSource={RelativeSource AncestorType=ContentPresenter},Path=(Canvas.Top),Mode=TwoWay}"> - <components:Ripple RippleFactor="20" RippleBrush="{StaticResource TangoRippleDarkBrush}" CornerRadius="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGridRow},Path=CornerRadius}"> - <components:Ripple.Style> - <Style TargetType="components:Ripple"> - <Setter Property="Disabled" Value="False"></Setter> - <Style.Triggers> - <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=IsScrolling}" Value="True"> - <Setter Property="Disabled" Value="True"></Setter> - </DataTrigger> - <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=IsMultiSelecting}" Value="True"> - <Setter Property="Disabled" Value="True"></Setter> - </DataTrigger> - </Style.Triggers> - </Style> - </components:Ripple.Style> - <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=Columns}"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <Grid> - <Grid.Tag> - <MultiBinding Converter="{StaticResource ColumnsToGridDefinitionsConverter}"> - <Binding RelativeSource="{RelativeSource Self}" Path="." /> - <Binding RelativeSource="{RelativeSource AncestorType=local:LightTouchDataGrid}" Path="Columns"/> - </MultiBinding> - </Grid.Tag> - </Grid> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemContainerStyle> - <Style TargetType="FrameworkElement"> - <Setter Property="Grid.Column"> - <Setter.Value> - <MultiBinding Converter="{StaticResource ColumnToColumnIndexConverter}"> - <Binding RelativeSource="{RelativeSource AncestorType=local:LightTouchDataGrid}" Path="Columns" /> - <Binding Path="." /> - </MultiBinding> - </Setter.Value> - </Setter> + <components:Ripple RippleFactor="20" RippleBrush="{StaticResource TangoRippleDarkBrush}" CornerRadius="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGridRow},Path=CornerRadius}"> + <components:Ripple.Style> + <Style TargetType="components:Ripple"> + <Setter Property="Disabled" Value="False"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=ScrollViewer.IsScrolling}" Value="True"> + <Setter Property="Disabled" Value="True"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=IsMultiSelecting}" Value="True"> + <Setter Property="Disabled" Value="True"></Setter> + </DataTrigger> + </Style.Triggers> </Style> - </ItemsControl.ItemContainerStyle> - <ItemsControl.ItemTemplate> - <DataTemplate> - <local:LightTouchDataGridCell> - <local:LightTouchDataGridCell.Style> - <Style TargetType="local:LightTouchDataGridCell" BasedOn="{StaticResource {x:Type local:LightTouchDataGridCell}}"> - <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment}"></Setter> - <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment}"></Setter> - </Style> - </local:LightTouchDataGridCell.Style> - <ContentControl Content="{Binding RelativeSource={RelativeSource AncestorType=ItemsPresenter},Path=DataContext}" ContentTemplate="{Binding CellTemplate}" VerticalContentAlignment="{Binding VerticalContentAlignment}" HorizontalContentAlignment="{Binding HorizontalContentAlignment}" /> - </local:LightTouchDataGridCell> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </components:Ripple> - </local:LightTouchDataGridRow> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - + </components:Ripple.Style> + <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:LightTouchDataGrid},Path=Columns}"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <Grid> + <Grid.Tag> + <MultiBinding Converter="{StaticResource ColumnsToGridDefinitionsConverter}"> + <Binding RelativeSource="{RelativeSource Self}" Path="." /> + <Binding RelativeSource="{RelativeSource AncestorType=local:LightTouchDataGrid}" Path="Columns"/> + </MultiBinding> + </Grid.Tag> + </Grid> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemContainerStyle> + <Style TargetType="FrameworkElement"> + <Setter Property="Grid.Column"> + <Setter.Value> + <MultiBinding Converter="{StaticResource ColumnToColumnIndexConverter}"> + <Binding RelativeSource="{RelativeSource AncestorType=local:LightTouchDataGrid}" Path="Columns" /> + <Binding Path="." /> + </MultiBinding> + </Setter.Value> + </Setter> + </Style> + </ItemsControl.ItemContainerStyle> + <ItemsControl.ItemTemplate> + <DataTemplate> + <local:LightTouchDataGridCell> + <local:LightTouchDataGridCell.Style> + <Style TargetType="local:LightTouchDataGridCell" BasedOn="{StaticResource {x:Type local:LightTouchDataGridCell}}"> + <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment}"></Setter> + <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment}"></Setter> + </Style> + </local:LightTouchDataGridCell.Style> + <ContentControl Content="{Binding RelativeSource={RelativeSource AncestorType=ItemsPresenter},Path=DataContext}" ContentTemplate="{Binding CellTemplate}" VerticalContentAlignment="{Binding VerticalContentAlignment}" HorizontalContentAlignment="{Binding HorizontalContentAlignment}" /> + </local:LightTouchDataGridCell> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </components:Ripple> + </local:LightTouchDataGridRow> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </local:LightTouchScrollViewer> <dragAndDrop:DraggingSurface x:Name="PART_DraggingSurface" /> </Grid> </DockPanel> diff --git a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.cs b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.cs index 8c9410586..eda8b759f 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.cs +++ b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.cs @@ -5,7 +5,9 @@ using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Animation; using Tango.Core.EventArguments; using Tango.DragAndDrop; @@ -14,13 +16,14 @@ namespace Tango.Touch.Controls { public class LightTouchScrollViewer : ContentControl { - private Border _grid_rows; - private Grid _items_control_rows; - private bool _mouse_down; + private Border _border_viewport; + private Grid _grid_content; private Point _mouse_down_location; private List<double> _last_manipulation_deltas; private const double touch_inertia_coefficiant = 10; private const double bounce_offset_max = 100; + private Thumb _thumb; + private Border _border_thumb; #region Properties @@ -35,6 +38,67 @@ namespace Tango.Touch.Controls public static readonly DependencyProperty IsScrollingProperty = DependencyProperty.Register("IsScrolling", typeof(bool), typeof(LightTouchScrollViewer), new PropertyMetadata(false)); + + /// <summary> + /// Gets or sets a value indicating whether this instance is mouse touch down. + /// </summary> + public bool IsMouseTouchDown + { + get { return (bool)GetValue(IsMouseTouchDownProperty); } + set { SetValue(IsMouseTouchDownProperty, value); } + } + public static readonly DependencyProperty IsMouseTouchDownProperty = + DependencyProperty.Register("IsMouseTouchDown", typeof(bool), typeof(LightTouchScrollViewer), new PropertyMetadata(false)); + + /// <summary> + /// Gets or sets the width of the scroll bar. + /// </summary> + /// <value> + /// The width of the scroll bar. + /// </value> + public double ScrollBarWidth + { + get { return (double)GetValue(ScrollBarWidthProperty); } + set { SetValue(ScrollBarWidthProperty, value); } + } + public static readonly DependencyProperty ScrollBarWidthProperty = + DependencyProperty.Register("ScrollBarWidth", typeof(double), typeof(LightTouchScrollViewer), new PropertyMetadata(5.0)); + + /// <summary> + /// Gets or sets the scroll bar background. + /// </summary> + public Brush ScrollBarBackground + { + get { return (Brush)GetValue(ScrollBarBackgroundProperty); } + set { SetValue(ScrollBarBackgroundProperty, value); } + } + public static readonly DependencyProperty ScrollBarBackgroundProperty = + DependencyProperty.Register("ScrollBarBackground", typeof(Brush), typeof(LightTouchScrollViewer), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the scroll bar foreground. + /// </summary> + public Brush ScrollBarForeground + { + get { return (Brush)GetValue(ScrollBarForegroundProperty); } + set { SetValue(ScrollBarForegroundProperty, value); } + } + public static readonly DependencyProperty ScrollBarForegroundProperty = + DependencyProperty.Register("ScrollBarForeground", typeof(Brush), typeof(LightTouchScrollViewer), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the scroll bar corner radius. + /// </summary> + public CornerRadius ScrollBarCornerRadius + { + get { return (CornerRadius)GetValue(ScrollBarCornerRadiusProperty); } + set { SetValue(ScrollBarCornerRadiusProperty, value); } + } + public static readonly DependencyProperty ScrollBarCornerRadiusProperty = + DependencyProperty.Register("ScrollBarCornerRadius", typeof(CornerRadius), typeof(LightTouchScrollViewer), new PropertyMetadata(default(CornerRadius))); + + + #endregion #region Constructors @@ -57,16 +121,60 @@ namespace Tango.Touch.Controls { base.OnApplyTemplate(); - _grid_rows = GetTemplateChild("PART_Border") as Border; - _items_control_rows = GetTemplateChild("PART_Grid_Content") as Grid; + _border_viewport = GetTemplateChild("PART_Border") as Border; + _grid_content = GetTemplateChild("PART_Grid_Content") as Grid; + _thumb = GetTemplateChild("PART_Thumb") as Thumb; + _border_thumb = GetTemplateChild("PART_Thumb_Border") as Border; + + ContentPresenter presenter = GetTemplateChild("PART_Content_Presenter") as ContentPresenter; + FrameworkElement firstChild = presenter.Content as FrameworkElement; + + _grid_content.Bind(Grid.HeightProperty, firstChild, FrameworkElement.HeightProperty); + + _border_viewport.IsManipulationEnabled = true; + _border_viewport.RegisterForMouseOrTouchDown(OnMouseTouchDown); + _border_viewport.RegisterForMouseOrTouchMove(OnMouseTouchMove); + _border_viewport.RegisterForPreviewMouseOrTouchUp(OnMouseTouchUp); - _grid_rows.IsManipulationEnabled = true; - _grid_rows.RegisterForMouseOrTouchDown(OnMouseTouchDown); - _grid_rows.RegisterForMouseOrTouchMove(OnMouseTouchMove); - _grid_rows.RegisterForPreviewMouseOrTouchUp(OnMouseTouchUp); + _border_viewport.ManipulationDelta += _grid_rows_ManipulationDelta; + _border_viewport.ManipulationCompleted += _grid_rows_ManipulationCompleted; - _grid_rows.ManipulationDelta += _grid_rows_ManipulationDelta; - _grid_rows.ManipulationCompleted += _grid_rows_ManipulationCompleted; + _thumb.DragDelta += _thumb_DragDelta; + _thumb.DragStarted += _thumb_DragStarted; + _thumb.DragCompleted += _thumb_DragCompleted; + } + + private void _thumb_DragCompleted(object sender, DragCompletedEventArgs e) + { + IsScrolling = false; + } + + private void _thumb_DragStarted(object sender, DragStartedEventArgs e) + { + IsScrolling = true; + } + + private void _thumb_DragDelta(object sender, DragDeltaEventArgs e) + { + var step = CalculateScrollbarThumbStep(); + + double content_margin = _grid_content.Margin.Top + -(step * e.VerticalChange); + + _grid_content.BeginAnimation(Grid.MarginProperty, null); + + Canvas.SetTop(_border_thumb, Canvas.GetTop(_border_thumb) + e.VerticalChange); + _grid_content.Margin = new Thickness(0, content_margin, 0, 0); + + if (content_margin > 0) + { + Canvas.SetTop(_border_thumb, 0); + _grid_content.Margin = new Thickness(0, 0, 0, 0); + } + else if (content_margin - _border_viewport.ActualHeight < -_grid_content.ActualHeight) + { + Canvas.SetTop(_border_thumb, _border_viewport.ActualHeight - _border_thumb.ActualHeight); + _grid_content.Margin = new Thickness(0, -(_grid_content.ActualHeight - _border_viewport.ActualHeight), 0, 0); + } } #endregion @@ -91,7 +199,7 @@ namespace Tango.Touch.Controls if (last_manipulation_delta != 0) { - var to = (_items_control_rows.Margin.Top + (last_manipulation_delta * touch_inertia_coefficiant)); + var to = (_grid_content.Margin.Top + (last_manipulation_delta * touch_inertia_coefficiant)); if (to > 0) { @@ -99,7 +207,7 @@ namespace Tango.Touch.Controls } else { - to = Math.Max(to, bounce_offset_max + -(_items_control_rows.ActualHeight - _grid_rows.ActualHeight)); + to = Math.Max(to, bounce_offset_max + -(_grid_content.ActualHeight - _border_viewport.ActualHeight)); } bool bounced = false; @@ -111,15 +219,17 @@ namespace Tango.Touch.Controls { if (!bounced) { - if (_items_control_rows.Margin.Top > 0 || (_items_control_rows.Margin.Top - _grid_rows.ActualHeight < -_items_control_rows.ActualHeight)) + if (_grid_content.Margin.Top > 0 || (_grid_content.Margin.Top - _border_viewport.ActualHeight < -_grid_content.ActualHeight)) { bounced = true; SnapRowsToBounds(); } } + + SetThumbPosition(); }; ani.DecelerationRatio = 1.0; - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); + _grid_content.BeginAnimation(Grid.MarginProperty, ani); _last_manipulation_deltas.Clear(); } @@ -152,8 +262,8 @@ namespace Tango.Touch.Controls return; } - _mouse_down_location = new Point(e.Location.X, e.Location.Y - _items_control_rows.Margin.Top); - _mouse_down = true; + _mouse_down_location = new Point(e.Location.X, e.Location.Y - _grid_content.Margin.Top); + IsMouseTouchDown = true; } /// <summary> @@ -163,20 +273,22 @@ namespace Tango.Touch.Controls /// <param name="e">The <see cref="MouseOrTouchEventArgs"/> instance containing the event data.</param> protected virtual void OnMouseTouchMove(object sender, MouseOrTouchEventArgs e) { - var a = _mouse_down_location.Y + _items_control_rows.Margin.Top; + var a = _mouse_down_location.Y + _grid_content.Margin.Top; - if (_mouse_down && Math.Abs((e.Location.Y - a)) > 5) + if (IsMouseTouchDown && Math.Abs((e.Location.Y - a)) > 5) { IsScrolling = true; - Mouse.Capture(_grid_rows); + Mouse.Capture(_border_viewport); if (e.TouchDevice != null) { - e.TouchDevice.Capture(_grid_rows); + e.TouchDevice.Capture(_border_viewport); } - _items_control_rows.Margin = new Thickness(0, e.Location.Y - _mouse_down_location.Y, 0, 0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, null); + _grid_content.Margin = new Thickness(0, e.Location.Y - _mouse_down_location.Y, 0, 0); + _grid_content.BeginAnimation(Grid.MarginProperty, null); + + SetThumbPosition(); } } @@ -187,9 +299,9 @@ namespace Tango.Touch.Controls /// <param name="e">The <see cref="MouseOrTouchEventArgs"/> instance containing the event data.</param> protected virtual void OnMouseTouchUp(object sender, MouseOrTouchEventArgs e) { - if (_mouse_down) + if (IsMouseTouchDown) { - _mouse_down = false; + IsMouseTouchDown = false; Mouse.Capture(null); if (e.TouchDevice != null) @@ -220,15 +332,41 @@ namespace Tango.Touch.Controls ani.Duration = TimeSpan.FromSeconds(0.2); ani.AccelerationRatio = 1; - if (_items_control_rows.Margin.Top > 0 || _items_control_rows.ActualHeight < _grid_rows.ActualHeight) + if (_grid_content.Margin.Top > 0 || _grid_content.ActualHeight < _border_viewport.ActualHeight) { ani.To = new Thickness(0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); + _grid_content.BeginAnimation(Grid.MarginProperty, ani); + } + else if (_grid_content.Margin.Top - _border_viewport.ActualHeight < -_grid_content.ActualHeight) + { + ani.To = new Thickness(0, -(_grid_content.ActualHeight - _border_viewport.ActualHeight), 0, 0); + _grid_content.BeginAnimation(Grid.MarginProperty, ani); + } + } + + private double CalculateScrollbarThumbStep() + { + var scrollTrackSpace = _grid_content.ActualHeight - _border_viewport.ActualHeight; + var scrollThumbSpace = _border_viewport.ActualHeight - _thumb.ActualHeight; + var scrollJump = scrollTrackSpace / scrollThumbSpace; + return scrollJump; + } + + private void SetThumbPosition() + { + double step = CalculateScrollbarThumbStep(); + double content_margin = _grid_content.Margin.Top; + double thumb_top = -(content_margin / step); + + Canvas.SetTop(_border_thumb, thumb_top); + + if (thumb_top < 0) + { + Canvas.SetTop(_border_thumb, 0); } - else if (_items_control_rows.Margin.Top - _grid_rows.ActualHeight < -_items_control_rows.ActualHeight) + else if (thumb_top + _border_thumb.ActualHeight > _border_viewport.ActualHeight) { - ani.To = new Thickness(0, -(_items_control_rows.ActualHeight - _grid_rows.ActualHeight), 0, 0); - _items_control_rows.BeginAnimation(ItemsControl.MarginProperty, ani); + Canvas.SetTop(_border_thumb, _border_viewport.ActualHeight - _border_thumb.ActualHeight); } } diff --git a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.xaml b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.xaml index df0e7a434..a1873acca 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.xaml +++ b/Software/Visual_Studio/Tango.Touch/Controls/LightTouchScrollViewer.xaml @@ -1,20 +1,75 @@ <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="clr-namespace:Tango.Touch.Converters" + xmlns:dragAndDrop="clr-namespace:Tango.DragAndDrop;assembly=Tango.DragAndDrop" xmlns:local="clr-namespace:Tango.Touch.Controls"> + <ResourceDictionary.MergedDictionaries> + <ResourceDictionary> + <converters:ScrollViewerThumbSizeConverter x:Key="ScrollViewerThumbSizeConverter" /> + <converters:ScrollViewerScrollBarVisibilityConverter x:Key="ScrollViewerScrollBarVisibilityConverter" /> + </ResourceDictionary> + </ResourceDictionary.MergedDictionaries> <Style TargetType="{x:Type local:LightTouchScrollViewer}"> + <Setter Property="ScrollBarBackground" Value="#33000000"></Setter> + <Setter Property="ScrollBarForeground" Value="#90000000"></Setter> + <Setter Property="ScrollBarWidth" Value="5"></Setter> + <Setter Property="ScrollBarCornerRadius" Value="3"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:LightTouchScrollViewer}"> - <Border x:Name="PART_Border" ClipToBounds="True" Background="Transparent"> - <Grid x:Name="PART_Grid_Content" ClipToBounds="True" Background="Transparent"> - <ContentPresenter Content="{TemplateBinding Content}" /> - </Grid> - </Border> + <Grid> + <Border x:Name="PART_Border" ClipToBounds="True" Background="Transparent"> + <Grid x:Name="PART_Grid_Content" Background="Transparent" VerticalAlignment="Top"> + <ContentPresenter x:Name="PART_Content_Presenter" Content="{TemplateBinding Content}" VerticalAlignment="Top" Height="Auto" /> + </Grid> + </Border> + + <Canvas Margin="0 0 5 0" x:Name="PART_canvas" HorizontalAlignment="Right" VerticalAlignment="Stretch" Width="{TemplateBinding ScrollBarWidth}" Background="{TemplateBinding ScrollBarBackground}"> + <Canvas.Visibility> + <MultiBinding Converter="{StaticResource ScrollViewerScrollBarVisibilityConverter}"> + <Binding ElementName="PART_Border" Path="ActualHeight" /> + <Binding ElementName="PART_Grid_Content" Path="ActualHeight" /> + </MultiBinding> + </Canvas.Visibility> + <Canvas.Style> + <Style TargetType="Canvas"> + <Setter Property="Opacity" Value="0"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsScrolling}" Value="True"> + <DataTrigger.EnterActions> + <BeginStoryboard> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:0.2"/> + </Storyboard> + </BeginStoryboard> + </DataTrigger.EnterActions> + <DataTrigger.ExitActions> + <BeginStoryboard> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" BeginTime="00:00:01" Duration="00:00:01"/> + </Storyboard> + </BeginStoryboard> + </DataTrigger.ExitActions> + </DataTrigger> + </Style.Triggers> + </Style> + </Canvas.Style> + <Border x:Name="PART_Thumb_Border" CornerRadius="{TemplateBinding ScrollBarCornerRadius}" Canvas.Top="0" Width="{Binding ElementName=PART_canvas,Path=ActualWidth}" Background="{TemplateBinding ScrollBarForeground}"> + <Border.Height> + <MultiBinding Converter="{StaticResource ScrollViewerThumbSizeConverter}"> + <Binding ElementName="PART_Border" Path="ActualHeight" /> + <Binding ElementName="PART_Grid_Content" Path="ActualHeight" /> + </MultiBinding> + </Border.Height> + <Thumb x:Name="PART_Thumb" Opacity="0" Background="Transparent" /> + </Border> + </Canvas> + </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> - + </ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerScrollBarVisibilityConverter.cs b/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerScrollBarVisibilityConverter.cs new file mode 100644 index 000000000..d477feeb3 --- /dev/null +++ b/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerScrollBarVisibilityConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace Tango.Touch.Converters +{ + public class ScrollViewerScrollBarVisibilityConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + double scroll_viewer_height = (double)values[0]; + double content_height = (double)values[1]; + return content_height > scroll_viewer_height ? Visibility.Visible : Visibility.Collapsed; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerThumbSizeConverter.cs b/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerThumbSizeConverter.cs new file mode 100644 index 000000000..5307f6509 --- /dev/null +++ b/Software/Visual_Studio/Tango.Touch/Converters/ScrollViewerThumbSizeConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Tango.Touch.Converters +{ + public class ScrollViewerThumbSizeConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + double scroll_viewer_height = (double)values[0]; + double content_height = (double)values[1]; + + var viewableRatio = scroll_viewer_height / content_height; + return scroll_viewer_height * viewableRatio; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj b/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj index d86cedc83..00f8b65fb 100644 --- a/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj +++ b/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj @@ -174,6 +174,8 @@ <Compile Include="Converters\NotZeroConverter.cs" /> <Compile Include="Converters\RotateTransformCentreConverter.cs" /> <Compile Include="Converters\RotateTransformConverter.cs" /> + <Compile Include="Converters\ScrollViewerScrollBarVisibilityConverter.cs" /> + <Compile Include="Converters\ScrollViewerThumbSizeConverter.cs" /> <Compile Include="Converters\SizeToRectConverter.cs" /> <Compile Include="Converters\StartPointConverter.cs" /> <Compile Include="Converters\WidthHeightToRectConverter.cs" /> diff --git a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml index cfea9140c..df8073e23 100644 --- a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml +++ b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml @@ -10,7 +10,7 @@ xmlns:keyboard="clr-namespace:Tango.Touch.Keyboard;assembly=Tango.Touch" xmlns:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch" mc:Ignorable="d" - Title="MainWindow" Height="800" Width="800" DataContext="{Binding RelativeSource={RelativeSource Self}}" Background="{StaticResource TangoMidBackgroundBrush}"> + Title="MainWindow" Height="1000" Width="800" DataContext="{Binding RelativeSource={RelativeSource Self}}" Background="{StaticResource TangoMidBackgroundBrush}"> <Grid> |
