WPF:Graphics图画-Brushes画刷-Brushes画刷集合(5)

LinearGradientBrushAnimationExamples线变画刷动画效果

  1. 通过对渐变属性Offset、Color、Opacity、StartPoint、EndPoint、S+EPoint组合的动画效果

clipboard.png
扩展:
clipboard.png

Interactive LinearGradientBrush Example互动线变画刷

关注问题点:

  1. 画刷的各属性绑定及更新
  2. 虚线起终点拖动实现及绑定

clipboard.png

1、画刷的各属性绑定及更新

  • 起始点文本框的输入数字的实时更新及转换,同理文本框坐标数据绑定到终点小圆圈的坐标。
  1. 设置文本框的键盘弹起事件
  2. 通过文本框的文本Point.Parse()转换为坐标点,若有错误cath里忽略
  3. 判断相关属性Point的匹配模式,相对还是绝对坐标,设置到起始点小圆圈的平移坐标里。
<TextBox 
  Name="StartPointTextBox"
  Grid.Column="1" Grid.Row="0"
  Text="0.0000,0.0000"
  KeyUp="OnStartPointTextBoxKeyUp" />
private void OnStartPointTextBoxKeyUp(object sender, KeyEventArgs args)
{
    var t = (TextBox) sender;
    try
    {
        var p = Point.Parse(t.Text);
        if (InteractiveLinearGradientBrush.MappingMode == BrushMappingMode.RelativeToBoundingBox)
        {
            StartPointMarkerTranslateTransform.X = p.X*GradientDisplayElement.ActualWidth;
            StartPointMarkerTranslateTransform.Y = p.Y*GradientDisplayElement.ActualHeight;
        }
        else
        {
            StartPointMarkerTranslateTransform.X = p.X;
            StartPointMarkerTranslateTransform.Y = p.Y;
        }
    }
    catch (InvalidOperationException)
    {
        // Ignore errors.
    }
    catch (FormatException)
    {
        // Ignore errors.
    }
}
  • ComboBox的绑定源及选择项处理
  1. 通过绑定源元素的一个属性,使用类型转换功能,特别是利用Enum.GetNames(Type)获取属性的类型(Enum)中的值来获取数据。
  2. LinearGradientBrush的SpreadMode、ColorInterpolationMode的ComboBox同理如此处理
  3. 源代码有漏项的,经调试是3个ComboBox的选择项的绑定模式应该是需添加Mode=OneWayToSource,而实际未加。否则运行出现bug。
<ComboBox 
  Name="MappingModeComboBox"
  Grid.Column="1" Grid.Row="2"
  SelectedIndex="1"
  SelectedItem="{Binding ElementName='InteractiveLinearGradientBrush', Path='MappingMode'}"
  ItemsSource="{Binding ElementName='InteractiveLinearGradientBrush', Path='MappingMode', Converter={StaticResource EnumStringConverterResource}}"
  Padding="5"
/>

很有创意的获取通用类型Enum中的值的方法:

namespace Brushes
{
    [ValueConversion(typeof (object), typeof (string[]))]
    public class EnumPossibleValuesToStringArrayConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => new ArrayList(Enum.GetNames(value.GetType()));

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null;
    }
}
  • 颜色文本框绑定到线变点的GradientStop的Color属性
<TextBox
  Grid.Column="2" Grid.Row="0" 
  Text="{Binding ElementName='GradientStop1', Path='Color'}" />

滑块绑定到线变点的GradientStop的偏移位置属性

<Slider
  Grid.Column="5" Grid.Row="0" 
  Minimum="0" Maximum="1" 
  Width="100" 
  Value="{Binding ElementName='GradientStop1', Path='Offset'}" />

偏移数值绑定到线变点的GradientStop的Offset属性通过Double显示,使用DoubleToString转换器。

