How to remap colors in a DrawingBrush? - wpf

Say that I have a DrawingBrush that has three colors hard-coded, i.e. a border, a foreground, and a background.
<!-- Resource -->
<DrawingBrush x:Key="EventIcon" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FF9200CE" Geometry="F1 M 51.2119,61.4688L 43.4193,61.4688L 43.4194,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 35.6267,77.1353L 51.2119,61.4688 Z "/>
<GeometryDrawing Brush="#FFB400FF" Geometry="F1 M 44.4789,64.2014L 40.2667,64.2667L 40.13,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 33.8667,75.1467L 44.4789,64.2014 Z "/>
<GeometryDrawing Geometry="F1 M 51.2119,61.4688L 43.4193,61.4688L 43.4194,29.318L 27.8341,29.318L 27.834,61.4688L 20.0414,61.4688L 35.6267,77.1353L 51.2119,61.4688 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="2" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" Brush="#FF3D0033"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 33.7559,53.2538L 32.6202,40.9989L 32.6202,35.3362L 37.3531,35.3362L 37.3531,40.9989L 36.2333,53.2538L 33.7559,53.2538 Z M 32.6202,59.6771L 32.6202,54.9442L 37.3531,54.9442L 37.3531,59.6771L 32.6202,59.6771 Z "/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<!-- Usage -->
<Rectangle Width="16" Height="16" Fill="{StaticResource EventIcon}" />
Question
What would be the best approach to be able to change these colors from the parent Rectangle, yet still have a default fallback?
As I write this question, I have thought to two possible solutions...
Possible Solution #1
Using a RelativeSource binding to connect each to their equivalent property, e.g. {Binding Path=BorderBrush, RelativeSource={RelativeSource AncestorType={x:type Rectangle}} however:
Rectangle being a Shape does not have BorderBrush properties;
I could not provide a default value. Specifying FallbackValue in the binding won't work as the binding would resolve and take it's default value. (Edit: As I write this, I thinking that I could possibly use the NullValue property).
Possible Solution #2
Write an attached property that takes an array of colors/brushes and then have a converter to map it to the GeometryDrawing.Brush. Provide a default value using the Binding.IsNull property as I can guarantee a null value is return if it cannot map if the attached property is null or that color is not remapped.

I would go with a dynamic resource reference, define the defaults at the application level (Application.Resources) and change them locally by adding brushes with the same key in some control's resources.

You could create attached properties for each of the three colours, and then create a default style to give them default values.
You could then override these values in your Rectangle declaration if you wanted using normal attached property syntax.
The only other way I can think of is to have the colours as static resources, which you could recreate in your Rectangle's resource dictionary if you wanted to override.

Related

Visually split Path into two side-by-side colors in WPF

If I have a rather meandering Path in my WPF app, is there a way I can make it appear as two differently-colored Paths of identical widths side-by-side? I'd rather not try to hand-code the whole thing again with slightly different values. I thought of using a Brush, but the list of Brushes doesn't appear to have one such.
Edit: I want a Path divided sharply by color, even if it curves, like this:
Made a little search, and found that also :
Two-color Path object
Timwi answer :
<StackPanel Orientation="Horizontal">
<StackPanel.LayoutTransform>
<ScaleTransform CenterX="0" CenterY="0" ScaleX="15" ScaleY="15" />
</StackPanel.LayoutTransform>
<Grid Margin="-5,0,0,0">
<Path Fill="Blue" Stroke="Transparent">
<Path.Data>
<PathGeometry>M10,10 C20,10 10,20 20,20 L20,19 C11,19 21,9 10,9</PathGeometry>
<!-- |← original path →| |← generated part →| -->
</Path.Data>
</Path>
<Path Fill="Red" Stroke="Transparent">
<Path.Data>
<PathGeometry>M10,10 C20,10 10,20 20,20 L20,21 C9,21 19,11 10,11</PathGeometry>
<!-- |← original path →| |← generated part →| -->
</Path.Data>
</Path>
</Grid>
</StackPanel>
So "playing" with margin may be much easier that the other options I told you about for what you need.
DropShadowEffect solved my issue.

How to create WPF vector images

I have an application that has icons which at the moment are PNG's. I'm trying to create vector images, however I cant seem to find any good tutorials.
There are a few that are pointing to this http://templarian.com/2011/08/06/tutorial_creating_an_icon/
but this is just exporting the file as a png.
I have seen an icon online that is in the same kind of xaml format i want
<DrawingImage x:Key="RestoreIcon">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 898.726,800.022L 913.274,800.022L 913.274,804.022L 918.607,804.022L 918.607,815.978L 904.06,815.978L 904.06,811.978L 898.726,811.978L 898.726,800.022 Z M 913.274,811.978L 906.57,811.978L 906.57,813.278L 915.964,813.278L 915.964,807.909L 913.274,807.909L 913.274,811.978 Z M 901.237,803.908L 901.237,809.278L 904.06,809.278L 904.06,804.022L 910.631,804.022L 910.631,803.909L 901.237,803.908 Z M 910.631,809.278L 910.631,807.909L 906.57,807.908L 906.57,809.278L 910.631,809.278 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="1" LineJoin="Round" Brush="#C7141414"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
This is drawing an icon from the GeometryDrawing information. I'm looking how i can go about making my own GeometryDrawing data?
Ideally i would like to be able to import my png image, draw over the tope of it and get GeometryDrawing data.
If you want to "import my png image, draw over the tope of it and get GeometryDrawing data", then the standard way is to use Blend. Or, you could use InkScape. How to do this is another subject itself, but Blend is probably where you want to start.

Re-sizing WPF Geometry Path

I'm working on a WPF application. Given a geometry string path, such as:
F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z
Is it possible to scale the drawing to a width and height (no matter how small/large the original was) while keeping the figure as a whole, and then finally return the string path representation of the new scaled figure?
There is no need to scale the values in a path geometry string. Just put it in the Data property of a Path control and set its Width, Height and Stretch properties as needed:
<Path Data="F1 M27,18 L23,26 33,30 24,38 33,46 23,50 27,58 45,58 55,38 45,18 27,18 Z"
Width="100" Height="100" Stretch="Uniform" Fill="Black"/>
Yes you can do it! The only thing you need to do, is to use a Viewbox for wrapping the item. This is a sample with a code that I had done, in this case I used the geometry as a DrawingBrush
...
<UserControl.Resources>
<DrawingBrush x:Key="Field" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="{StaticResource FieldGrassBrush}" Geometry="F1 M 91.733,119.946C 92.8352,122.738 93.9374,125.529 92.9241,129.209C 91.9107,132.889 88.7819,137.458 84.4263,139.271C 80.0707,141.084 74.4885,140.142 70.8885,137.982C 67.2885,135.822 65.6707,132.444 65.1819,129.182C 64.693,125.92 65.333,122.773 65.973,119.626L 0.16,53.9203C 0.444319,53.4758 0.728638,53.0312 3.48413,48.7023C 6.23962,44.3733 11.4663,36.16 18.5596,28C 25.653,19.84 34.613,11.7333
........
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</UserControl.Resources>
...
Then the View Box (Note that the Grid has fixed Height and Width, but it will be stretched to the Viewbox's size, in this case, in an uniform way):
...
<Viewbox Stretch="Uniform" Grid.Row="2" Grid.ColumnSpan="2">
<Grid Height="300" Width="390">
<Rectangle Fill="{DynamicResource Field}" StrokeThickness="0"/>
...

How do I use the icon that is stored in a xaml file in WPF?

Sorry for my bad English. The icon is stored in a xaml file named Icon.xaml. And I add the file to the project as a resource.
<ResourceDictionary x:Class="resources_icons_xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Canvas x:Key="appbar_acorn" Width="48" Height="48" Clip="F1 M 0,0L 48,0L 48,48L 0,48L 0,0">
<Path Width="22.3248" Height="25.8518" Canvas.Left="13.6757" Canvas.Top="11.4012" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 16.6309,18.6563C 17.1309,8.15625 29.8809,14.1563 29.8809,14.1563C 30.8809,11.1563 34.1308,11.4063 34.1308,11.4063C 33.5,12 34.6309,13.1563 34.6309,13.1563C 32.1309,13.1562 31.1309,14.9062 31.1309,14.9062C 41.1309,23.9062 32.6309,27.9063 32.6309,27.9062C 24.6309,24.9063 21.1309,22.1562 16.6309,18.6563 Z M 16.6309,19.9063C 21.6309,24.1563 25.1309,26.1562 31.6309,28.6562C 31.6309,28.6562 26.3809,39.1562 18.3809,36.1563C 18.3809,36.1563 18,38 16.3809,36.9063C 15,36 16.3809,34.9063 16.3809,34.9063C 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z "/>
</Canvas>
</ResourceDictionary >
Now,I wanna use the icon in a window's titlebar. How can I do this?
The Window.Icon is an ImageSource. So you should not use a Canvas here. Try changing it to some DrawingImage like this:
<DrawingImage x:Key="appbar_acorn">
<DrawingImage.Drawing>
<GeometryDrawing Brush="{DynamicResource BlackBrush}" Geometry="F1 M 16.6309,18.6563C 17.1309,8.15625 29.8809,14.1563 29.8809,14.1563C 30.8809,11.1563 34.1308,11.4063 34.1308,11.4063C 33.5,12 34.6309,13.1563 34.6309,13.1563C 32.1309,13.1562 31.1309,14.9062 31.1309,14.9062C 41.1309,23.9062 32.6309,27.9063 32.6309,27.9062C 24.6309,24.9063 21.1309,22.1562 16.6309,18.6563 Z M 16.6309,19.9063C 21.6309,24.1563 25.1309,26.1562 31.6309,28.6562C 31.6309,28.6562 26.3809,39.1562 18.3809,36.1563C 18.3809,36.1563 18,38 16.3809,36.9063C 15,36 16.3809,34.9063 16.3809,34.9063C 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z">
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
Then use it for your Window like this:
<Window ...
Icon="{StaticResource appbar_acorn}">
<!-- ... -->
</Window>
If you import the resource file inside the Window scope, you have to use DynamicResource instead:
<Window ...
Icon="{DynamicResource appbar_acorn}">
<!-- ... -->
</Window>

Drawing a 2px Hatch brush in WPF

I'm trying to build a WPF DrawingBrush that will draw a hatch pattern using two 1px by 1px rectangles. The resulting pattern would look like the background on classic Macintosh apps.
Here's what I'm working with:
<Canvas SnapsToDevicePixels="True">
<Canvas.Background>
<DrawingBrush x:Name="gridBackgroundBrush"
Viewport="0,0,10,10"
ViewportUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M0,0 L10,0 10,10, 0,10Z" Brush="Green"/>
<GeometryDrawing Geometry="M10,10 L20,10 20,20, 10,20Z" Brush="Green" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
</Canvas>
Everything looks clear and sharp, except that the boxes are way too big. As I adjust the Viewport on the brush, things start to get blurry. It looks like the anti-aliasing is what is killing me; it wants to use 3px to fade from solid green to nothing, which doesn't work when I get to sizes below 3-4px. Is there anything I can do to totally disable the anti-aliasing and do pixel-precise drawing?
Offset the drawing with 0.5px and you'll get rid of the antialiasing effect. It happens because drawing is done on the edge of the pixel offset, rather than on the actual pixel offset that you have specified. By offset'ing X,Y with half a pixel, you tell the drawing engine to draw on the pixel itself, which eliminates the need of antialiasing.
For some reason this doesn't work with gradientbrushes though.
This behaviour is similar to what you have in Quarts drawing on Mac.
Note: It's not the viewport that you should offset, but the actual shape you're drawing when using the specified brush.
For a more complete answer, please read this article.

Resources