public class FlowingPoint : Control
{
static FlowingPoint()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlowingPoint), new FrameworkPropertyMetadata(typeof(FlowingPoint)));
}
#region Property
StackPanel stack = null;
Ellipse t = null;
#region 点大小
public double PointSize
{
get { return (double)GetValue(PointSizeProperty); }
set { SetValue(PointSizeProperty, value); }
}
// Using a DependencyProperty as the backing store for PointSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointSizeProperty =
DependencyProperty.Register("PointSize", typeof(double), typeof(FlowingPoint), new PropertyMetadata(10d, OnPropertyChanged));
#endregion
#region 点颜色
public Brush PointBrush
{
get { return (Brush)GetValue(PointBrushProperty); }
set { SetValue(PointBrushProperty, value); }
}
// Using a DependencyProperty as the backing store for PointBrush. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointBrushProperty =
DependencyProperty.Register("PointBrush", typeof(Brush), typeof(FlowingPoint), new PropertyMetadata(Brushes.Red, OnPropertyChanged));
#endregion
#region 方向
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
// Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(FlowingPoint), new PropertyMetadata(Orientation.Horizontal, OnPropertyChanged));
#endregion
#region 是否流动
public bool IsFlowing
{
get { return (bool)GetValue(IsFlowingProperty); }
set { SetValue(IsFlowingProperty, value); }
}
// Using a DependencyProperty as the backing store for IsFlowing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsFlowingProperty =
DependencyProperty.Register("IsFlowing", typeof(bool), typeof(FlowingPoint), new PropertyMetadata(true));
#endregion
#endregion
#region OnPropertyChanged
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d == null
|| !(d is FlowingPoint))
{
return;
}
(d as FlowingPoint).UpdatePoint();
}
#endregion
#region OnApplyTemplate
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
stack = (StackPanel)this.Template.FindName("stackPanel", this);
t = (Ellipse)this.Template.FindName("t", this);
}
#endregion
#region OnRender
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdatePoint();
}
#endregion
#region UpdatePoint()
private void UpdatePoint()
{
if (stack == null)
{
return;
}
IsFlowing = false;
while (stack.Children.Count > 1)
{
stack.Children.RemoveAt(1);
}
if (Orientation == Orientation.Horizontal)
{
t.Margin = new Thickness(-10, 0, 0, 0);
if (ActualWidth == 0)
{
return;
}
for (int i = 0; i < ActualWidth / 10; i++)
{
stack.Children.Add(new Ellipse() { Fill = PointBrush, Width = PointSize, Height = PointSize, Margin = new Thickness(10, 0, 0, 0) });
}
}
else
{
t.Margin = new Thickness(0, -10, 0, 0);
if (ActualHeight == 0)
{
return;
}
for (int i = 0; i < ActualHeight / 10; i++)
{
stack.Children.Add(new Ellipse() { Fill = PointBrush, Width = PointSize, Height = PointSize, Margin = new Thickness(0, 10, 0, 0) });
}
}
}
#endregion
}
<Style TargetType="{x:Type local:FlowingPoint}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FlowingPoint}">
<StackPanel x:Name="stackPanel" Background="{TemplateBinding Background}"
Orientation="{Binding Orientation,RelativeSource={RelativeSource Mode=TemplatedParent}}">
<Ellipse x:Name="t" Width="{Binding PointSize,RelativeSource={RelativeSource Mode=TemplatedParent}}"
Height="{Binding PointSize,RelativeSource={RelativeSource Mode=TemplatedParent}}"
Fill="{Binding PointBrush,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</StackPanel>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Orientation,RelativeSource={RelativeSource Mode=Self}}" Value="Horizontal"/>
<Condition Binding="{Binding IsFlowing,RelativeSource={RelativeSource Mode=Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="00:00:03" From="-10,0,0,0" To="0,0,0,0" RepeatBehavior="Forever"
Storyboard.TargetName="t" Storyboard.TargetProperty="Margin"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Orientation,RelativeSource={RelativeSource Mode=Self}}" Value="Vertical"/>
<Condition Binding="{Binding IsFlowing,RelativeSource={RelativeSource Mode=Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="00:00:03" From="0,-10,0,0" To="0,0,0,0" RepeatBehavior="Forever"
Storyboard.TargetName="t" Storyboard.TargetProperty="Margin"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>