VisualBrush color not changing when selected - wpf

I've created a VisualBrush in WPF to give me a wavy underline for a character.
<VisualBrush x:Key="WavyBrush">
<VisualBrush.Visual>
<Path Data="M 0,2 L 2,0 4,2 6,0 8,2 10,0 12,2" Stroke="Black" />
</VisualBrush.Visual>
</VisualBrush>
It works great except when I put the result in a DataGrid and select the row. If an unselected row has black text with white background and selecting the row turns the text white, (and the background blue or some other color) my VisualBrush with Stroke="Black" stays black, doesn't go white and looks confusing.
Is there a way to get the brush to act like color of text?

You can use DataTrigger to change the color of the brush when the row is selected. The code uses ListBox but you could easily adapt it to use DataGrid.
<Window.Resources>
<VisualBrush x:Key="WavyBrush">
<VisualBrush.Visual>
<Path Data="M 0,2 L 2,0 4,2 6,0 8,2 10,0 12,2" Stroke="Black" />
</VisualBrush.Visual>
</VisualBrush>
<VisualBrush x:Key="whiteWavyBrush">
<VisualBrush.Visual>
<Path Data="M 0,2 L 2,0 4,2 6,0 8,2 10,0 12,2" Stroke="White" />
</VisualBrush.Visual>
</VisualBrush>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="50" Width="350">
<Border x:Name="border" Width="350" Height="10" VerticalAlignment="Bottom" Background="{StaticResource WavyBrush}">
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="border" Property="Background" Value="{StaticResource whiteWavyBrush}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

If anyone else is interested, the DrawingBrush acts like text and changes color when selected.
<DrawingBrush x:Key="TextBoxWavyBrush">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M 0,2 L 2,0 4,2 6,0 8,2 10,0 12,2">
<GeometryDrawing.Pen>
<Pen Thickness="1" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" Brush="{Binding RelativeSource={RelativeSource AncestorType=TextBox, Mode=FindAncestor}, Path=Foreground}"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>

Related

WPF. GeometryDrawing's Brush is always only Transparent

Here is a test app that just shows a hatched ellipse in a window. No matter how I write it, the background of the ellipse is transparent; not the color I'm setting the GeometryDrawing. I'm stumped. The resulting picture shows the background of the ellipse as transparent when it should be Green.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Background="Red"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<DrawingBrush x:Key="HatchBrush"
Stretch="UniformToFill"
TileMode="Tile"
Viewbox="0 0 10 10"
ViewboxUnits="Absolute"
Viewport="0 0 10 10"
ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<GeometryGroup>
<LineGeometry StartPoint="0 0"
EndPoint="10 0" />
<LineGeometry StartPoint="0 0"
EndPoint="0 10" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Yellow"
EndLineCap="Square"
StartLineCap="Square"
Thickness="3" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Ellipse x:Name="c_Ellipse"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="{StaticResource HatchBrush}"
Stroke="Black" />
</Grid>
</Window>
This is what I get:
The background of the ellipse should be Green:
<GeometryDrawing Brush="Green">
There is no filled geometry that uses the GeometryDrawing's Brush.
You may use a RectangleGeometry instead of two LineGeometries:
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,10,10"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Yellow" Thickness="1.5"/>
</GeometryDrawing.Pen>
</GeometryDrawing>

Can't create multiple instances of Geometry Drawing [duplicate]

This question already has answers here:
Content of a Button Style appears only in one Button instance
(3 answers)
Closed 4 years ago.
Icons in my application are stored as geometry drawings in a resource dicitony. for example:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="ViewboxIconClose"
Width="16"
Height="16">
<Rectangle Width="16" Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1M9.4141,8L13.9571,12.543 12.5431,13.957 8.0001,9.414 3.4571,13.957 2.0431,12.543 6.5861,8 2.0431,3.457 3.4571,2.043 8.0001,6.586 12.5431,2.043 13.9571,3.457z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Viewbox>
This icon is used like this:
<Button>
<StaticResource ResourceKey="ViewboxIconClose" />
</Button>
Now my Problem:
if i use this geometry somewhere else, it will only work at one place. for example if i use this geometry in a menu, the geometry on the button will disapear in the moment, i open the menu.
You could use x:Shared=false to solve this but I'd probably use a style and an image instead. The image should be more efficient than a rectangle with a brush and a viewbox.
Title="MainWindow" Height="350" Width="525"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
>
<Window.Resources>
<Style x:Key="CloseIcon" TargetType="Image">
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="Source">
<Setter.Value>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1M9.4141,8L13.9571,12.543 12.5431,13.957 8.0001,9.414 3.4571,13.957 2.0431,12.543 6.5861,8 2.0431,3.457 3.4571,2.043 8.0001,6.586 12.5431,2.043 13.9571,3.457z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Grid Height="100" Width="100" Background="Red">
<Image Style="{StaticResource CloseIcon}"/>
</Grid>
<Grid Height="30" Width="30" Background="Blue">
<Image Style="{StaticResource CloseIcon}"/>
</Grid>
</StackPanel>
</Grid>
With this specific requirement you could just use one geometry as data for a path. Geometries aren't visuals.

