是否可以将WPF折线的角半径设置为自定义值。 对于WPF边界,它是可能的。 在我看来,折线只能设置StrokeLineJoin =“Round”,而不是半径:
<Polyline Points="0,0 0,100 200,100" StrokeLineJoin="Round" />
对于border:CornerRadius =“...”是可能的:
<Border CornerRadius="8" ... />
是否有简单的解决方法/黑客来实现折线的自定义圆角(在线连接处)? (例如,Microsoft Visio能够执行此操作。) 谢谢!
答案 0 :(得分:6)
直接使用开箱即用的WPF中的内容是不可能的。然而,这是我做过的一段代码,它不是我称之为“一个简单的解决方法”,但它应该工作。在Xaml中,我认为声明应该是:
<myCustomControls:RoundPolyline Points="0,0 0,100 200,100" Radius="10" />
C#代码:
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MyControls
{
public sealed class RoundPolyline : Shape
{
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register("FillRule", typeof(FillRule), typeof(RoundPolyline), new FrameworkPropertyMetadata(FillRule.EvenOdd, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty PointsProperty = DependencyProperty.Register("Points", typeof(PointCollection), typeof(RoundPolyline), new FrameworkPropertyMetadata(GetEmptyPointCollection(), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(RoundPolyline), new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
private Geometry _geometry;
private void DefineGeometry()
{
PointCollection points = Points;
if (points == null)
{
_geometry = Geometry.Empty;
return;
}
PathFigure figure = new PathFigure();
if (points.Count > 0)
{
// start point
figure.StartPoint = points[0];
if (points.Count > 1)
{
// points between
double desiredRadius = Radius;
for (int i = 1; i < (points.Count - 1); i++)
{
// adjust radius if points are too close
Vector v1 = points[i] - points[i - 1];
Vector v2 = points[i + 1] - points[i];
double radius = Math.Min(Math.Min(v1.Length, v2.Length) / 2, desiredRadius);
// draw the line, and stop before the next point
double len = v1.Length;
v1.Normalize();
v1 *= (len - radius);
LineSegment line = new LineSegment(points[i - 1] + v1, true);
figure.Segments.Add(line);
// draw the arc to the next point
v2.Normalize();
v2 *= radius;
SweepDirection direction = (Vector.AngleBetween(v1, v2) > 0) ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
ArcSegment arc = new ArcSegment(points[i] + v2, new Size(radius, radius), 0, false, direction, true);
figure.Segments.Add(arc);
}
// last point
figure.Segments.Add(new LineSegment(points[points.Count - 1], true));
}
}
PathGeometry geometry = new PathGeometry();
geometry.Figures.Add(figure);
geometry.FillRule = FillRule;
if (geometry.Bounds == Rect.Empty)
{
_geometry = Geometry.Empty;
}
else
{
_geometry = geometry;
}
}
protected override Size MeasureOverride(Size constraint)
{
DefineGeometry();
return base.MeasureOverride(constraint);
}
protected override Geometry DefiningGeometry
{
get
{
return _geometry;
}
}
public double Radius
{
get
{
return (double)GetValue(RadiusProperty);
}
set
{
SetValue(RadiusProperty, value);
}
}
public FillRule FillRule
{
get
{
return (FillRule)GetValue(FillRuleProperty);
}
set
{
SetValue(FillRuleProperty, value);
}
}
public PointCollection Points
{
get
{
return (PointCollection)GetValue(PointsProperty);
}
set
{
SetValue(PointsProperty, value);
}
}
// NOTE: major hack because none of this is public, and this is very unfortunate, it should be...
private static PointCollection _emptyPointCollection;
private static ConstructorInfo _freezableDefaultValueFactoryCtor;
private static object GetEmptyPointCollection()
{
if (_freezableDefaultValueFactoryCtor == null)
{
_emptyPointCollection = (PointCollection)typeof(PointCollection).GetProperty("Empty", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null);
Type freezableDefaultValueFactoryType = typeof(DependencyObject).Assembly.GetType("MS.Internal.FreezableDefaultValueFactory");
_freezableDefaultValueFactoryCtor = freezableDefaultValueFactoryType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
}
return _freezableDefaultValueFactoryCtor.Invoke(new object[] { _emptyPointCollection });
}
}
}
答案 1 :(得分:1)
不是我知道但是他们是黑客...创建边框或矩形,使用表达式混合设置角半径然后转换为路径?这将采用一个矩形(设置一个角半径)并使其成为路径!
答案 2 :(得分:1)
答案 3 :(得分:1)
为了那些可能需要它的人。我也在寻找支持圆角的WPF Polygon类。 我进一步建立了Simon Mourier的代码。
每个点都有自己的可选半径和全局半径。
public sealed class ArcPolygon : Shape
{
public bool RefreshOnPointAdd { get; set; }
private readonly Geometry _geometry;
private readonly PathFigure _figure;
public ArcPolygon()
{
RefreshOnPointAdd = true;
_geometry = new PathGeometry();
_figure = new PathFigure();
((PathGeometry)_geometry).Figures.Add(_figure);
_pointCollection = new ArcPointCollection();
_pointCollection.CollectionChanged += PointCollectionChanged;
}
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register("FillRule", typeof(FillRule), typeof(ArcPolygon), new FrameworkPropertyMetadata(FillRule.EvenOdd, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double?), typeof(ArcPolygon), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public void Refresh()
{
DefineGeometry();
}
private void DefineGeometry()
{
var points = PointCollection;
_figure.Segments.Clear();
if(points.Any())
{
// start point
_figure.StartPoint = points[0];
if(points.Count > 1)
{
// points between
for(int i = 1; i < (points.Count - 1); i++)
{
// adjust radius if points are too close
var v1 = (Point)points[i] - points[i - 1];
var v2 = (Point)points[i + 1] - points[i];
var radius = (points[i].Radius ?? Radius) ?? 0;
radius = Math.Min(Math.Min(v1.Length, v2.Length) / 2, radius);
// draw the line, and stop before the next point
double len = v1.Length;
v1.Normalize();
v1 *= (len - radius);
var line = new LineSegment((Point)points[i - 1] + v1, true);
_figure.Segments.Add(line);
// draw the arc to the next point
v2.Normalize();
v2 *= radius;
var direction = (Vector.AngleBetween(v1, v2) > 0) ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
var arc = new ArcSegment((Point)points[i] + v2, new Size(radius, radius), 0, false, direction, true);
_figure.Segments.Add(arc);
}
// last point
_figure.Segments.Add(new LineSegment(points[points.Count - 1], true));
}
}
}
protected override Size MeasureOverride(Size constraint)
{
DefineGeometry();
return base.MeasureOverride(constraint);
}
protected override Geometry DefiningGeometry
{
get
{
return _geometry;
}
}
public double? Radius
{
get
{
return (double?)GetValue(RadiusProperty);
}
set
{
SetValue(RadiusProperty, value);
}
}
public FillRule FillRule
{
get
{
return (FillRule)GetValue(FillRuleProperty);
}
set
{
SetValue(FillRuleProperty, value);
((PathGeometry)_geometry).FillRule = value;
}
}
private ArcPointCollection _pointCollection;
/// <summary>
/// Gets or sets a collection that contains the points of the polygon.
/// </summary>
public ArcPointCollection PointCollection
{
get { return _pointCollection; }
set
{
_pointCollection = value;
if(RefreshOnPointAdd) Refresh();
}
}
// <summary>
// Gets or sets the control's text
// </summary>
public string Points
{
get
{
return string.Join(" ", PointCollection.Select(x => x.ToString()));
}
set
{
var pointCollection = new ArcPointCollection();
//10,50,45 180,50 180,150,45 10,150
var points = value.Split(' ');
foreach(var point in points)
{
if(point.Trim() == string.Empty) continue;
var xyarc = point.Split(',');
var item = new ArcPoint();
if(xyarc.Length >= 1) item.X = double.Parse(xyarc[0], CultureInfo.InvariantCulture);
if(xyarc.Length >= 2) item.Y = double.Parse(xyarc[1], CultureInfo.InvariantCulture);
if(xyarc.Length >= 3) item.Radius = double.Parse(xyarc[2], CultureInfo.InvariantCulture);
pointCollection.Add(item);
}
PointCollection = pointCollection;
}
}
private void PointCollectionChanged(object sender, EventArgs e)
{
if(RefreshOnPointAdd) Refresh();
}
public void Reset()
{
_pointCollection.Clear();
}
public void AddPoint(double x, double y)
{
_pointCollection.Add(x, y);
}
public void AddPoint(double x, double y, double? radius)
{
_pointCollection.Add(x, y, radius);
}
}
public sealed class ArcPoint
{
public double X { get; set; }
public double Y { get; set; }
public double? Radius { get; set; }
public ArcPoint()
{
}
public ArcPoint(double x, double y)
{
this.X = x;
this.Y = y;
}
public ArcPoint(double x, double y, double? radius)
{
this.X = x;
this.Y = y;
this.Radius = radius;
}
public static implicit operator Point(ArcPoint point)
{
return new Point(point.X, point.Y);
}
public static implicit operator ArcPoint(Point point)
{
return new ArcPoint(point.X, point.Y);
}
public override string ToString()
{
if(Radius == null)
return string.Format(CultureInfo.InvariantCulture, "{0},{1}", X, Y);
return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", X, Y, Radius);
}
}
public sealed class ArcPointCollection : ObservableCollection<ArcPoint>
{
public void Add(double x, double y)
{
Add(new ArcPoint(x, y));
}
public void Add(double x, double y, double? radius)
{
Add(new ArcPoint(x, y, radius));
}
}