WPF Rectangle filled with horizontal or vertical lines - wpf

I am trying to create one rectangle filled with Horizontal or Vertical lines.
The width of the rectangle is dynamic so I can not use an image brush.
Please let me know if anybody knows any solution.

I figured out a straight-forward way of doing this; finally, I used following visual brush resources to fill rectangle with horizontal, vertical or dotted vertical lines respectively
<!--for horizontal lines-->
<VisualBrush
x:Key="HorizontalLines"
TileMode="Tile" Viewport="0,0,4,4"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Path Stroke="Black" Data="M 0 10 l 10 0" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<!--For vertical lines-->
<VisualBrush
x:Key="VerticalLines"
TileMode="Tile" Viewport="0,0,4,4"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas >
<Path Stroke="Black" Data="M 0 0 l 0 10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<!--For dotted vertical lines-->
<VisualBrush
x:Key="DottedVerticalLinesWithFill"
TileMode="Tile" Viewport="0,0,10,10"
ViewportUnits="Absolute" Viewbox="0,0,10,10"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Path Stroke="Purple" Data="M 0 5l 0 -10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>

You can easily do this with a LinearGradientBrush:
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<LinearGradientBrush SpreadMethod="Reflect" StartPoint="0 0" EndPoint="0 0.05">
<GradientStop Offset="0.5" Color="Black"/>
<GradientStop Offset="0.5" Color="White"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
You control line thickness and orientation with the EndPoint property.

Have you tried using a TileBrush?

ImageBrush derives from TileBrush so you can use the Viewport property to repeat the image. See this MSDN page for an example.

Related

How to clip a line to an ellipse in XAML

I have defined two concentric circles and two diagonal lines, which I want to clip to one of the circles.
I have written the following XAML and my problem is that I do not understand its behavior.
<Canvas>
<Ellipse x:Name="OuterCircle" Width="200" Height="200" StrokeThickness="2" Stroke="Black">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFCBCFD5" Offset="1"/>
<GradientStop Color="#FFF5F6F7" Offset="0"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="InnerCircle" Width="184" Height="184" Margin="8" >
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFCBCFD5" Offset="0"/>
<GradientStop Color="#FFF5F6F7" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<!-- In my opinion the lines are clipped to the wrong circle -->
<Line X1="0" X2="200" Y1="0" Y2="200" Stroke="Black" StrokeThickness="2">
<Line.OpacityMask>
<VisualBrush Visual="{Binding ElementName=InnerCircle}" />
</Line.OpacityMask>
</Line>
<Line X1="0" X2="200" Y1="200" Y2="2" Stroke="Black" StrokeThickness="2">
<Line.OpacityMask>
<VisualBrush Visual="{Binding ElementName=InnerCircle}" />
</Line.OpacityMask>
</Line>
</Canvas>
</Grid>
</Window>
This appears as follows:
The diagonal lines are clearly clipped to the outer circle.
In the XAML they are specifically clipped to the inner circle using ElementName=InnerCircle.
Why is that?
UPDATE
Using Stretch="None" as suggested by Clemens
<Line X1="0" X2="200" Y1="0" Y2="200" Stroke="Black" StrokeThickness="2">
<Line.OpacityMask>
<VisualBrush Visual="{Binding ElementName=InnerCircle}" Stretch="None"/>
</Line.OpacityMask>
</Line>
<Line X1="0" X2="200" Y1="200" Y2="2" Stroke="Black" StrokeThickness="2">
<Line.OpacityMask>
<VisualBrush Visual="{Binding ElementName=InnerCircle}" Stretch="None"/>
</Line.OpacityMask>
</Line>
produces exactly the result I had expected.
Using Clip did not work as I expected
<Line X1="0" X2="200" Y1="0" Y2="200" Stroke="Black" StrokeThickness="2" Clip="{Binding ElementName=InnerCircle, Path=RenderedGeometry}"/>
<Line X1="0" X2="200" Y1="200" Y2="2" Stroke="Black" StrokeThickness="2" Clip="{Binding ElementName=InnerCircle, Path=RenderedGeometry}"/>
In the designer, it looked like this
Clearly the margin on the inner circle has not been accounted for. (If I set the margin to 0, the circles are not concentric, but the clipping is correct.)
Weirdly, when I start the application, the diagonal lines do not appear at all.
Brushes are stretched by default. Set Stretch to None:
<VisualBrush Visual="{Binding ElementName=InnerCircle}" Stretch="None"/>
Alternatively, you can also clip the Lines by the geometry of the Ellipse:
<Line ... Clip="{Binding ElementName=InnerCircle, Path=RenderedGeometry}"/>

