我想修改现有的ControlTemplate
在自定义日历中绘制一个recantgle来绘制下边框,如下图所示:
当前ControlTemplate
如下所示:
<ControlTemplate x:Key="FesterBlockTemplate" TargetType="ContentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Style="{StaticResource ContinueFromPreviousSignStyle}" />
<ContentControl Grid.Row="2" Style="{StaticResource ToBeContinuedSignStyle}" />
<!--Display of the activity text-->
<Border Opacity="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" BorderThickness="0">
<Border.Background>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}" Path="UpperBackground" />
</Border.Background>
<TextBlock Margin="3" Grid.Row="1" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}, Path=UpperText}"
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" TextWrapping="WrapWithOverflow">
<TextBlock.Foreground>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}" Path="UpperTextForeground" />
</TextBlock.Foreground>
<TextBlock.Background>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}" Path="UpperTextBackground" />
</TextBlock.Background>
<TextBlock.LayoutTransform>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}, Path=TextRotationAngle}" />
</TextBlock.LayoutTransform>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}, Path=HasCustomFontSize}" Value="True">
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:AktivitaetViewBase}}, Path=TextFontSize}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Path Grid.Row="1" Stretch="Fill" Stroke="Black" StrokeThickness="1" Data="M0,0 L0,1" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
<Path Grid.Row="1" Stretch="Fill" Stroke="Black" StrokeThickness="1" Data="M0,0 L0,1" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
</Grid>
</ControlTemplate>
我找到了一种通过指定静态尺寸绘制所需形状的方法:
<UserControl x:Class="WpfComplexShapeTest.ComplexShapeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
Background="Transparent">
<Path Stroke="Black" StrokeThickness="1" Fill="Orange">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="10,10">
<PathFigure.Segments>
<LineSegment Point="10, 210"/>
<BezierSegment Point1="50,0"
Point2="70,350"
Point3="110,150"/>
<LineSegment Point="110, 10"/>
<LineSegment Point="10, 10"/>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</UserControl>
导致这种形状(取自VS设计师):
下一步是让它正确调整大小。它必须采用可用的水平和垂直空间,并且下边界的波形的幅度由int值(HourSegmentHeight
)指定,并且必须保持不变。这就是我创建依赖项属性和属性的原因,这些属性和属性在控件大小更改时会重新计算,如下面的代码所示:
XAML:
<UserControl x:Class="WpfComplexShapeTest.OpenEndControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Background="Transparent"
SizeChanged="OnUserControlSizeChanged"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300"
d:DesignWidth="500">
<Path Stroke="Black" StrokeThickness="1" Fill="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="0,0">
<PathFigure.Segments>
<LineSegment Point="{Binding LowerLeftPoint}"/>
<BezierSegment Point1="{Binding BezierPoint1}"
Point2="{Binding BezierPoint2}"
Point3="{Binding BezierPoint3}"/>
<LineSegment Point="{Binding UpperRightPoint}"/>
<LineSegment Point="0, 0"/>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</UserControl>
代码背后:
using System.Diagnostics;
using System.Windows;
namespace WpfComplexShapeTest {
/// <summary>
/// Interaction logic for OpenEndControl.xaml
/// </summary>
public partial class OpenEndControl {
#region Private Fields
private int m_hourSegmentHeight;
#endregion
#region Public Properties
public static readonly DependencyProperty UpperRightPointProperty = DependencyProperty.Register("UpperRightPoint", typeof (Point), typeof (OpenEndControl));
public static readonly DependencyProperty LowerLeftPointProperty = DependencyProperty.Register("LowerLeftPoint", typeof (Point), typeof (OpenEndControl));
public static readonly DependencyProperty BezierPoint1Property = DependencyProperty.Register("BezierPoint1", typeof (Point), typeof (OpenEndControl));
public static readonly DependencyProperty BezierPoint2Property = DependencyProperty.Register("BezierPoint2", typeof (Point), typeof (OpenEndControl));
public static readonly DependencyProperty BezierPoint3Property = DependencyProperty.Register("BezierPoint3", typeof (Point), typeof (OpenEndControl));
/// <summary>
/// Gets or sets the upper right point.
/// </summary>
/// <value>
/// The upper right point.
/// </value>
public Point UpperRightPoint {
get { return (Point) GetValue(UpperRightPointProperty); }
set { SetValue(UpperRightPointProperty, value); }
}
/// <summary>
/// Gets or sets the lower left point.
/// </summary>
/// <value>
/// The lower left point.
/// </value>
public Point LowerLeftPoint {
get { return (Point) GetValue(LowerLeftPointProperty); }
set { SetValue(LowerLeftPointProperty, value); }
}
/// <summary>
/// Gets or sets the bezier point 1.
/// </summary>
/// <value>
/// The bezier point 1.
/// </value>
public Point BezierPoint1 {
get { return (Point) GetValue(BezierPoint1Property); }
set { SetValue(BezierPoint1Property, value); }
}
/// <summary>
/// Gets or sets the bezier point 2.
/// </summary>
/// <value>
/// The bezier point 2.
/// </value>
public Point BezierPoint2 {
get { return (Point) GetValue(BezierPoint2Property); }
set { SetValue(BezierPoint2Property, value); }
}
/// <summary>
/// Gets or sets the bezier point 3.
/// </summary>
/// <value>
/// The bezier point 3.
/// </value>
public Point BezierPoint3 {
get { return (Point) GetValue(BezierPoint3Property); }
set { SetValue(BezierPoint3Property, value); }
}
/// <summary>
/// Gets or sets the height of the hour segment.
/// </summary>
/// <value>
/// The height of the hour segment.
/// </value>
public int HourSegmentHeight {
get { return m_hourSegmentHeight; }
set {
if (m_hourSegmentHeight != value) {
m_hourSegmentHeight = value;
RefreshPoints();
}
}
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="OpenEndControl"/> class.
/// </summary>
public OpenEndControl() {
InitializeComponent();
RefreshPoints();
}
#endregion
#region Private Methods
/// <summary>
/// Refreshes the points.
/// </summary>
private void RefreshPoints() {
UpperRightPoint = new Point(ActualWidth, 0);
LowerLeftPoint = new Point(0, ActualHeight);
BezierPoint1 = new Point(ActualWidth/2, HourSegmentHeight);
BezierPoint2 = new Point(ActualWidth/2 + 10, 2*HourSegmentHeight);
BezierPoint3 = new Point(ActualWidth, ActualHeight/2 - HourSegmentHeight);
Debug.WriteLine("Width={0}, Height={1}", ActualWidth, ActualHeight);
}
/// <summary>
/// Called when the size of the user control has changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
private void OnUserControlSizeChanged(object sender, SizeChangedEventArgs e) {
RefreshPoints();
}
#endregion
}
}
RereshPoints()
方法无法计算贝塞尔点1,2和3的正确值,我无法在阅读Bézier curve article之后找出要使用的公式
某个控件大小的结果如下所示:
问题:
- 这是绘制我想要的形状的好方法吗?
- 如果是的话,你能帮我找到合适的公式来计算贝塞尔点吗?
答案 0 :(得分:1)
a)只有你可以决定。如果它看起来像你想要它的样子,并且它不需要永远计算,那么它肯定足够好。 b)三次贝塞尔曲线由两个曲线上和两个曲线外曲线定义。开始和结束曲线上的点只是形状需要与矩形连接的位置,因此您可以将该部分放下。形状将从第一个控制点方向的起点“离开”,并从第二个控制点的方向“到达”端点,因此您需要分别为{{1}的控制点C1和C2 }和(startpoint_x, ...)
可以合理地自由选择y坐标。只要C1和C2在中点上方和下方的高度相等,曲线就会显得不错:
(endpoint_x, ...)
只需为d选择一个值,然后看看你最喜欢什么 - 这是你的可视化,我们无法告诉你想要你认为看起来最好=)
此jsfiddle是该概念的简单演示者(将鼠标移到图形上以改变with
start = { 0, ... }
end = { width, ... }
d = ...
c1 = { 0, (start_y + end_y)/2 - d }
c2 = { width, (start_y + end_y)/2 + d }
form
curve(start, c1, c2, end)
的强度。)
答案 1 :(得分:0)
这只是一个想法:您可以按照您想要的方式绘制图片(使用固定点),然后在用户控件或容器中将形状包裹在ViewBox
内。通过这种方式,视图框始终保持形状的比例。