原文:WPF Timeline简易时间轴控件的实现

效果图:

由于整个控件是实现之后才写的教程,因此这里记录的代码是最终实现后的,前后会引用到其他的一些依赖属性或者代码,需要阅读整篇文章。

1、确定Timeline继承的基类

从效果图中可以看到,时间轴都是由一节一节的子节点组成的,这个很容易联想到我们应该将Timeline继承自ItemsControl。之外仔细观察效果图,可以发现第一项的时间轴节点与其他都不同,而且拆解每一个子项,发现都是由一个圆圈和一个竖线组成,但是最后一项和上面的都不同,少了一个竖线,因此为了控制这些样式,我们需要重新定义一个TimelineItem,将其继承自ContentControl,来重新定义逻辑。

2、具体实现

2.1、TimelineItem的具体实现
2.1.1、设计思路
2.1.1.1、为了能确定当前子项所处的位置(是第一项、中间项还是最后一项),我能想到的有下面2种实现方式
①、使用Converter转换器,将当前Item传入后台的转换器种,通过ItemsControl自带的【ItemContainerGenerator.IndexFromContainer】方法获取到当前Item的Index,然后将Index与ItemsControl的Items进行比较,判断当前Item的所处位置。
②、直接在TimelineItem类里面定义3个依赖属性:IsFirstItem、IsMiddleItem、IsLastItem,来定义TimelineItem的身份,这样我们就可以在触发器里面根据这些属性来进行一些样式上面的设置,就像使用IsMouseOver属性一样。
接下来的代码示例中,我采用了第二种实现方式(因为第一种我已经用过了^_^)
2.1.1.2、为了控件的灵活性以及用户可能需要自定义第一项、中间项、最后一项的样式,或者更极端一点,用户可能会自定义每一个Item的外观,因此在Timeline类里面定义了好几个依赖属性:FirstSlotTemplate、MiddleSlotTemplate、LastSlotTemplate、IsCustomEverySlot以及SlotTemplate。
2.1.2、具体代码实现
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.ComponentModel;
  8. namespace ZdfFlatUI
  9. {
  10. public class TimelineItem : ContentControl
  11. {
  12. #region DependencyProperty
  13. #region IsFirstItem
  14. /// <summary>
  15. /// 获取或者设置该项在列表中是否是第一个
  16. /// </summary>
  17. [Bindable(true), Description("获取或者设置该项在列表中是否是第一个")]
  18. public bool IsFirstItem
  19. {
  20. get { return (bool)GetValue(IsFirstItemProperty); }
  21. set { SetValue(IsFirstItemProperty, value); }
  22. }
  23.  
  24. public static readonly DependencyProperty IsFirstItemProperty =
  25. DependencyProperty.Register("IsFirstItem", typeof(bool), typeof(TimelineItem), new PropertyMetadata(false));
  26. #endregion
  27. #region IsMiddleItem
  28. /// <summary>
  29. /// 获取或者设置该项在列表中是否是中间的一个
  30. /// </summary>
  31. [Bindable(true), Description("获取或者设置该项在列表中是否是中间的一个")]
  32. public bool IsMiddleItem
  33. {
  34. get { return (bool)GetValue(IsMiddleItemProperty); }
  35. set { SetValue(IsMiddleItemProperty, value); }
  36. }
  37. public static readonly DependencyProperty IsMiddleItemProperty =
  38. DependencyProperty.Register("IsMiddleItem", typeof(bool), typeof(TimelineItem), new PropertyMetadata(false));
  39. #endregion
  40. #region IsLastItem
  41. /// <summary>
  42. /// 获取或者设置该项在列表中是否是最后一个
  43. /// </summary>
  44. [Bindable(true), Description("获取或者设置该项在列表中是否是最后一个")]
  45. public bool IsLastItem
  46. {
  47. get { return (bool)GetValue(IsLastItemProperty); }
  48. set { SetValue(IsLastItemProperty, value); }
  49. }
  50.  
  51. public static readonly DependencyProperty IsLastItemProperty =
  52. DependencyProperty.Register("IsLastItem", typeof(bool), typeof(TimelineItem), new PropertyMetadata(false));
  53. #endregion
  54. #endregion
  55. #region Constructors
  56. static TimelineItem()
  57. {
  58. DefaultStyleKeyProperty.OverrideMetadata(typeof(TimelineItem), new FrameworkPropertyMetadata(typeof(TimelineItem)));
  59. }
  60. #endregion
  61. }
  62. }

