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

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.

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>

VisualBrush color not changing when selected

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>

wpf canvas add element from resources

I am new to wpf
<Window.Resources>
<Ellipse x:Key="connectorNode" Height="20" Width="20" Fill="Green" Stroke="Black" StrokeThickness="2" MouseMove="Ellipse_MouseMove" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown" MouseLeftButtonUp="Ellipse_MouseLeftButtonUp"></Ellipse>
</Window.Resources>
How can i add an instance of the Ellipse in the resources to a canvas, i only want to specify Canvas.Left and Canvas.Right but use the same property values as in resources
<Canvas>
</Canvas>
It seems like you want to create a generic style and apply it to every Ellipse added, this is how you can do it:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="EllipseStyle" TargetType="Ellipse">
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="20" />
<EventSetter Event="Control.MouseMove" Handler="Control_MouseMove" />
</Style>
</Window.Resources>
<Grid>
<Canvas>
<Ellipse Style="{StaticResource EllipseStyle}" />
</Canvas>
</Grid>
</Window>
Edit: I've added "EventSetter" so your events can be defined in the style (see this post).
If you really want to use an element from resources and apply only canvas properties you can do it this way:
<Window.Resources>
<ControlTemplate x:Key="connectorNode" >
<Ellipse
Height="20"
Width="20"
Fill="Green"
Stroke="Black"
StrokeThickness="2"
MouseMove="Ellipse_MouseMove"
MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp" />
</ControlTemplate>
</Window.Resources>
<Canvas>
<ContentControl Template="{StaticResource connectorNode}"/>
<ContentControl Canvas.Left="50" Template="{StaticResource connectorNode}"/>
</Canvas>

Why does WPF render two identical objects differently?

Take this Window as an example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" ResizeMode="NoResize" SizeToContent="WidthAndHeight" SnapsToDevicePixels="True">
<Grid Width="17" Margin="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="1" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="1" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
</Grid>
</Window>
Once the application has been ran, the topmost RepeatButton is taller than the bottom one (consequently the top triangle is also bigger than the bottom one). Why?
If I create 4 rows of identical RepeatButtons, then the 1-st and 3-rd RepeatButtons are of equal size and are bigger than the 2-nd and 4-th RepeatButton?!?
I'm thinking this must be a bug in the WPF layout system, but how to work around this problem? I can't use fixed heights (which does solve the problem), because I need the RepeatButtons and triangles to strecth as the container gets bigger (the example I provided is simplifed just to show the issue, I know I can't resize the example window...).
Edit:
In reply to Ben's comments:
Yes, with the added style the triangles do come out as 9px and 8px tall (I could just as well through out the RepeatButtons alltogether and leave only the polylines as the grids children, that would give the same result). Because the triangles are equal sided, then giving the grid a width of 17 will indeed cause the height to become 17 as well, which of course is not enough for two equal height triangles..
What I'm actually trying to do is create a NumericUpDown control. I've found that by default a spinner width of 17 and a UserControl MinHeight of 24 looks very good. The only problem is, that if I drop this UserControl into a Grid, then the top triangle always pushes itself 1px to tall, ruining the look. No matter how I've tried to mingle with the internal Margins and Paddings, the top triangle always makes itself 1px taller than necessary. So in essence what I want is to have a NumericUpDown, that when put into a Grid, doesn't distort itself. By default it should look perfect from the get go (no Grid RowHeight="Auto") and scale properly (no fixed heights). It must be possible, because by looking at the pixels physically then everything can fit into the given dimensions nicely.
Here is my NumericUpDown, I've stripped out all the non essential things to make it more compact:
<UserControl x:Class="HRS.NumericUpDown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
MinWidth="40" MinHeight="24" Name="ucNUPD" Background="White" SnapsToDevicePixels="True">
<UserControl.Resources>
<Style TargetType="{x:Type RepeatButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Name="borderOuter" BorderThickness="1" BorderBrush="Red">
<Border Name="borderInner" BorderThickness="1" BorderBrush="Blue">
<ContentPresenter Margin="2,1" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="triangleStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Polyline HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Green" RenderOptions.EdgeMode="Aliased" Stretch="Uniform">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Border BorderThickness="1" BorderBrush="#ABADB3">
<DockPanel>
<UniformGrid Margin="1" DockPanel.Dock="Right" Rows="2" MinWidth="17" Width="17">
<RepeatButton Name="repeatButtonUp" Grid.Row="0">
<ContentControl Style="{StaticResource triangleStyle}" />
</RepeatButton>
<RepeatButton Name="repeatButtonDown" Grid.Row="1">
<ContentControl Style="{StaticResource triangleStyle}" RenderTransformOrigin="0.5, 0.5">
<ContentControl.RenderTransform>
<ScaleTransform ScaleY="-1" />
</ContentControl.RenderTransform>
</ContentControl>
</RepeatButton>
</UniformGrid>
<TextBox BorderThickness="0" VerticalContentAlignment="Center" Text="0" />
</DockPanel>
</Border>
</UserControl>
Here is a picture of what the end result looks like:
(The image doesn't fit a 100%, but you can still see all the relevant details).
On the right side you can see a zoom-in of the NumericUpDowns. The bottom one looks correct, but only because the grid's row Height is set to Auto. The top one is distorted, but by default I want it to look exactly like the bottom one.
Hmmm...
I might just have found a workable solution:
It seems that by setting the Margin of the ContentPresenter in my NumericUpDown to "3,1", everything looks perfect. Preliminary testing is very promising as everything seems to be exactly the way it should be...
I'll test it some more tommorow and if all goes good will mark Ben's answer as correct :)
With SizeToContent="WidthAndHeight" the height will be 17 as you you set the Grid's Width to 17. But with 17/2=8.5 one row will be 9 (rounding occurs becouse of SnapsToDevicePixels="True") but the other will be 8 pixel tall. If you set the Width to 18 they will be equal.
Proof of my theory:
<Grid Width="17" Margin="0">
<Grid.Resources>
<Style TargetType="{x:Type RepeatButton}">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<ContentPresenter Margin="0"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
With this snippet you gain a Triangle that has 9 pixel height, and one with 8 pixels.
But if you want for a solution try this:
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True" Height="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth}" >
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
This way the width and the height of the buttons will be equal.
If think you can write a little converter too, that will can do some nasty things:
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True" Height="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth, Converter={StaticResource TriangleWidthConverter}}" >
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
public class TriangleWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int width = 0;
if (int.TryParse(value.ToString(), out width))
return width + 1; // Do some fun here.
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I hope these will help.
No idea why you are seeing this behavior, but this might work to fix it: try setting the height on each of your your rows to .5* if you have 2 rows or .25* if you have 4 rows (or .1* if you have 10 rows, etc.).