WPF apply margin on ImageBrush in Ellipse?

Please take a look at my following code:
<Grid>
<Ellipse StrokeThickness="2" Stroke="White">
<Ellipse.Fill>
<ImageBrush ImageSource="someImage.png"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
What I'm trying to do is to margin the someImage.png 5px from left and 5px from top. I'm wondering if it's possible as I couldn't find any margin property available.
You could put two Ellipses on top of each other like so:
<Grid>
<Ellipse StrokeThickness="2" Stroke="White"/>
<Ellipse Margin="5" StrokeThickness="2" Stroke="White">
<Ellipse.Fill>
<ImageBrush ImageSource="someImage.png"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
The grid will put all the items in the same row/column on top of each other.

Grid Clipping- Silverlight

<Grid Height="333">
<Canvas Margin="0,-41">
<Rectangle Height="60" Width="72" Canvas.Left="73" Canvas.Top="355">
<Rectangle.Fill>
<ImageBrush Stretch="None" ImageSource="aaa.png"/>
</Rectangle.Fill>
</Rectangle>
</Canvas>
</Grid>
The problem is Rectangle is visible out of the Grid and I don't want this.
What should I do?
You need to clip the Grid. I wrote an attached property that will do this for you. See the following blog post:
http://www.scottlogic.co.uk/blog/colin/2009/05/silverlight-cliptobounds-can-i-clip-it-yes-you-can/
You can use it as follows:
<Grid Height="333" util:Clip.ToBounds="true">
<Canvas Margin="0,-41">
<Rectangle Height="60" Width="72" Canvas.Left="73" Canvas.Top="355">
<Rectangle.Fill>
<ImageBrush Stretch="None" ImageSource="aaa.png"/>
</Rectangle.Fill>
</Rectangle>
</Canvas>
</Grid>
Internally this attached behaviour sets the FrameworkElement.Clip property to the required geometry based on the current size of the element it is attached to.

Creating Button using GeometryDrawing WPF

Hi I am trying to create a Button with a semi arc form something like this:
alt text http://www.freeimagehosting.net/uploads/6f804323db.jpg
I am using xaml and Control templates, it works fine but, the button recives the click event even if i click in any part into the rectangle form by the geometry, it launch the click event, I want the event to be catch only inside the geomtry...
here is the xaml
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing x:Name="X"Geometry= "M 0,0
A .8,.8 180 1 1 0,4
L 0,3
A .6,.6 180 1 0 0,1
L 0,0">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness=".1" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Blue"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<Viewbox>
<ContentControl Margin="20" Content="{TemplateBinding Content}"/>
</Viewbox>
</Grid>
</ControlTemplate>
...
<Button Template="{StaticResource ButtonTemplate}" Click="Button_Click" HorizontalAlignment="Right">OK</Button>
Thanks for any Comments
Instead of using an Image, which will have rectangular bounds that are used for hit testing, you can use a Path element with your Geometry data. The Path will only do hit testing on the area defined by the outline. Whatever text or other content is set will also be clickable unless you set IsHitTestVisible="false" on the ContentPresenter.
<Button Content="OK">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Path Data="M 0,0
A .8,.8 180 1 1 0,4
L 0,3
A .6,.6 180 1 0 0,1
L 0,0" Stroke="Black" StrokeThickness="1" Stretch="Uniform">
<Path.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Blue"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Viewbox>
<ContentPresenter Margin="20" />
</Viewbox>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>

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