Timeline代码

  1. 1 using System;
  2. 2 using System.Collections.Generic;
  3. 3 using System.Collections.Specialized;
  4. 4 using System.Linq;
  5. 5 using System.Text;
  6. 6 using System.Windows;
  7. 7 using System.Windows.Controls;
  8. 8 using System.ComponentModel;
  9. 9 namespace ZdfFlatUI
  10. 10 {
  11. 11 /// <summary>
  12. 12 /// 时间轴
  13. 13 /// </summary>
  14. 14 /// <remarks>add by zhidanfeng 2017.5.29</remarks>
  15. 15 public class Timeline : ItemsControl
  16. 16 {
  17. 17 #region private fields
  18. 18 #endregion
  19. 19 #region DependencyProperty
  20. 20 #region FirstSlotTemplate
  21. 21 /// <summary>
  22. 22 /// 获取或者设置第一个时间轴点的样子
  23. 23 /// </summary>
  24. 24 [Bindable(true), Description("获取或者设置第一个时间轴点的样子")]
  25. 25 public DataTemplate FirstSlotTemplate
  26. 26 {
  27. 27 get { return (DataTemplate)GetValue(FirstSlotTemplateProperty); }
  28. 28 set { SetValue(FirstSlotTemplateProperty, value); }
  29. 29 }
  30. 30
  31. 31 public static readonly DependencyProperty FirstSlotTemplateProperty =
  32. 32 DependencyProperty.Register("FirstSlotTemplate", typeof(DataTemplate), typeof(Timeline));
  33. 33 #endregion
  34. 34 #region MiddleSlotTemplate
  35. 35 /// <summary>
  36. 36 /// 获取或者设置中间的时间轴点的样子
  37. 37 /// </summary>
  38. 38 [Bindable(true), Description("获取或者设置中间的时间轴点的样子")]
  39. 39 public DataTemplate MiddleSlotTemplate
  40. 40 {
  41. 41 get { return (DataTemplate)GetValue(MiddleSlotTemplateProperty); }
  42. 42 set { SetValue(MiddleSlotTemplateProperty, value); }
  43. 43 }
  44. 44
  45. 45 public static readonly DependencyProperty MiddleSlotTemplateProperty =
  46. 46 DependencyProperty.Register("MiddleSlotTemplate", typeof(DataTemplate), typeof(Timeline));
  47. 47 #endregion
  48. 48 #region LastItemTemplate
  49. 49 /// <summary>
  50. 50 /// 获取或者设置最后一个时间轴点的样子
  51. 51 /// </summary>
  52. 52 [Bindable(true), Description("获取或者设置最后一个时间轴点的样子")]
  53. 53 public DataTemplate LastSlotTemplate
  54. 54 {
  55. 55 get { return (DataTemplate)GetValue(LastSlotTemplateProperty); }
  56. 56 set { SetValue(LastSlotTemplateProperty, value); }
  57. 57 }
  58. 58
  59. 59 public static readonly DependencyProperty LastSlotTemplateProperty =
  60. 60 DependencyProperty.Register("LastSlotTemplate", typeof(DataTemplate), typeof(Timeline));
  61. 61 #endregion
  62. 62 #region IsCustomEverySlot
  63. 63 /// <summary>
  64. 64 /// 获取或者设置是否自定义每一个时间轴点的外观。
  65. 65 /// </summary>
  66. 66 [Bindable(true), Description("获取或者设置是否自定义每一个时间轴点的外观。当属性值为True时,FirstSlotTemplate、MiddleSlotTemplate、LastSlotTemplate属性都将失效,只能设置SlotTemplate来定义每一个时间轴点的样式")]
  67. 67 public bool IsCustomEverySlot
  68. 68 {
  69. 69 get { return (bool)GetValue(IsCustomEverySlotProperty); }
  70. 70 set { SetValue(IsCustomEverySlotProperty, value); }
  71. 71 }
  72. 72
  73. 73 public static readonly DependencyProperty IsCustomEverySlotProperty =
  74. 74 DependencyProperty.Register("IsCustomEverySlot", typeof(bool), typeof(Timeline), new PropertyMetadata(false));
  75. 75 #endregion
  76. 76 #region SlotTemplate
  77. 77 /// <summary>
  78. 78 /// 获取或者设置每个时间轴点的外观
  79. 79 /// </summary>
  80. 80 [Bindable(true), Description("获取或者设置每个时间轴点的外观。只有当IsCustomEverySlot属性为True时,该属性才生效")]
  81. 81 public DataTemplate SlotTemplate
  82. 82 {
  83. 83 get { return (DataTemplate)GetValue(SlotTemplateProperty); }
  84. 84 set { SetValue(SlotTemplateProperty, value); }
  85. 85 }
  86. 86
  87. 87 public static readonly DependencyProperty SlotTemplateProperty =
  88. 88 DependencyProperty.Register("SlotTemplate", typeof(DataTemplate), typeof(Timeline));
  89. 89 #endregion
  90. 90 #endregion
  91. 91 #region Constructors
  92. 92 static Timeline()
  93. 93 {
  94. 94 DefaultStyleKeyProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata(typeof(Timeline)));
  95. 95 }
  96. 96 #endregion
  97. 97 #region Override
  98. 98 protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  99. 99 {
  100. 100 int index = this.ItemContainerGenerator.IndexFromContainer(element);
  101. 101 TimelineItem timelineItem = element as TimelineItem;
  102. 102 if(timelineItem == null)
  103. 103 {
  104. 104 return;
  105. 105 }
  106. 106 if(index == 0)
  107. 107 {
  108. 108 timelineItem.IsFirstItem = true;
  109. 109 }
  110. 110 if(index == this.Items.Count - 1)
  111. 111 {
  112. 112 timelineItem.IsLastItem = true;
  113. 113 }
  114. 114 base.PrepareContainerForItemOverride(timelineItem, item);
  115. 115 }
  116. 116 protected override DependencyObject GetContainerForItemOverride()
  117. 117 {
  118. 118 return new TimelineItem();
  119. 119 }
  120. 120 public override void OnApplyTemplate()
  121. 121 {
  122. 122 base.OnApplyTemplate();
  123. 123 }
  124. 124 protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  125. 125 {
  126. 126 base.OnItemsChanged(e);
  127. 127 //以下代码是为了新增项或者移除项时,正确设置每个Item的外观
  128. 128 switch (e.Action)
  129. 129 {
  130. 130 case NotifyCollectionChangedAction.Add:
  131. 131 if (e.NewStartingIndex == 0) //如果新添加项是放在第一位,则更改原来的第一位的属性值
  132. 132 {
  133. 133 this.SetTimelineItem(e.NewStartingIndex + e.NewItems.Count);
  134. 134 }
  135. 135 //如果新添加项是放在最后一位,则更改原来的最后一位的属性值
  136. 136 if (e.NewStartingIndex == this.Items.Count - e.NewItems.Count)
  137. 137 {
  138. 138 this.SetTimelineItem(e.NewStartingIndex - 1);
  139. 139 }
  140. 140 break;
  141. 141 case NotifyCollectionChangedAction.Remove:
  142. 142 if(e.OldStartingIndex == 0) //如果移除的是第一个,则更改更新后的第一项的属性值
  143. 143 {
  144. 144 this.SetTimelineItem(0);
  145. 145 }
  146. 146 else
  147. 147 {
  148. 148 this.SetTimelineItem(e.OldStartingIndex - 1);
  149. 149 }
  150. 150 break;
  151. 151 }
  152. 152 }
  153. 153 #endregion
  154. 154 #region private function
  155. 155 /// <summary>
  156. 156 /// 设置TimelineItem的位置属性
  157. 157 /// </summary>
  158. 158 /// <param name="index"></param>
  159. 159 private void SetTimelineItem(int index)
  160. 160 {
  161. 161 if(index > this.Items.Count || index < 0)
  162. 162 {
  163. 163 return;
  164. 164 }
  165. 165 TimelineItem timelineItem = this.ItemContainerGenerator.ContainerFromIndex(index) as TimelineItem;
  166. 166 if(timelineItem == null)
  167. 167 {
  168. 168 return;
  169. 169 }
  170. 170 timelineItem.IsFirstItem = index == 0;
  171. 171 timelineItem.IsLastItem = index == this.Items.Count - 1;
  172. 172 timelineItem.IsMiddleItem = index > 0 && index < this.Items.Count - 1;
  173. 173 }
  174. 174 #endregion
  175. 175 }
  176. 176 }