<TextBlock 
  Style="{StaticResource EntryLabelStyle}"
  Grid.Column="6" Grid.Row="0" 
  FontFamily="Courier"
  Margin="20,0,0,0"
  Text="{Binding ElementName='GradientStop1', Path='Offset',
    Converter='{StaticResource DoubleToStringConverterResource}'}" />

2、虚线起终点拖动实现及绑定

  • Border的背景色及相关拖动鼠标引起属性变动事件
  1. 书本左键按下、弹起、 拖动事件处理画刷的起终点变化逻辑
<Border Name="GradientDisplayElement" 
  SizeChanged="GradientDisplaySizeChanged"
  MouseLeftButtonDown="GradientDisplayMouseLeftButtonDown"
  MouseLeftButtonUp="GradientDisplayMouseLeftButtonUp"
  MouseMove="GradientDisplayMouseMove"
  Margin="20"
  Height="200" Width="400">
  <Border.Background>
    <LinearGradientBrush 
      x:Name="InteractiveLinearGradientBrush"
      Changed="OnInteractiveLinearGradientBrushChanged">
      <GradientStop x:Name="GradientStop1" Offset="0" Color="Blue" />
      <GradientStop x:Name="GradientStop2" Offset="0.5" Color="Purple" />
      <GradientStop x:Name="GradientStop3" Offset="1" Color="Red" />
    </LinearGradientBrush>
  </Border.Background>

Canvas上的Line及小圆圈:

  1. 设置线条,其起终点坐标绑定到小圆圈的平移转换坐标
  2. 设置起终点小圆圈的默认位置-10,-10,同时设置需要平移转换的X、Y坐标作为移动坐标,这个小技巧方便文本绑定的位置,不与小圆圈显示重叠。
<Canvas  
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

    <Line
      Stroke="Black" StrokeThickness="3"
      StrokeDashArray="2,1"
      X1="{Binding ElementName=StartPointMarkerTranslateTransform, Path=X}"
      Y1="{Binding ElementName=StartPointMarkerTranslateTransform, Path=Y}"
      X2="{Binding ElementName=EndPointMarkerTranslateTransform, Path=X}"
      Y2="{Binding ElementName=EndPointMarkerTranslateTransform, Path=Y}" />

      <!-- Marks the brush's StartPoint. -->
    <Ellipse
      Name="StartPointMarker"
      Style="{StaticResource MarkerEllipseStyle}"
      Canvas.Left="-10" Canvas.Top="-10">
      <Ellipse.RenderTransform>
        <TranslateTransform 
          x:Name="StartPointMarkerTranslateTransform"
          X="0" Y="0" />
      </Ellipse.RenderTransform>
    </Ellipse>

    <!-- Marks the brush's EndPointPoint. -->
    <Ellipse
      Name="EndPointMarker"
      Style="{StaticResource MarkerEllipseStyle}"
      Canvas.Left="-10" Canvas.Top="-10">
      <Ellipse.RenderTransform>
        <TranslateTransform 
          x:Name="EndPointMarkerTranslateTransform"
          X="0" Y="0" />
      </Ellipse.RenderTransform>
    </Ellipse>

    <!-- Labels the StartPoint marker. -->
    <Label Content="StartPoint">
      <Label.RenderTransform>
        <TranslateTransform
          X="{Binding ElementName=StartPointMarkerTranslateTransform, Path=X}"
          Y="{Binding ElementName=StartPointMarkerTranslateTransform, Path=Y}" />
      </Label.RenderTransform>
    </Label>

    <!-- Labels the EndPoint marker. -->
    <Label Content="EndPoint">
      <Label.RenderTransform>
        <TranslateTransform
          X="{Binding ElementName=EndPointMarkerTranslateTransform, Path=X}"
          Y="{Binding ElementName=EndPointMarkerTranslateTransform, Path=Y}" />
      </Label.RenderTransform>
    </Label>
  </Canvas>

后台代码:

  1. 在页面加载时进行ComboBox的选择项改变事件。
  2. 起终点文本框键盘弹起事情(测试此代码无意义,可注释掉):
