Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Gradient Brush along a Circular Path

Tags:

c#

gradient

wpf

I need to create a multi-step gradient along a circular path, as demonstrated in the following image:

Wheel Gradient

Does anyone have any ideas on how this could be accomplished in XAML rather than code? Would it be possible to use the existing gradient brushes or composite them somehow to achieve this effect?

like image 640
Charlie Avatar asked Jan 29 '11 21:01

Charlie


2 Answers

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:

enter image description here

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.

like image 121
Rick Sladkey Avatar answered Nov 17 '22 10:11

Rick Sladkey


In GDI+/Winforms you can use the PathGradientBrush to do this:

  • https://web.archive.org/web/20140906083853/http://bobpowell.net/pgb.aspx
  • https://learn.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.

like image 41
Patrick Klug Avatar answered Nov 17 '22 11:11

Patrick Klug