Cant get right label size

I need to write a control, which will look like this:
Click Here to se callout
Problem is that i cant get label's real size to redraw my rectangle geometry. Label's height is always much bigger then space that it really occupies on the screen. I dont know, what to. Here is code:
<Popup x:Class="Controls.Callout"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<Grid>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing >
<GeometryDrawing Brush="Orange" x:Name="geometryDrawing">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="2"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<RectangleGeometry x:Name="rectangel"
RadiusX="15" RadiusY="15"
Rect="0,30, 300,100"
/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry>
<PathFigure StartPoint="30,30" IsClosed="False">
<PolyLineSegment Points="15,0, 90,30"/>
</PathFigure>
</PathGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<Label Padding="5 20" MaxWidth="300" Name="myLabel" FontSize="16" >
<!--Content="{Binding}">-->
<!--MaxHeight="100" MaxWidth="300">-->
<AccessText TextWrapping="Wrap" MaxHeight="50"/>
</Label>
</Grid>
</Popup>
Code behind:
internal partial class Callout : Popup
{
public Callout()
{
InitializeComponent();
}
protected override void OnOpened(System.EventArgs e)
{
rectangel = new RectangleGeometry(new Rect(0,30,300, myLabel.Height/2));
}
}
Why not use a Grid with a Rectangle with rounded corners, a TextBlock, and some shape on top of the rectangle to make the callout "pointer"? As a Grid, the rectangle can automatically expand to the full size needed by the TextBlock.
For example, you can create a UserControl (or a full-fledged templated control) and given it a MaxWidth so that the text wraps. Then put the control in a Canvas so that it can determine its own size.
<UserControl MaxWidth="200">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="1" RadiusX="50" RadiusY="50"
StrokeThickness="8" Stroke="Gray" />
<!-- Here will be pointer in Grid.Row="0"-->
<TextBlock Grid.Row="1" Name="myLabel" Margin="20" Foreground="Black"
Text="This is the textblock....." FontSize="20" TextWrapping="Wrap" />
</Grid>
</UserControl>

Resources