private void OnPageLoaded(object sender, RoutedEventArgs s)
{
    MappingModeComboBox.SelectionChanged += MappingModeChanged;
    OnStartPointTextBoxKeyUp(StartPointTextBox, null);
    OnEndPointTextBoxKeyUp(EndPointTextBox, null);
}

线性渐变画刷的坐标模式选择值变化处理:
相对坐标与绝对坐标的转换

  1. 测试发现此选择项改变后起终点文本框的数值无变化,应该源代码遗漏了,添加如下:
// Updates the StartPoint and EndPoint and their markers when
// the user changes the brush's MappingMode.
private void MappingModeChanged(object sender, SelectionChangedEventArgs e)
{
    var oldStartPoint = InteractiveLinearGradientBrush.StartPoint;
    var newStartPoint = new Point();
    var oldEndPoint = InteractiveLinearGradientBrush.EndPoint;
    var newEndPoint = new Point();


    if (InteractiveLinearGradientBrush.MappingMode ==
        BrushMappingMode.RelativeToBoundingBox)
    {
        // The MappingMode changed from absolute to relative.
        // To find the new relative point, divide the old absolute points
        // by the painted area's width and height. 
        newStartPoint.X = oldStartPoint.X/GradientDisplayElement.ActualWidth;
        newStartPoint.Y = oldStartPoint.Y/GradientDisplayElement.ActualHeight;
        InteractiveLinearGradientBrush.StartPoint = newStartPoint;

        newEndPoint.X = oldEndPoint.X/GradientDisplayElement.ActualWidth;
        newEndPoint.Y = oldEndPoint.Y/GradientDisplayElement.ActualHeight;
        InteractiveLinearGradientBrush.EndPoint = newEndPoint;
    }
    else
    {
        // The MappingMode changed from relative to absolute.
        // To find the new absolute point, multiply the old relative points
        // by the painted area's width and height. 
        newStartPoint.X = oldStartPoint.X*GradientDisplayElement.ActualWidth;
        newStartPoint.Y = oldStartPoint.Y*GradientDisplayElement.ActualHeight;
        InteractiveLinearGradientBrush.StartPoint = newStartPoint;

        newEndPoint.X = oldEndPoint.X*GradientDisplayElement.ActualWidth;
        newEndPoint.Y = oldEndPoint.Y*GradientDisplayElement.ActualHeight;

        InteractiveLinearGradientBrush.EndPoint = newEndPoint;
    }

    // Update the StartPoint and EndPoint display text.
    StartPointTextBox.Text = newStartPoint.X.ToString("F4") +
                             "," + newStartPoint.Y.ToString("F4");
    EndPointTextBox.Text = newEndPoint.X.ToString("F4") +
                           "," + newEndPoint.Y.ToString("F4");
}

键盘弹起事件:
测试发现源码缺少InteractiveLinearGradientBrush.StartPoint = p;会引起MappingMode选择项改变后Text内容还是原来的。

private void OnStartPointTextBoxKeyUp(object sender, KeyEventArgs args)
{
    var t = (TextBox) sender;
    try
    {
        var p = Point.Parse(t.Text);
        if (InteractiveLinearGradientBrush.MappingMode == BrushMappingMode.RelativeToBoundingBox)
        {
            StartPointMarkerTranslateTransform.X = p.X*GradientDisplayElement.ActualWidth;
            StartPointMarkerTranslateTransform.Y = p.Y*GradientDisplayElement.ActualHeight;
        }
        else
        {
            StartPointMarkerTranslateTransform.X = p.X;
            StartPointMarkerTranslateTransform.Y = p.Y;
        }
        InteractiveLinearGradientBrush.StartPoint = p;
    }
    catch (InvalidOperationException)
    {
        // Ignore errors.
    }
    catch (FormatException)
    {
        // Ignore errors.
    }
}