VisualBrush of geometry DrawingImage is blurry and cut off

I would like to draw circular geometry in VisualBrush (in order to create an OpacityMask), the result however are of rather low quality:
This image is 500% zoomed in but the cut off of the circle is apparent (especially on top and bottom) and even at original size the mask is rather blurry. The image was generated with following code:
<Border Background="Blue">
<Border.OpacityMask>
<VisualBrush TileMode="None" Stretch="Uniform" AlignmentX="Center" AlignmentY="Center">
<VisualBrush.Visual>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="0,0" RadiusX="1" RadiusY="1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
How can I fix the mask so that is neither blurry nor cut off?
This may be better. At least it is simpler.
<Border Background="Blue">
<Border.OpacityMask>
<DrawingBrush Stretch="Uniform" AlignmentX="Center" AlignmentY="Center">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="1" RadiusY="1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>
</Border>

Nothing displayed when set a line to DashStyles.Dot

<DockPanel LastChildFill="True" Height="18">
<Image Height="18" Width="80">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<LineGeometry StartPoint="0,9" EndPoint="38,9" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen DashCap="Flat"
Brush="Black"
Thickness="0.5"
DashStyle="{x:Static DashStyles.Dot}, Mode=OneTime}"
/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Height="18" Text="{Binding Name, Mode=OneTime}" Margin="3,2,5,0" />
</DockPanel>
Remove DashCap="Flat" and you will see the dots. There are other DashCaps that will work: Round, Square, Triangle.

WPF drawing that stretches without stretching the Pen

I need to draw some simple lines within a Border control (or similar) that always stretch to the bounds of the Border. Is there a way to stretch the lines only but not its pen? Without involving lots of C#?
In this version the lines stretch:
<Border>
<Border.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Red">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0 100,1000" />
<LineGeometry StartPoint="0,0" EndPoint="100,1000"/>
<LineGeometry StartPoint="100,0" EndPoint="0,1000"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="20" Brush="Black"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.Background>
</Border>
The best solution I have come up with is this:
<Border>
<Grid>
<Path Stretch="Fill" Fill="Red" Stroke="Black" StrokeThickness="4" Data="M0,0 L100,0 100,1000 0,1000 z" />
<Path Stretch="Fill" Stroke="Black" StrokeThickness="4" Data="M 0,0 L0,0 100,1000" />
<Path Stretch="Fill" Stroke="Black" StrokeThickness="4" Data="M 100,0 L100,0 0,1000" />
</Grid>
</Border>
But isn't there a better solution? That doesn't involve extra Grid?
I've done this by scaling a Path's Data, not the visual component.
Place a Path in a Canvas.
Set path.Data to a Geometry representing your data as percentages of the logical range.
Set path.Data.Transform to a ScaleTransform with ScaleX and ScaleY bound to the actual width and height.
Within a line, you can bind the width (or height, depending on which way you are drawing the line) to that of the parent container to achieve what you want.
<Grid x:Name="Grid" Margin="10">
<Border BorderBrush="Black" BorderThickness="1" />
<Line X1="0" X2="{Binding ElementName=Grid, Path=ActualWidth}" Y1="1" Y2="1" Stroke="Red" Margin="0,10,0,0" />
<Line X1="0" X2="{Binding ElementName=Grid, Path=ActualWidth}" Y1="1" Y2="1" Stroke="Green" Margin="0,30,0,0" />
<Line X1="0" X2="{Binding ElementName=Grid, Path=ActualWidth}" Y1="1" Y2="1" Stroke="Blue" Margin="0,50,0,0" />
</Grid>
Edit: Here is another way without using binding
<Border BorderBrush="Black" BorderThickness="1" >
<Path Stroke="Red" StrokeThickness="1" Data="M0,0 1,0Z" Stretch="Fill" />
</Border>
None that I know of. But unless you're doing something really extravagant, it really isn't a lot of effort to override OnRender and draw it yourself:
public class CustomBorder : Border
{
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
dc.DrawLine(new Pen(BorderBrush, BorderThickness.Top), new Point(0, 0), new Point(ActualWidth, ActualHeight));
}
}
Result:

Resources