样式代码:

  1. 1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. 2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ZUI="clr-namespace:ZdfFlatUI">
  3. 3 <PathGeometry x:Key="Icon_Gou" Figures="M378.410667 850.450963C364.491852 850.450963 350.610963 845.293037 340.02963 834.939259L20.920889 523.529481C-0.279704 502.821926-0.279704 469.295407 20.920889 448.587852 42.121481 427.880296 76.48237 427.880296 97.682963 448.587852L378.410667 722.526815 925.75763 188.491852C946.958222 167.784296 981.319111 167.784296 1002.519704 188.491852 1023.720296 209.161481 1023.720296 242.688 1002.519704 263.395556L416.791704 834.939259C406.172444 845.293037 392.291556 850.450963 378.410667 850.450963L378.410667 850.450963Z" />
  4. 4 <DataTemplate x:Key="FirstSlotTemplate">
  5. 5 <Grid>
  6. 6 <Ellipse x:Name="Slot1" Width="15" Height="15" Fill="#30AAADAF" />
  7. 7 <Ellipse x:Name="Slot2" Width="7" Height="7" Fill="#FF6501" />
  8. 8 </Grid>
  9. 9 </DataTemplate>
  10. 10 <DataTemplate x:Key="LastSlotTemplate">
  11. 11 <Grid>
  12. 12 <Ellipse x:Name="Slot1" Width="15" Height="15" Fill="#AAADAF" />
  13. 13 <Path x:Name="path" Width="9"
  14. 14 Data="{StaticResource Icon_Gou}"
  15. 15 Fill="#FFFFFF" Stretch="Uniform" />
  16. 16 </Grid>
  17. 17 </DataTemplate>
  18. 18 <Style TargetType="{x:Type ZUI:TimelineItem}">
  19. 19 <Setter Property="Background" Value="Transparent" />
  20. 20 <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  21. 21 <Setter Property="BorderThickness" Value="0" />
  22. 22 <Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  23. 23 <Setter Property="Padding" Value="15,0,15,0" />
  24. 24 <Setter Property="MinHeight" Value="50" />
  25. 25 <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  26. 26 <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  27. 27 <Setter Property="SnapsToDevicePixels" Value="True" />
  28. 28 <Setter Property="UseLayoutRounding" Value="True" />
  29. 29 <Setter Property="Template">
  30. 30 <Setter.Value>
  31. 31 <ControlTemplate TargetType="{x:Type ZUI:TimelineItem}">
  32. 32 <Grid>
  33. 33 <Grid.RowDefinitions>
  34. 34 <RowDefinition Height="auto" />
  35. 35 <RowDefinition Height="*" />
  36. 36 </Grid.RowDefinitions>
  37. 37 <Grid.ColumnDefinitions>
  38. 38 <ColumnDefinition Width="auto" />
  39. 39 <ColumnDefinition Width="*" />
  40. 40 </Grid.ColumnDefinitions>
  41. 41 <ContentPresenter x:Name="Slot" ContentTemplate="{Binding MiddleSlotTemplate, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  42. 42 <Rectangle x:Name="Line" Grid.Row="1" Width="1"
  43. 43 Fill="{TemplateBinding BorderBrush}" />
  44. 44 <ContentPresenter Grid.RowSpan="2" Grid.Column="1"
  45. 45 Margin="{TemplateBinding Padding}"
  46. 46 HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  47. 47 VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
  48. 48 </Grid>
  49. 49 <ControlTemplate.Triggers>
  50. 50 <!--
  51. 51 IsCustomEverySlotTrue时,FirstSlotTemplateMiddleSlotTemplateLastSlotTemplate都将失效,
  52. 52 只能设置SlotTemplate来定义每一个时间轴点的样式
  53. 53 -->
  54. 54 <MultiDataTrigger>
  55. 55 <MultiDataTrigger.Conditions>
  56. 56 <Condition Binding="{Binding IsFirstItem, RelativeSource={RelativeSource Self}}" Value="True" />
  57. 57 <Condition Binding="{Binding IsCustomEverySlot, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" Value="False" />
  58. 58 </MultiDataTrigger.Conditions>
  59. 59 <Setter TargetName="Slot" Property="ContentTemplate" Value="{Binding FirstSlotTemplate, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  60. 60 </MultiDataTrigger>
  61. 61 <MultiDataTrigger>
  62. 62 <MultiDataTrigger.Conditions>
  63. 63 <Condition Binding="{Binding IsLastItem, RelativeSource={RelativeSource Self}}" Value="True" />
  64. 64 <Condition Binding="{Binding IsCustomEverySlot, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" Value="False" />
  65. 65 </MultiDataTrigger.Conditions>
  66. 66 <Setter TargetName="Slot" Property="ContentTemplate" Value="{Binding LastSlotTemplate, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  67. 67 </MultiDataTrigger>
  68. 68 <MultiDataTrigger>
  69. 69 <MultiDataTrigger.Conditions>
  70. 70 <Condition Binding="{Binding IsMiddleItem, RelativeSource={RelativeSource Self}}" Value="True" />
  71. 71 <Condition Binding="{Binding IsCustomEverySlot, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" Value="False" />
  72. 72 </MultiDataTrigger.Conditions>
  73. 73 <Setter TargetName="Slot" Property="ContentTemplate" Value="{Binding MiddleSlotTemplate, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  74. 74 </MultiDataTrigger>
  75. 75 <DataTrigger Binding="{Binding IsCustomEverySlot, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" Value="True">
  76. 76 <Setter TargetName="Slot" Property="ContentTemplate" Value="{Binding SlotTemplate, RelativeSource={RelativeSource AncestorType={x:Type ZUI:Timeline}}}" />
  77. 77 </DataTrigger>
  78. 78 <Trigger Property="IsLastItem" Value="True">
  79. 79 <Setter TargetName="Line" Property="Visibility" Value="Collapsed" />
  80. 80 </Trigger>
  81. 81 <Trigger Property="IsMiddleItem" Value="True">
  82. 82 <Setter TargetName="Line" Property="Visibility" Value="Visible" />
  83. 83 </Trigger>
  84. 84 </ControlTemplate.Triggers>
  85. 85 </ControlTemplate>
  86. 86 </Setter.Value>
  87. 87 </Setter>
  88. 88 </Style>
  89. 89 <Style TargetType="{x:Type ZUI:Timeline}">
  90. 90 <Setter Property="Background" Value="Transparent" />
  91. 91 <Setter Property="BorderBrush" Value="#F0F0F0" />
  92. 92 <Setter Property="BorderThickness" Value="0" />
  93. 93 <Setter Property="Foreground" Value="Black" />
  94. 94 <Setter Property="HorizontalContentAlignment" Value="Left" />
  95. 95 <Setter Property="VerticalContentAlignment" Value="Top" />
  96. 96 <Setter Property="SnapsToDevicePixels" Value="True" />
  97. 97 <Setter Property="UseLayoutRounding" Value="True" />
  98. 98 <Setter Property="FirstSlotTemplate" Value="{StaticResource FirstSlotTemplate}" />
  99. 99 <Setter Property="MiddleSlotTemplate" Value="{StaticResource LastSlotTemplate}" />
  100. 100 <Setter Property="LastSlotTemplate" Value="{StaticResource LastSlotTemplate}" />
  101. 101 <Setter Property="Template">
  102. 102 <Setter.Value>
  103. 103 <ControlTemplate TargetType="{x:Type ZUI:Timeline}">
  104. 104 <Border Background="{TemplateBinding Background}"
  105. 105 BorderBrush="{TemplateBinding BorderBrush}"
  106. 106 BorderThickness="{TemplateBinding BorderThickness}"
  107. 107 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
  108. 108 UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
  109. 109 <ZUI:ZScrollViewer>
  110. 110 <ItemsPresenter />
  111. 111 </ZUI:ZScrollViewer>
  112. 112 </Border>
  113. 113 </ControlTemplate>
  114. 114 </Setter.Value>
  115. 115 </Setter>
  116. 116 </Style>
  117. 117 </ResourceDictionary>