private void OnEndPointTextBoxKeyUp(object sender, KeyEventArgs args)
{
    var t = (TextBox) sender;
    try
    {
        var p = Point.Parse(t.Text);
        if (InteractiveLinearGradientBrush.MappingMode == BrushMappingMode.RelativeToBoundingBox)
        {
            EndPointMarkerTranslateTransform.X = p.X*GradientDisplayElement.ActualWidth;
            EndPointMarkerTranslateTransform.Y = p.Y*GradientDisplayElement.ActualHeight;
            
        }
        else
        {
            EndPointMarkerTranslateTransform.X = p.X;
            EndPointMarkerTranslateTransform.Y = p.Y;
        }
        InteractiveLinearGradientBrush.EndPoint = p;
    }
    catch (InvalidOperationException)
    {
        // Ignore errors.
    }
    catch (FormatException)
    {
        // Ignore errors.
    }
}
  • 获取Border的线变显示尺寸变化事件:

当Border初始化时的尺寸影响到起终点圆圈的位置。

 // Update the StartPoint and EndPoint markers when the gradient display
// element's size changes.
private void GradientDisplaySizeChanged(object sender, SizeChangedEventArgs e)
{
    // The marker positions only need recalcutated if the brush's MappingMode
    // is RelativeToBoundingBox.
    if (InteractiveLinearGradientBrush.MappingMode ==
        BrushMappingMode.RelativeToBoundingBox)
    {
        StartPointMarkerTranslateTransform.X =
            InteractiveLinearGradientBrush.StartPoint.X*e.NewSize.Width;
        StartPointMarkerTranslateTransform.Y =
            InteractiveLinearGradientBrush.StartPoint.Y*e.NewSize.Height;

        EndPointMarkerTranslateTransform.X =
            InteractiveLinearGradientBrush.EndPoint.X*e.NewSize.Width;
        EndPointMarkerTranslateTransform.Y =
            InteractiveLinearGradientBrush.EndPoint.Y*e.NewSize.Height;
    }
}
  • 在Border上按下鼠标位置:
  1. 获取按下的小圆圈对象,此处注册的一个依赖项属性
  2. 不知是否设置一个Shape字段存储,待后续测试
public static readonly DependencyProperty SelectedMarkerProperty =
    DependencyProperty.Register
        ("SelectedMarker", typeof (Shape), typeof (InteractiveLinearGradientBrushExample),
            new PropertyMetadata(null));
// Determine whether the user clicked a marker.
private void GradientDisplayMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (e.OriginalSource is Shape)
    {
        SetValue(SelectedMarkerProperty, (Shape) e.OriginalSource);
    }
    else
        SetValue(SelectedMarkerProperty, null);
}
  • 鼠标按键弹起事件
  1. 获取弹起时的相对Border元素的坐标,提取存储的Shape对象进行按下及弹起对象的比较。
  2. 鼠标同步?
  3. 判断坐标模式,设置坐标文本框及画刷的坐标
// Determines whether the user just finished dragging a marker. If so,
// this method updates the brush's StartPoint or EndPoint property,
// depending on which marker was dragged. 
private void GradientDisplayMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    var clickPoint = e.GetPosition(GradientDisplayElement);
    var s = (Shape) GetValue(SelectedMarkerProperty);
    if (s == EndPointMarker || s == StartPointMarker)
    {
        var translation = (TranslateTransform) s.RenderTransform;
        translation.X = clickPoint.X;
        translation.Y = clickPoint.Y;
        SetValue(SelectedMarkerProperty, null);
        Mouse.Synchronize();

        Point p;
        if (InteractiveLinearGradientBrush.MappingMode == BrushMappingMode.RelativeToBoundingBox)
        {
            p = new Point(clickPoint.X/GradientDisplayElement.ActualWidth,
                clickPoint.Y/GradientDisplayElement.ActualHeight);
        }
        else
        {
            p = clickPoint;
        }

        if (s == StartPointMarker)
        {
            InteractiveLinearGradientBrush.StartPoint = p;
            StartPointTextBox.Text = p.X.ToString("F4") + "," + p.Y.ToString("F4");
        }
        else
        {
            InteractiveLinearGradientBrush.EndPoint = p;
            EndPointTextBox.Text = p.X.ToString("F4") + "," + p.Y.ToString("F4");
        }
    }
}

