Scorpunduk

TabPanelSample

Sep 22nd, 2021
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.80 KB | None | 0 0
  1. /// <summary>
  2.     /// Логика взаимодействия для TabPanel.xaml
  3.     /// </summary>
  4.     public partial class TabPanel : UserControl
  5.     {
  6.         public TabPanel()
  7.         {
  8.             InitializeComponent();
  9.             SizeChanged += TabPanel_SizeChanged;
  10.             KeyDown += TabPanel_KeyDown;  
  11.         }
  12.  
  13.         private void TabPanel_KeyDown(object sender, KeyEventArgs e)
  14.         {
  15.            if(e.KeyboardDevice.Modifiers == ModifierKeys.Shift && e.Key == Key.S)
  16.            {
  17.                 ScrollInfo();
  18.            }
  19.         }
  20.  
  21.         private void TabPanel_SizeChanged(object sender, SizeChangedEventArgs e)
  22.         {
  23.             SetArrowsVisibility();
  24.         }
  25.        
  26.         // активная вкладка
  27.         private UserControl activeTab;
  28.         public UserControl ActiveTab
  29.         {
  30.             get { return activeTab; }
  31.             set { activeTab = value; }
  32.         }
  33.  
  34.         // возврат текущего количества вкладок на ленте
  35.         private int tabCount;
  36.         public int TabCount
  37.         {
  38.             get
  39.             {
  40.                 tabCount = tabContainer.Children.Count;
  41.                 return tabCount;
  42.             }            
  43.         }
  44.  
  45.         public Action<UserControl> ActiveTabLastAction;
  46.         public Action<UserControl> ActiveTabFirstAction;
  47.        
  48.         /* метод для пометки вкладки на ленте как активной через заполнение соответствующего поля
  49.          * также отрабатывают два метода, переданных с делегатами, влияющих на состояние
  50.          * вкладки, теряющей активный статус и
  51.          * вкладки, приобретающей активный статус
  52.          * например, стиль кнопки и т.п.
  53.          * здесь методы в аргументах, и это предпочтительно
  54.          */
  55.         public void SetActiveTab(UserControl newActiveTab, Action<UserControl> ActiveTabLastAction, Action<UserControl> ActiveTabFirstAction)
  56.         {
  57.             if (ContainsTab(newActiveTab))
  58.             {
  59.                 if (ActiveTab != null)
  60.                 {                    
  61.                     ActiveTabLastAction?.Invoke(ActiveTab);
  62.                 }
  63.                 ActiveTab = newActiveTab;
  64.                 ActiveTabFirstAction?.Invoke(ActiveTab);
  65.             }
  66.         }
  67.  
  68.         /* метод для пометки вкладки на ленте как активной через заполнение соответствующего поля
  69.          * также отрабатывают два метода, переданных с делегатами, влияющих на состояние
  70.          * вкладки, теряющей активный статус и
  71.          * вкладки, приобретающей активный статус
  72.          * здесь взяты методы из полей самого класса ленты с вкладками
  73.          * присвоение происходит в конструкторе родительского окна или контрола с лентой вкладок
  74.          */
  75.         public void SetActiveTab(UserControl newActiveTab)
  76.         {
  77.             SetActiveTab(newActiveTab, ActiveTabLastAction, ActiveTabFirstAction);
  78.         }
  79.  
  80.         /* метод для добавления новой вкладки на ленту
  81.          */
  82.         public UserControl AddNewTab(UserControl tab)
  83.         {            
  84.             tabContainer.Children.Add(tab);
  85.             tab.SizeChanged += Tab_SizeChanged;
  86.             tab.MouseLeftButtonUp += SwipeOnPartialHide;
  87.             SetArrowsVisibility();
  88.  
  89.                 double sum = 0;
  90.                 double[] swipeSteps = GetSwipeSteps();
  91.                 for(int i = 0; i < swipeSteps.Length; i++)
  92.                 {
  93.                     sum += swipeSteps[i];
  94.                 }
  95.  
  96.                 tabScroll.ScrollToHorizontalOffset(sum);
  97.  
  98.             return tab;
  99.         }
  100.  
  101.  
  102.         /* методя для удаления вкладки с ленты
  103.          */
  104.         public void RemoveTab(UserControl tab)
  105.         {
  106.             tab.SizeChanged -= Tab_SizeChanged;
  107.             tabContainer.Children.Remove(tab);
  108.             if (tab == activeTab) activeTab = null;
  109.             SetArrowsVisibility();
  110.         }
  111.  
  112.         /* данный метод-обработчик события изменения размера контрола
  113.          * пришлось ввести так как ActualWidth вкладки
  114.          * меняется не сразу. В тот момент, когда он должен быть известен
  115.          * он всё ещё равняется нулю. Соответственно, метод отслеживающий
  116.          * момент переполнения видимой области ленты вкладок неверно
  117.          * определяет когда должны появиться кнопки навигации по ленте
  118.          * опоздание на одну вкладку
  119.          */
  120.         private void Tab_SizeChanged(object sender, SizeChangedEventArgs e)
  121.         {
  122.             SetArrowsVisibility();
  123.             UserControl tab = sender as UserControl;            
  124.         }
  125.  
  126.        
  127.         /* метод для скрытия вкладки на ленте без удаления
  128.          * хз пока зачем он нужен, больше похоже на древний артефакт
  129.          */
  130.         public void HideTab(UserControl tab)
  131.         {
  132.             tab.Visibility = Visibility.Collapsed;
  133.         }
  134.  
  135.         /* получить вкладку из ленты по индексу
  136.          */
  137.         public UserControl GetTab(int index)
  138.         {
  139.             if(tabContainer.Children.Count > 0)
  140.             {
  141.                 if(index < tabContainer.Children.Count) return tabContainer.Children[index] as UserControl;
  142.                 else
  143.                 {
  144.                     return null;
  145.                 }
  146.             }
  147.             else
  148.             {
  149.                 return null;
  150.             }
  151.         }
  152.  
  153.         /* поскольку класс TabPanel является лишь интерфейсом для организации
  154.          * элементов, может существовать внешний источник с элементами
  155.          * и может быть ситуация, когда надо проводить есть ли элемент в ленте
  156.          */
  157.         public bool ContainsTab(UserControl tab)
  158.         {
  159.             return tabContainer.Children.Contains(tab);
  160.         }
  161.  
  162.         /* получить индекс элемента
  163.          * можно задавать как
  164.          */
  165.         public int GetTabIndex(UserControl tab)
  166.         {
  167.             return tabContainer.Children.IndexOf(tab);
  168.         }
  169.  
  170.         /* определение является ли элемент активной вкладкой
  171.          */
  172.         public bool IsActiveTab(UserControl tabToCompare)
  173.         {
  174.             if (activeTab == null) return false;
  175.             return activeTab == tabToCompare;
  176.         }
  177.        
  178.        
  179.         /* метод для получения общей суммарной ширины всех элементов в
  180.          * ряду вкладок
  181.          */
  182.         private double GetTotalWidth()
  183.         {
  184.             double totalWidth = 0;          
  185.             foreach (UserControl item in tabContainer.Children)
  186.             {                
  187.                 totalWidth += item.ActualWidth;                
  188.             }
  189.             return totalWidth;
  190.         }
  191.  
  192.         /* свойство для возврата значения отступа на ScrollView назад
  193.          * в зависимости от текущего отступа и длины вкладки
  194.          */
  195.         public double OffsetBack
  196.         {
  197.             get
  198.             {
  199.                 double sum = 0;
  200.                 double[] swipePositions = GetSwipeSteps();
  201.                 for (int i = 0; i < swipePositions.Length; i++)
  202.                 {
  203.                     sum += swipePositions[i];
  204.                     if (sum >= tabScroll.HorizontalOffset)
  205.                     {
  206.                         return sum - swipePositions[i];
  207.                     }
  208.                 }
  209.                 return 0;
  210.             }
  211.         }
  212.  
  213.         /* свойство для возврата значения отступа ScrollView вперёд
  214.          * в зависимости от текущего отступа и длины вкладок
  215.          */
  216.         public double OffsetForward
  217.         {
  218.             get
  219.             {
  220.                 double sum = 0;
  221.                 double[] swipePositions = GetSwipeSteps();
  222.                 for (int i = 0; i < swipePositions.Length; i++)
  223.                 {
  224.                     sum += swipePositions[i];
  225.                     if (sum > tabScroll.HorizontalOffset)
  226.                     {
  227.                         return sum;
  228.  
  229.                     }
  230.                 }
  231.                 return 0;
  232.             }
  233.         }
  234.  
  235.         /* получить список длин всех вкладок в ленте
  236.          */
  237.         private double[] GetSwipeSteps()
  238.         {
  239.             int index = 0;
  240.             double[] swipePositions = new double[tabContainer.Children.Count];
  241.             foreach (UserControl item in tabContainer.Children)
  242.             {
  243.                 swipePositions[index] = item.ActualWidth;
  244.                 index++;
  245.             }
  246.             return swipePositions;
  247.         }
  248.  
  249.         /* условие для видимости стрелок прокрутки вкладок
  250.          * когда общая длина вкладок больше, чем видимая область ScrollView
  251.          * стрелки появляются
  252.          * и наоборот
  253.          */
  254.         public void SetArrowsVisibility()
  255.         {            
  256.             if (GetTotalWidth() >= tabScroll.ActualWidth) SwipeArrowsVisible = true;
  257.             else SwipeArrowsVisible = false;
  258.         }
  259.  
  260.         /* сдвижка scrollview влево
  261.          */
  262.         private void LeftTabSwipe_Click(object sender, RoutedEventArgs e)
  263.         {
  264.             try
  265.             {
  266.                 tabScroll.ScrollToHorizontalOffset(OffsetBack);
  267.             }
  268.             catch
  269.             {
  270.            
  271.             }
  272.         }
  273.  
  274.         /* сдвижка scrollview вправо
  275.          */
  276.         private void RightTabSwipe_Click(object sender, RoutedEventArgs e)
  277.         {
  278.             try
  279.             {                
  280.                 tabScroll.ScrollToHorizontalOffset(OffsetForward);                
  281.             }
  282.             catch
  283.             {
  284.            
  285.             }
  286.         }
  287.  
  288.         private void SwipeOnPartialHide(object sender, MouseButtonEventArgs e)
  289.         {
  290.             UserControl tab = sender as UserControl;
  291.             int index = GetTabIndex(tab);
  292.             double[] swipeSteps = GetSwipeSteps();
  293.  
  294.             double sum = 0;
  295.  
  296.             for(int i = 0; i < swipeSteps.Length; i++)
  297.             {
  298.                 sum += swipeSteps[i];
  299.                 if( i == index)
  300.                 {
  301.                     if (tabScroll.HorizontalOffset < sum && tabScroll.HorizontalOffset > sum - swipeSteps[i])
  302.                         tabScroll.ScrollToHorizontalOffset(sum - swipeSteps[i]);
  303.  
  304.  
  305.                     double offsetPlusWidth = tabScroll.HorizontalOffset + tabScroll.ActualWidth;
  306.  
  307.                     if(offsetPlusWidth > sum - swipeSteps[i] && offsetPlusWidth < sum)
  308.                     {
  309.                         tabScroll.ScrollToHorizontalOffset(tabScroll.HorizontalOffset + (sum - offsetPlusWidth));
  310.                     }
  311.                        
  312.                 }
  313.             }
  314.  
  315.         }
  316.        
  317.  
  318.         #region test control methods
  319.  
  320.         private void ScrollInfo()
  321.         {
  322.             MessageBox.Show($"Total tab width = {GetTotalWidth()} \n " +
  323.                 $"{nameof(tabScroll.ActualWidth)} = {tabScroll.ActualWidth} \n " +
  324.                 $"{nameof(tabScroll.HorizontalOffset)} = {tabScroll.HorizontalOffset} \n " +
  325.                 //$"{nameof(currentSwipe)} = {currentSwipe} \n " +
  326.                 $"{nameof(tabScroll.ExtentWidth)} - {tabScroll.ExtentWidth} \n" +
  327.                 $"{nameof(tabScroll.ContentHorizontalOffset)} - {tabScroll.ContentHorizontalOffset} \n" +
  328.                 $"{nameof(tabScroll.ScrollableWidth)} = {tabScroll.ScrollableWidth} \n ");
  329.  
  330.         }
  331.  
  332.         #endregion
  333.     }
Advertisement
Add Comment
Please, Sign In to add comment