在Timeline类中,有几处关键代码

①、为了能将TimelineItem和Timeline关联起来,需要重写【GetContainerForItemOverride】方法
  1. protected override DependencyObject GetContainerForItemOverride()
  2. {
  3. return new TimelineItem();
  4. }

②、在控件第一次初始化以及在后来的新增时,需要设置TimelineItem的几个依赖属性值,即设置Item具体是第一个、中间项还是最后一项,因此需要重写【PrepareContainerForItemOverride】

  1. protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  2. {
  3. int index = this.ItemContainerGenerator.IndexFromContainer(element);
  4. TimelineItem timelineItem = element as TimelineItem;
  5. if(timelineItem == null)
  6. {
  7. return;
  8. }
  9. if(index == 0)
  10. {
  11. timelineItem.IsFirstItem = true;
  12. }
  13. if(index == this.Items.Count - 1)
  14. {
  15. timelineItem.IsLastItem = true;
  16. }
  17. base.PrepareContainerForItemOverride(timelineItem, item);
  18. }

③、Timeline控件在运行过程中,可能会涉及到新增或者删除节点,这时同样需要实时的设置每个TimelineItem的IsFirstItem、IsMiddleItem、IsLastItem,这样呈现正确的外观

  1. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  2. {
  3. base.OnItemsChanged(e);
  4. //以下代码是为了新增项或者移除项时,正确设置每个Item的外观
  5. switch (e.Action)
  6. {
  7. case NotifyCollectionChangedAction.Add:
  8. if (e.NewStartingIndex == 0) //如果新添加项是放在第一位,则更改原来的第一位的属性值
  9. {
  10. this.SetTimelineItem(e.NewStartingIndex + e.NewItems.Count);
  11. }
  12. //如果新添加项是放在最后一位,则更改原来的最后一位的属性值
  13. if (e.NewStartingIndex == this.Items.Count - e.NewItems.Count)
  14. {
  15. this.SetTimelineItem(e.NewStartingIndex - 1);
  16. }
  17. break;
  18. case NotifyCollectionChangedAction.Remove:
  19. if(e.OldStartingIndex == 0) //如果移除的是第一个,则更改更新后的第一项的属性值
  20. {
  21. this.SetTimelineItem(0);
  22. }
  23. else
  24. {
  25. this.SetTimelineItem(e.OldStartingIndex - 1);
  26. }
  27. break;
  28. }
  29. }