鼠标移动事件处理:

  1. 同理如上处理
  2. 竟然有重复代码,忍不住要重构下了~~
// Update the StartPoint or EndPoint when the user drags one of the
// points with the mouse. 
private void GradientDisplayMouseMove(object sender, MouseEventArgs e)
{
    var currentPoint = e.GetPosition(GradientDisplayElement);
    var s = (Shape) GetValue(SelectedMarkerProperty);

    // Determine whether the user dragged a StartPoint or
    // EndPoint marker.
    if (s == EndPointMarker || s == StartPointMarker)
    {
        // Move the selected marker to the current mouse position.
        var translation = (TranslateTransform) s.RenderTransform;
        translation.X = currentPoint.X;
        translation.Y = currentPoint.Y;
        Mouse.Synchronize();

        Point p;

        // Calculate the StartPoint or EndPoint.
        if (InteractiveLinearGradientBrush.MappingMode ==
            BrushMappingMode.RelativeToBoundingBox)
        {
            // If the MappingMode is relative, compute the relative
            // value of the new point.
            p = new Point(currentPoint.X/GradientDisplayElement.ActualWidth,
                currentPoint.Y/GradientDisplayElement.ActualHeight);
        }
        else
        {
            // If the MappingMode is absolute, there's no more
            // work to do.
            p = currentPoint;
        }

        if (s == StartPointMarker)
        {
            // If the selected marker is the StartPoint marker,
            // update the brush's StartPoint.
            InteractiveLinearGradientBrush.StartPoint = p;
            StartPointTextBox.Text = p.X.ToString("F4") + "," + p.Y.ToString("F4");
        }
        else
        {
            // Otherwise, update the brush's EndPoint.
            InteractiveLinearGradientBrush.EndPoint = p;
            EndPointTextBox.Text = p.X.ToString("F4") + "," + p.Y.ToString("F4");
        }
    }
}

最后是画刷属性的改变事件:

  1. 更新文本显示的代码内容。
// Update the markup display whenever the brush changes.
private void OnInteractiveLinearGradientBrushChanged(object sender, EventArgs e)
{
    if (GradientDisplayElement != null)
    {
        markupOutputTextBlock.Text =
            GenerateLinearGradientBrushMarkup(InteractiveLinearGradientBrush);
    }
}

// Helper method that displays the markup of interest for
// creating the specified brush.
private static string GenerateLinearGradientBrushMarkup(LinearGradientBrush theBrush)
{
    var sBuilder = new StringBuilder();
    sBuilder.Append("<" + theBrush.GetType().Name + "\n" +
                    "  StartPoint=\"" + theBrush.StartPoint + "\"" +
                    "  EndPoint=\"" + theBrush.EndPoint + "\" \n" +
                    "  MappingMode=\"" + theBrush.MappingMode + "\"" +
                    "  SpreadMethod=\"" + theBrush.SpreadMethod + "\"\n" +
                    "  ColorInterpolationMode=\"" + theBrush.ColorInterpolationMode + "\"" +
                    "  Opacity=\"" + theBrush.Opacity.ToString(CultureInfo.InvariantCulture) + "\"" + ">\n");

    foreach (var stop in theBrush.GradientStops)
    {
        sBuilder.Append
            (
                "  <GradientStop Offset=\"" + stop.Offset.ToString("F4")
                + "\" Color=\"" + stop.Color + "\" />\n"
            );
    }
    sBuilder.Append("</LinearGradientBrush>");
    return sBuilder.ToString();
}
}

以上,,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值