1
+ using System ;
2
+ using System . Globalization ;
3
+ using System . Windows ;
4
+ using System . Windows . Data ;
5
+ using System . Windows . Media ;
6
+
7
+ namespace DebitExpress . VatRelief . Converters ;
8
+
9
+ public sealed class BorderClipConverter : IMultiValueConverter
10
+ {
11
+ public object Convert ( object [ ] values , Type targetType , object parameter , CultureInfo culture )
12
+ {
13
+ if ( values . Length <= 1 || values [ 0 ] is not double width || values [ 1 ] is not double height )
14
+ return DependencyProperty . UnsetValue ;
15
+ if ( width < 1.0 || height < 1.0 ) return Geometry . Empty ;
16
+
17
+ CornerRadius cornerRadius = default ;
18
+ Thickness borderThickness = default ;
19
+ if ( values . Length > 2 && values [ 2 ] is CornerRadius radius )
20
+ {
21
+ cornerRadius = radius ;
22
+ if ( values . Length > 3 && values [ 3 ] is Thickness thickness ) borderThickness = thickness ;
23
+ }
24
+
25
+ var geometry = GetRoundRectangle ( new Rect ( 0 , 0 , width , height ) , borderThickness , cornerRadius ) ;
26
+ geometry . Freeze ( ) ;
27
+
28
+ return geometry ;
29
+ }
30
+
31
+ public object [ ] ConvertBack ( object value , Type [ ] targetTypes , object parameter , CultureInfo culture ) =>
32
+ throw new NotImplementedException ( ) ;
33
+
34
+ // https://wpfspark.wordpress.com/2011/06/08/clipborder-a-wpf-border-that-clips/
35
+ private static Geometry GetRoundRectangle ( Rect baseRect , Thickness borderThickness , CornerRadius cornerRadius )
36
+ {
37
+ // Normalizing the corner radius
38
+ if ( cornerRadius . TopLeft < double . Epsilon ) cornerRadius . TopLeft = 0.0 ;
39
+
40
+ if ( cornerRadius . TopRight < double . Epsilon ) cornerRadius . TopRight = 0.0 ;
41
+
42
+ if ( cornerRadius . BottomLeft < double . Epsilon ) cornerRadius . BottomLeft = 0.0 ;
43
+
44
+ if ( cornerRadius . BottomRight < double . Epsilon ) cornerRadius . BottomRight = 0.0 ;
45
+
46
+ // Taking the border thickness into account
47
+ var leftHalf = borderThickness . Left * 0.5 ;
48
+ if ( leftHalf < double . Epsilon ) leftHalf = 0.0 ;
49
+
50
+ var topHalf = borderThickness . Top * 0.5 ;
51
+ if ( topHalf < double . Epsilon ) topHalf = 0.0 ;
52
+
53
+ var rightHalf = borderThickness . Right * 0.5 ;
54
+ if ( rightHalf < double . Epsilon ) rightHalf = 0.0 ;
55
+
56
+ var bottomHalf = borderThickness . Bottom * 0.5 ;
57
+ if ( bottomHalf < double . Epsilon ) bottomHalf = 0.0 ;
58
+
59
+ // Create the rectangles for the corners that needs to be curved in the base rectangle
60
+ // TopLeft Rectangle
61
+ var topLeftRect = new Rect (
62
+ baseRect . Location . X ,
63
+ baseRect . Location . Y ,
64
+ Math . Max ( 0.0 , cornerRadius . TopLeft - leftHalf ) ,
65
+ Math . Max ( 0.0 , cornerRadius . TopLeft - rightHalf ) ) ;
66
+
67
+ // TopRight Rectangle
68
+ var topRightRect = new Rect (
69
+ baseRect . Location . X + baseRect . Width - cornerRadius . TopRight + rightHalf ,
70
+ baseRect . Location . Y ,
71
+ Math . Max ( 0.0 , cornerRadius . TopRight - rightHalf ) ,
72
+ Math . Max ( 0.0 , cornerRadius . TopRight - topHalf ) ) ;
73
+
74
+ // BottomRight Rectangle
75
+ var bottomRightRect = new Rect (
76
+ baseRect . Location . X + baseRect . Width - cornerRadius . BottomRight + rightHalf ,
77
+ baseRect . Location . Y + baseRect . Height - cornerRadius . BottomRight + bottomHalf ,
78
+ Math . Max ( 0.0 , cornerRadius . BottomRight - rightHalf ) ,
79
+ Math . Max ( 0.0 , cornerRadius . BottomRight - bottomHalf ) ) ;
80
+
81
+ // BottomLeft Rectangle
82
+ var bottomLeftRect = new Rect (
83
+ baseRect . Location . X ,
84
+ baseRect . Location . Y + baseRect . Height - cornerRadius . BottomLeft + bottomHalf ,
85
+ Math . Max ( 0.0 , cornerRadius . BottomLeft - leftHalf ) ,
86
+ Math . Max ( 0.0 , cornerRadius . BottomLeft - bottomHalf ) ) ;
87
+
88
+ // Adjust the width of the TopLeft and TopRight rectangles so that they are proportional to the width of the baseRect
89
+ if ( topLeftRect . Right > topRightRect . Left )
90
+ {
91
+ var newWidth = topLeftRect . Width / ( topLeftRect . Width + topRightRect . Width ) * baseRect . Width ;
92
+ topLeftRect = new Rect ( topLeftRect . Location . X , topLeftRect . Location . Y , newWidth , topLeftRect . Height ) ;
93
+ topRightRect = new Rect (
94
+ baseRect . Left + newWidth ,
95
+ topRightRect . Location . Y ,
96
+ Math . Max ( 0.0 , baseRect . Width - newWidth ) ,
97
+ topRightRect . Height ) ;
98
+ }
99
+
100
+ // Adjust the height of the TopRight and BottomRight rectangles so that they are proportional to the height of the baseRect
101
+ if ( topRightRect . Bottom > bottomRightRect . Top )
102
+ {
103
+ var newHeight = topRightRect . Height / ( topRightRect . Height + bottomRightRect . Height ) *
104
+ baseRect . Height ;
105
+ topRightRect = new Rect ( topRightRect . Location . X , topRightRect . Location . Y , topRightRect . Width ,
106
+ newHeight ) ;
107
+ bottomRightRect = new Rect (
108
+ bottomRightRect . Location . X ,
109
+ baseRect . Top + newHeight ,
110
+ bottomRightRect . Width ,
111
+ Math . Max ( 0.0 , baseRect . Height - newHeight ) ) ;
112
+ }
113
+
114
+ // Adjust the width of the BottomLeft and BottomRight rectangles so that they are proportional to the width of the baseRect
115
+ if ( bottomRightRect . Left < bottomLeftRect . Right )
116
+ {
117
+ var newWidth = bottomLeftRect . Width / ( bottomLeftRect . Width + bottomRightRect . Width ) * baseRect . Width ;
118
+ bottomLeftRect = new Rect ( bottomLeftRect . Location . X , bottomLeftRect . Location . Y , newWidth ,
119
+ bottomLeftRect . Height ) ;
120
+ bottomRightRect = new Rect (
121
+ baseRect . Left + newWidth ,
122
+ bottomRightRect . Location . Y ,
123
+ Math . Max ( 0.0 , baseRect . Width - newWidth ) ,
124
+ bottomRightRect . Height ) ;
125
+ }
126
+
127
+ // Adjust the height of the TopLeft and BottomLeft rectangles so that they are proportional to the height of the baseRect
128
+ if ( bottomLeftRect . Top < topLeftRect . Bottom )
129
+ {
130
+ var newHeight = topLeftRect . Height / ( topLeftRect . Height + bottomLeftRect . Height ) * baseRect . Height ;
131
+ topLeftRect = new Rect ( topLeftRect . Location . X , topLeftRect . Location . Y , topLeftRect . Width , newHeight ) ;
132
+ bottomLeftRect = new Rect (
133
+ bottomLeftRect . Location . X ,
134
+ baseRect . Top + newHeight ,
135
+ bottomLeftRect . Width ,
136
+ Math . Max ( 0.0 , baseRect . Height - newHeight ) ) ;
137
+ }
138
+
139
+ var roundedRectGeometry = new StreamGeometry ( ) ;
140
+
141
+ using var context = roundedRectGeometry . Open ( ) ;
142
+ // Begin from the Bottom of the TopLeft Arc and proceed clockwise
143
+ context . BeginFigure ( topLeftRect . BottomLeft , true , true ) ;
144
+
145
+ // TopLeft Arc
146
+ context . ArcTo ( topLeftRect . TopRight , topLeftRect . Size , 0 , false , SweepDirection . Clockwise , true , true ) ;
147
+
148
+ // Top Line
149
+ context . LineTo ( topRightRect . TopLeft , true , true ) ;
150
+
151
+ // TopRight Arc
152
+ context . ArcTo ( topRightRect . BottomRight , topRightRect . Size , 0 , false , SweepDirection . Clockwise , true ,
153
+ true ) ;
154
+
155
+ // Right Line
156
+ context . LineTo ( bottomRightRect . TopRight , true , true ) ;
157
+
158
+ // BottomRight Arc
159
+ context . ArcTo ( bottomRightRect . BottomLeft , bottomRightRect . Size , 0 , false , SweepDirection . Clockwise ,
160
+ true , true ) ;
161
+
162
+ // Bottom Line
163
+ context . LineTo ( bottomLeftRect . BottomRight , true , true ) ;
164
+
165
+ // BottomLeft Arc
166
+ context . ArcTo ( bottomLeftRect . TopLeft , bottomLeftRect . Size , 0 , false , SweepDirection . Clockwise , true ,
167
+ true ) ;
168
+
169
+ return roundedRectGeometry ;
170
+ }
171
+ }
0 commit comments