最终效果图:

 

 
 代码下载:https://github.com/zhidanfeng/WPF.UI

WPF Timeline简易时间轴控件的实现的更多相关文章

  1. SNF开发平台WinForm之十五-时间轴控件使用-SNF快速开发平台3.3-Spring.Net.Framework

    一.显示效果如下: 二.在控件库里选择UCTimeAxis 拖拽到窗体里. 三.加入以下代码,在load事件里进行调用就可以运行了. #region 给时间轴控件加载数据 private void U ...

  2. winform中的时间轴控件

    我现在做的项目遇到一个需求,就是有没有类似的控件: 我要实现的功能是:播放录像. 某个时间段内假如有2个录像,这个坐标表示的是时间,假如我现在拖动时间轴,拖到第一个录像里面开始播放第一个录像,拖到2个 ...

  3. WPF 时间编辑控件的实现(TimeEditer)

    一.前言 有个项目需要用到时间编辑控件,在大量搜索无果后只能自己自定义一个了.MFC中倒是有这个控件,叫CDateTimeCtrl.大概是这个样子: 二.要实现的功能 要实现的功能包含: 编辑时.分. ...

  4. [转载]ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件

    作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/)版权声明:本文的版权归作者与博客园共有.转载时须注明本文的详细链接,否则作者将保留追究其法律 ...

  5. ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件

    本篇要登场的有三个控件,分别是滚轴控件.进度条控件和编辑控件. 一.滚轴控件 Ext.slider 1.滚轴控件的定义 下面我们定义三个具有代表意义滚轴控件,分别展示滚轴横向.纵向,以及单值.多值选择 ...

  6. 老猪带你玩转自定义控件三——sai大神带我实现ios 8 时间滚轮控件

    ios 8 的时间滚轮控件实现了扁平化,带来很好用户体验,android没有现成控件,小弟不才,数学与算法知识不过关,顾十分苦恼,幸好在github上找到sai大神实现代码,甚为欣喜,顾把学习这个控件 ...

  7. WPF 动画:同为控件不同命 - 简书

    原文:WPF 动画:同为控件不同命 - 简书 1. 及格与优秀 读大学的时候,有一门课的作业是用 PPT 展示. 但是我们很多同学都把 PPT 当做 Word 来用,就单纯地往里面堆文字. 大家都单纯 ...

  8. WPF自定义LED风格数字显示控件

    原文:WPF自定义LED风格数字显示控件 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP11199988899/article/de ...

  9. .NET CORE(C#) WPF 方便的实现用户控件切换(祝大家新年快乐)

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF 方便的实现用户控件切换(祝大家新年快乐) 快到2020年了 ...

随机推荐

  1. crx 【 集合 】

    Vimium dbepggeogbaibhgnhhndojpepiihcmeb-1.64-Crx4Chrome.com.crx https://www.crx4chrome.com/down/731/ ...

  2. Synopsys工艺库札记

    Synopsys工艺库札记 库的基本信息 库类 库类语句指定库名. library ( smic13HT_ss ) { ... <lirary description> ... } /*e ...

  3. [RxJS] Convert RxJS Subjects to Observables

    The use of RxJS Subjects is common, but not without problems. In this lesson we will see how they ca ...

  4. Java解析HTML之HTMLParser使用与详解 分类: C_OHTERS 2014-05-19 21:46 2309人阅读 评论(0) 收藏

    转自:http://free0007.iteye.com/blog/1131163 HTMLParser具有小巧,快速的优点,缺点是相关文档比较少(英文的也少),很多功能需要自己摸索.对于初学者还是要 ...

  5. iOS View自定义窍门——UIButton实现上显示图片,下显示文字

    “UIButton实现上显示图片,下显示文字”这个需求相信大家在开发中都或多或少会遇见.比如自定义分享View的时候.当然,也可以封装一个item,上边imageView,下边一个label.但是既然 ...

  6. Android 控件EditText的setOnEditorActionListener方法的理解

    需要注意的是 setOnEditorActionListener这个方法,并不是在我们点击EditText的时候触发,也不是在我们对EditText进行编辑时触发,而是在我们编辑完之后点击软键盘上的回 ...

  7. linux java配置环境变量

    export JAVA_HOME=/alidata/server/java/jdk1.8.0_65export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME ...

  8. Linux基本命令(二)

    1. 输出重定向命令:> Linux允许将命令执行结果重定向到一个文件,本应显示在终端上的内容保存到指定文件中. 如:ls > test.txt ( test.txt 如果不存在,则创建, ...

  9. [Typescript] Generics using TypeScript

    In this lesson we cover the key reason why programming languages need generics. We then show how use ...

  10. 【u008】瑞瑞的木棍

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 瑞瑞有一堆的玩具木棍,每根木棍的两端分别被染上了某种颜色,现在他突然有了一个想法,想要把这 些木棍连在 ...