Creating Gradient Brush along a Circular Path
In GDI+/Winforms you can use the PathGradientBrush
to do this:
- https://web.archive.org/web/20140906083853/http://bobpowell.net/pgb.aspx
- https://docs.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.pathgradientbrush
Unfortunately there is no support for a PathGradientBrush in WPF, but a few people have asked for it here:
http://dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/suggestions/480949-add-a-pathgradientbrush-like-in-winforms-
(might be worth casting your vote too!)
Because of the lack of support you cannot do it directly in XAML, you could however use GDI+ code to create an image and then use the image in your XAML. This might give you better performance than using a non-affine transformation.
You can get a cross-radial effect by using a non-affine transformation such as a perspective transform. I used the ideas in this article by Charles Petzold:
- Non-Affine Transforms in 2D?
to create a XAML-only annular region with a cross-radial gradient. Here is the markup:
<Canvas x:Name="LayoutRoot">
<Canvas.Resources>
<x:Array x:Key="sampleData" Type="sys:Object">
<x:Array Type="sys:Object">
<sys:Double>0</sys:Double>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Yellow" Offset="0.5"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</x:Array>
<x:Array Type="sys:Object">
<sys:Double>90</sys:Double>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Blue" Offset="0"/>
<GradientStop Color="Green" Offset="0.5"/>
<GradientStop Color="Red" Offset="1"/>
</LinearGradientBrush>
</x:Array>
<x:Array Type="sys:Object">
<sys:Double>180</sys:Double>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Yellow" Offset="0.5"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</x:Array>
<x:Array Type="sys:Object">
<sys:Double>270</sys:Double>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Blue" Offset="0"/>
<GradientStop Color="Green" Offset="0.5"/>
<GradientStop Color="Red" Offset="1"/>
</LinearGradientBrush>
</x:Array>
</x:Array>
</Canvas.Resources>
<ItemsControl ItemsSource="{StaticResource sampleData}">
<ItemsControl.OpacityMask>
<RadialGradientBrush>
<GradientStop Color="Transparent" Offset="0.95"/>
<GradientStop Color="White" Offset="0.949"/>
<GradientStop Color="White" Offset="0.501"/>
<GradientStop Color="Transparent" Offset="0.5"/>
</RadialGradientBrush>
</ItemsControl.OpacityMask>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Width="1" Height="1">
<Canvas.RenderTransform>
<RotateTransform Angle="{Binding [0]}" CenterX="124" CenterY="124"/>
</Canvas.RenderTransform>
<Viewport3D Width="250" Height="250">
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0 0 0, 0 1 0, 1 0 0, 1 1 0" TextureCoordinates="0 1, 0 0, 1 1, 1 0" TriangleIndices="0 2 1, 2 3 1"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="{Binding [1]}"/>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<MatrixTransform3D Matrix="0.002,0,0,0,-0.499,-0.498,0,-0.998,0,0,1,0,0.499,0.5,0,1"/>
</GeometryModel3D.Transform>
</GeometryModel3D>
<AmbientLight Color="White" />
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<Viewport3D.Camera>
<OrthographicCamera Position="0.5 0.5 1" LookDirection="0 0 -1" UpDirection="0 1 0" Width="1"/>
</Viewport3D.Camera>
</Viewport3D>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
and here is the visual result:
The effect uses a data source collection with items that have two properties, an angle and a brush. It draw four quadrants (up, right, down and left) using a different brush for each quadrant. Then the whole thing is clipped to the annular region with an opacity mask.