C# WPF 中的 EllipseGeometry 收缩和淡化不超过 Canvas 边界

EllipseGeometry Shrink & Fade Without Exceeding Canvas Bounds in C# WPF

当用户在 canvas 的边缘周围单击时,如何使动画以更大的尺寸停留在 canvas 内?目前,如果尺寸太大并且用户在 canvas 的边缘附近单击,椭圆将增长到 canvas 之外以覆盖按钮。我需要动画留在 canvas 内,使它看起来像一片比萨饼。

应该是这样的: Size 50 where user clicks near top left of canvas

目前看起来像这样: Size 50 where user clicks near top left of canvas

Xaml:

<Window.Resources>
    <Storyboard x:Key="anim">
        <DoubleAnimation 
            Storyboard.TargetName="myCircle" 
            Storyboard.TargetProperty="RadiusX"
            AutoReverse="True"/>
        <DoubleAnimation 
            Storyboard.TargetName="myCircle" 
            Storyboard.TargetProperty="RadiusY"
            AutoReverse="True"/>
        <DoubleAnimation
            Storyboard.TargetName="path"
            Storyboard.TargetProperty="Opacity"
            AutoReverse="True"/>
    </Storyboard>
</Window.Resources>

<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <Grid.RowDefinitions>
        <RowDefinition Height="23"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Margin="0,0,0,1">
        <Menu DockPanel.Dock="Top" Height="23">
            <MenuItem Header="Main" RenderTransformOrigin="-1.896,0.643" HorizontalAlignment="Left" Width="39" Height="23">
                <MenuItem Header="Exit, Esc" Click="MenuItem_Click_Exit"/>
            </MenuItem>
        </Menu>
    </DockPanel>

    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Name="pane">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Name="pane2">
            <Grid.RowDefinitions>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="50"/>
            </Grid.ColumnDefinitions>

            <Label Content="Size" Grid.Row="0" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
            <Label Content="Fill Color" Grid.Row="1" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
            <Label Content="Stroke Thickness" Grid.Row="2" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
            <Label Content="Stroke Color" Grid.Row="3" Grid.Column="0" VerticalAlignment="Top" Height="25"/>

            <Slider x:Name="Slider_Size" Grid.Row="0" Grid.Column="1" Height="20" Width="45" 
                    Minimum="5" Maximum="50" 
                    AutoToolTipPlacement="BottomRight" 
                    TickFrequency="1"
                    IsSnapToTickEnabled="True"
                    PreviewMouseUp="Slider_Size_PreviewMouseUp"/>
            <Label Name="tempSize" Content="{Binding Path=Value, ElementName=Slider_Size}" Margin="0,25,0,131" Grid.Row="3" Visibility="Hidden"/>
            <ComboBox Name="ComboBox_FillColor" Grid.Row="1" Grid.Column="1" Height="20" Width="45" SelectionChanged="ComboBox_FillColor_Selected"/>
            <TextBox Name="textBox" Grid.Row="2" Grid.Column="1" Height="20" Width="45" TextChanged="textBox_TextChanged"/>
            <ComboBox Name="ComboBox_StrokeColor" Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Height="20" Width="45" SelectionChanged="ComboBox_StrokeColor_Selected"/>
        </Grid>

        <Border Name ="border" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="Black" Grid.Column="1" BorderThickness="2">
            <Canvas Name="canvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="Canvas_MouseDown">
                <Path x:Name="path">
                    <Path.Data>
                        <EllipseGeometry x:Name="myCircle"/>
                    </Path.Data>
                </Path>
                <Canvas.Background>
                    <SolidColorBrush Color="White" Opacity="0"/>
                </Canvas.Background>
            </Canvas>
        </Border>
    </Grid>
</Grid>

C#:

public partial class MainWindow : Window
{
    private int size;
    private SolidColorBrush fillColor;
    private SolidColorBrush strokeColor;
    private List<SolidColorBrush> colors;
    private int fillIndex;
    private int strokeIndex;
    private int strokeThickness = 1;
    private int fillColorDefault;
    private int strokeColorDefault;
    private Point? _start = null;
    public MainWindow()
    {
        InitializeComponent();
        addColors();
        textBox.Text = strokeThickness.ToString();
        parse();
    }
    private void MenuItem_Click_Exit(object sender, RoutedEventArgs e) { Environment.Exit(1); }

    private void Window_KeyUp_ESC(object sender, KeyEventArgs e)
    {
        if (Key.Escape == e.Key)
            MenuItem_Click_Exit(sender, e);
    }
    private void addColors()
    {
        colors = typeof(Brushes).GetProperties().Select(p => p.GetValue(null, null) as SolidColorBrush).ToList();

        int count = 0;
        foreach (SolidColorBrush color in colors)
        {
            ComboBox_FillColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });
            ComboBox_StrokeColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });

            if (color.Color == Colors.Red)
            {
                fillIndex = count;
                fillColor = colors[fillIndex];
                ComboBox_FillColor.SelectedIndex = count;
                fillColorDefault = count;
            }

            if (color.Color == Colors.Black)
            {
                strokeIndex = count;
                strokeColor = colors[strokeIndex];
                ComboBox_StrokeColor.SelectedIndex = count;
                strokeColorDefault = count;
            }

            count++;
        }
    }
    private void ComboBox_FillColor_Selected(object sender, RoutedEventArgs e) { fillIndex = ComboBox_FillColor.SelectedIndex; fillColor = colors[fillIndex]; }

    private void ComboBox_StrokeColor_Selected(object sender, RoutedEventArgs e) { strokeIndex = ComboBox_StrokeColor.SelectedIndex; strokeColor = colors[strokeIndex]; }

    private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        path.Stroke = strokeColor;
        path.StrokeThickness = strokeThickness;
        path.Fill = fillColor;
        path.HorizontalAlignment = HorizontalAlignment.Stretch;
        path.VerticalAlignment = VerticalAlignment.Stretch;
        path.Stretch = Stretch.None;
        path.SetValue(Grid.ColumnProperty, 1);

        _start = Mouse.GetPosition((UIElement)sender);
        myCircle.Center = (Point)_start;

        var sb = FindResource("anim") as Storyboard;

        var x = sb.Children.First() as DoubleAnimation;
        x.To = 2 * size;
        x.Duration = new Duration(TimeSpan.FromSeconds(0.5));

        var y = sb.Children.ElementAt(1) as DoubleAnimation;
        y.To = 2 * size;
        y.Duration = new Duration(TimeSpan.FromSeconds(0.5));

        var z = sb.Children.Last() as DoubleAnimation;
        z.From = 0.0;
        z.To = 1.0;
        z.Duration = new Duration(TimeSpan.FromSeconds(0.5));

        sb.Begin(path);
    }
    private void textBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        //regex where any string of chars besides numbers
        Regex pattern = new Regex(@"^([^0-9]*)$", RegexOptions.Compiled);


        Match result = pattern.Match(textBox.Text);
        if (textBox.Text.ToString() == string.Empty)
            return;
        else if (result.Success)
        {
            MessageBox.Show("Invalid character entered. Integer numbers only. Stroke Thickness will be reseted to a default of 1.");
            strokeThickness = 1;
            textBox.Text = strokeThickness.ToString();
            textBox.SelectAll();
        }
        else
        {
            int x;
            if (int.TryParse(textBox.Text, out x))
                strokeThickness = int.Parse(textBox.Text);
        }
    }
    private void Slider_Size_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        parse();
    }
    private void parse()
    {
        int x;
        if (int.TryParse(tempSize.Content.ToString(), out x))
            size = x;
    }
}

}

所以您不需要将椭圆留在 Canvas 内,但您想要剪掉离开它的部分,对吧?只需将 ClipToBounds(Canvas 的)设置为 true(可以在 Xaml 中完成)。