Draw Image Outside of Layout in WPF - wpf

I'm creating a style to customize a RadioButton so that the RadioButton can display a star image behind the Bullet. I have the star being drawn with an ImageBrush as the background of a 20x20 grid that holds the Bullet layout. It's working fine, except I don't want the overall height of the RadioButton to be 20px tall. So I want to make the grid only 10x10 but still have the star 20x20 and centered behind the bullet (therefore the star's top left coordinate would be -5,-5 relative to the top left of grdBullet). How can I draw the star image behind or outside of the layout?
Excerpt of my style:
....
<ImageBrush x:Key="StarBrush" ImageSource="/Common;component/Resources/Images/FavoriteStar_FrontFacing_24x24_96.png" />
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator VerticalAlignment="Center">
<BulletDecorator.Bullet>
<Grid Name="grdBullet" Height="20" Width="20" Background="{StaticResource StarBrush}">
<Grid Width="10" Height="10" HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Name="RadioOuter" Fill="#FFF4F4F4" Stroke="#FF8E8F8F" StrokeThickness="1"/>
<Ellipse Name="RadioInner" StrokeThickness="1" Margin="2" Fill="{StaticResource RadioInnerDefaultFill}" Stroke="{StaticResource RadioInnerDefaultStroke}" />
<Ellipse Name="RadioChecked" StrokeThickness=".75" Margin="2.5" Stroke="#FF193B55" Fill="{StaticResource RadioCheckedFill}" Visibility="Hidden" />
<Border CornerRadius="0" Margin="4" Name="RadioMark" Background="#FFADADAD" Visibility="Hidden" />
</Grid>
</Grid>
</BulletDecorator.Bullet>
<!--Text element-->
<TextBlock Margin="3,0,0,0" VerticalAlignment="Center" Foreground="{TemplateBinding Foreground}" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}">
<ContentPresenter />
</TextBlock>
</BulletDecorator>
....

Two words: Negative margins.
<BulletDecorator.Bullet>
<Grid Width="10" Height="10" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Height="20" Width="20" Margin="-5" Fill="{StaticResource StarBrush}" />
...
</Grid>
</BulletDecorator.Bullet>
For a more general purpose, you could set the Margin using a MultiValueConverter that returns a Thickness. For example, Margin.Left would be equal to -(SelfWidth - ParentWidth) / 2.

You can render anything anywhere with negative margins, no need for canvas. Try this (i have assumed -5,5 is center of image otherwise you need to put -15 15 or even -25 25
<ImageBrush x:Key="StarBrush"
ImageSource="/Common;component/Resources/Images/FavoriteStar_FrontFacing_24x24_96.png"
Margin="-5 5 0 0" />

Related

WPF Border ZIndex not working as expected

I have the following problem: I overlay 2 controls over one another and want a single border to surround them both. I thought I could control the ZIndex of the border over each control and give them a low ZIndex: 2. Then give the controls a higher ZIndex (4 and 5). The following xaml as a UserControl is what I'm referring to:
<Canvas>
<Border BorderBrush="Black" BorderThickness="4" Canvas.ZIndex="2" Canvas.Left="50" Canvas.Top="30">
<Rectangle Width="200" Height="20" Fill="Aqua" Canvas.ZIndex="5"/>
</Border>
<Border BorderBrush="Black" BorderThickness="4" Canvas.ZIndex="2" Canvas.Left="150" Canvas.Top="00">
<Rectangle Width="50" Height="200" Fill="Yellow" Canvas.ZIndex="5"/>
</Border>
</Canvas>
The result looks like:
But I desire is the following image but with the border with red-X removed:
Can anyone recommend a way to do this ? The Border ZIndex method is not working.
Thanks!
ZIndex is not working cause Rectangles are not childs of Canvas. So you can fix it by placing them outside of borders and adjusting its Heights, Widths and Canvas.Left, Canvas.Top properties. So the example looks like this.
<Canvas>
<Border BorderBrush="Black" BorderThickness="4" Canvas.ZIndex="2"
Canvas.Left="46" Canvas.Top="26"
Width="208" Height="28">
</Border>
<Rectangle Width="200" Height="20" Fill="Aqua" Canvas.ZIndex="5"
Canvas.Left="50" Canvas.Top="30" />
<Border BorderBrush="Black" Height="208" Width="58" BorderThickness="4"
Canvas.ZIndex="2" Canvas.Left="146" Canvas.Top="00">
</Border>
<Rectangle Canvas.Left="150" Canvas.Top="4" Width="50" Height="200" Fill="Yellow"
Canvas.ZIndex="5"/>
</Canvas>

Can I make two containers (e.g. Grid and Canvas) take the same space to combine their abilities?

I have the following layout:
The Rectangles are placed using a Grid. On top of that, I want to add more "fluid" stuff, like Paths and lines that would be located dynamically.
For instance:
the lines between the Rectangles, are stretched from one Rectangle's mid-point to the mid-point of the one below it
The left-manually-drawn-ugly-red-"path" originate from the mid-point of the left-half of the top-Rectangle, and go to the mid-point of the left Rectange below it.
So the question is: the Rectangles match Grid behavior, other stuff, like the lines, match Canvas behavior. How do I use the advantages of both these containers? can I lay one over the other?
You don't need to mix and match your controls at all... you can chose either a Gird or a Canvas control to draw on using a Path element. Clearly, I don't want to do it all for you, so this is just a basic example of drawing in a Grid:
The end result:
The XAML:
<Grid Width="800">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Stroke" Value="Black" />
<Setter Property="Fill" Value="White" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="Width" Value="250" />
<Setter Property="Height" Value="100" />
<Setter Property="Rectangle.RadiusX" Value="20" />
<Setter Property="Rectangle.RadiusY" Value="20" />
</Style>
</Grid.Resources>
<Path Grid.Row="1" Grid.RowSpan="2" Grid.Column="0" Data="M0,0 0,100" Stroke="Black" StrokeThickness="2" Fill="{x:Null}" Height="100" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<Path Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" Data="M0,0 0,100" Stroke="Black" StrokeThickness="2" Fill="{x:Null}" Height="100" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<Rectangle Grid.Row="0" Grid.ColumnSpan="2" />
<Rectangle Grid.Row="1" Grid.Column="0" />
<Rectangle Grid.Row="1" Grid.Column="1" />
<Rectangle Grid.Row="2" Grid.Column="0" />
<Rectangle Grid.Row="2" Grid.Column="1" />
<Path Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Data="M0,0 A 100,100 90 0 0 -100,100" Stroke="Red" StrokeThickness="2" Fill="{x:Null}" Height="100" VerticalAlignment="Stretch" HorizontalAlignment="Center" Margin="0,0,150,0" />
<Path Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Data="M0,0 A 100,100 90 0 1 100,100" Stroke="Red" StrokeThickness="2" Fill="{x:Null}" Height="100" VerticalAlignment="Stretch" HorizontalAlignment="Center" Margin="0,0,-250,0" />
</Grid>
You can also find the syntax that you need to use in the Path.Data property in the Path Markup Syntax page on MSDN.
Yes you can lay the Canvas on top of a Grid, but you probably don't want to.
<Grid x:Name="container">
<!-- We use this to put the two items in the same location -->
<!-- i.e. Row="0" Column="0" is implicit for both the canvas and the grid below-->
<Grid x:Name="rectangleGrid"/>
<Canvas x:Name="shapeCanvas"/>
</Grid>
It really is that simple, but lets have a look at what we have now.
The shapeCanvas will be in front of the rectangleGrid (and if it isn't just tweak its ZIndex). If it has a non-transparent BackColor then you won't of course see the rectangleGrid, so you will need to sort that out.
If we want to line up the right hand red-line of yours we need to work out where to draw it from. Given that gridColumns don't expose sizes, then that's leftRectangle.ActualWidth + leftRectangle.Margin.Left + leftRectangle.Margin.Right + rightRectangle.Margin.Left + (rightRectangle.ActualWidth/2) and topRectangle.ActualHeight + topRectangle.Margin.Top + someConstantForHowTallThatSpacerRowIs + rightRectangle.Margin.Top + (rightRectangle.Height/2). Ouch
If we resize the container, then the rectangleGrid will also resize, but if you have used start-sizing for your columns then the rectangles all just resized. Now I have to go and recalculate all the sizes again.
So at this point, I'd start asking myself if I really wanted the rectangleGrid to handle the sizing or maybe I should just put everything into the Canvas.
You don't need to resize (although be careful because there are lots of high DPI screens out there now)
If you resize then the sizes are much simpler (e.g. if we assume that our margins are rectangles are 3/4 of the size and the margins 1/8 each side) so that redline top point now becomes shapeCanvas.Size *11/16 and (shapeCanvas.Height - someConstantForHowTallThatSpacerRowIs)/14

WPF : Rounded-Corners Images

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black">
<!-- Rounded yellow border -->
<Border BorderThickness="3" BorderBrush="Yellow" CornerRadius="10" Padding="2"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<!-- Rounded mask (stretches to fill Grid) -->
<Border Name="mask" Background="White" CornerRadius="7"/>
<!-- Main content container -->
<StackPanel>
<!-- Use a VisualBrush of 'mask' as the opacity mask -->
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/>
</StackPanel.OpacityMask>
<!-- Any content -->
<Image Source="http://chriscavanagh.files.wordpress.com/2006/12/chriss-blog-banner.jpg"/>
<Rectangle Height="50" Fill="Red"/>
<Rectangle Height="50" Fill="White"/>
<Rectangle Height="50" Fill="Blue"/>
</StackPanel>
</Grid>
</Border>
</Page>
This XAML is from WPF – Easy rounded corners for anything but it doesn't work form me =(
<Border Canvas.Left="55"
Canvas.Top="30"
Width="100"
Height="Auto"
Margin="12,12,8,0"
VerticalAlignment="Top"
BorderBrush="#FF3B5998"
BorderThickness=".5"
CornerRadius="18">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Opacity=".5"
ShadowDepth="3" />
</Border.Effect>
<Border Name="ReceiverColor"
BorderBrush="#FF96B2E4"
BorderThickness="6"
CornerRadius="15">
<Border Name="Mask"
BorderBrush="#FF3B5998"
BorderThickness=".5"
CornerRadius="13">
<StackPanel>
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Mask}" />
</StackPanel.OpacityMask>
<Image Name="Receiver" />
</StackPanel>
</Border>
</Border>
</Border>
--- EDIT ---
I make borders sizes to auto and change source of image to an image from a link
when window loaded border size becomes as image size but image not shown !!!
You can define a <Border/> element and set its <Border.Background/>
property to an <ImageBrush/> , set the Borders CornerRadius property and you
are all set!
<Border CornerRadius="8,0,8,0">
<Border.Background>
<ImageBrush Stretch="Fill" ImageSource="ImageSource"/>
</Border.Background>
</Border>
You forgot the Grid that makes the mask and the image siblings and nested the image in the mask. and you forgot to set the background of the mask.
This works:
<Grid>
<Border Canvas.Left="55"
Canvas.Top="30"
Width="100"
Height="Auto"
Margin="12,12,8,0"
VerticalAlignment="Top"
BorderBrush="#FF3B5998"
BorderThickness=".5"
CornerRadius="18">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Opacity=".5"
ShadowDepth="3" />
</Border.Effect>
<Border Name="ReceiverColor"
BorderBrush="#FF96B2E4"
BorderThickness="6"
CornerRadius="15">
<Grid>
<Border Name="Mask"
Background="White"
BorderBrush="#FF3B5998"
BorderThickness=".5"
CornerRadius="13">
</Border>
<StackPanel>
<Image Name="Receiver"
Source="/Images/test.jpg" />
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Mask}" />
</StackPanel.OpacityMask>
</StackPanel>
</Grid>
</Border>
</Border>
</Grid>
in wpf this one works for me
<Ellipse Width="50" Height="50">
<Ellipse.Fill>
<ImageBrush ImageSource="http://chriscavanagh.files.wordpress.com/2006/12/chriss-blog-banner.jpg" />
</Ellipse.Fill>
</Ellipse>
None of the above answers worked for me completely. I was trying to implement rounded corners on image which could be resized and has properties Stretch="UniformToFill" VerticalAlignment="Center" and HorizontalAlignment="Center".
The center alignments keeps the middle part cropped as opposed to bottom and right side being cropped, when image is resized. Solutions with image brush were working but I was facing problem in keeping the content at center cropped.
The marked answer has a problem with transparent non rectangular images as the "mask" border will end up showing as white background. Following was the imlementation which worked for me:
<Grid>
<WrapPanel Name ="container">
<Image Source="sample_image.png" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="UniformToFill"/>
<WrapPanel.OpacityMask>
<VisualBrush >
<VisualBrush.Visual>
<Border Height="{Binding ElementName=container, Path=ActualHeight}"
Width="{Binding ElementName=container, Path=ActualWidth}"
Background="White" CornerRadius="15" />
</VisualBrush.Visual>
</VisualBrush>
</WrapPanel.OpacityMask>
</WrapPanel>
</Grid>
You can use an ellipse like how Usman Ali has said (I thought this myself and I didn't take it from him)
It's very simple, make an ellipse with the properties you want, then set the fill to an imagebrush with your desired image like this in XAML:
<Ellipse Height="Auto" Width="100">
<Ellipse.Fill>
<ImageBrush ImageSource="YOUR IMAGE SOURCE/LINK HERE"/>
</Ellipse.Fill>
</Ellipse>
In C#, if in any case you want to do in C#:
Ellipse YourEllipseName = new Ellipse
{
Height = 50,
Width = 50,
StrokeThickness = 0,
Fill = new ImageBrush
{
Stretch = Stretch.Uniform,
ImageSource = new BitmapImage(new Uri("YOUR IMAGE SOURCE HERE"))
}
};
<Grid Background="Black">
<Rectangle RadiusX="20" RadiusY="20"
Width="130"
Height="130">
<Rectangle.Fill>
<ImageBrush x:Name="myImage" ImageSource="C:\Path\Desktop\visual-studio-2010-logo.png"/>
</Rectangle.Fill>
</Rectangle>
</Grid>

WPF drop shadow

Whenever I set the Border.Effect property to a drop shadow effect every control contained within the control has a drop shadow.
Is there a way to set the shadow just to the border and not every control contained in the border?
Here is a short example of my code:
<Grid>
<Border Margin="68,67,60,67" BorderBrush="Black" BorderThickness="1" CornerRadius="10">
<Border.Effect>
<DropShadowEffect/>
</Border.Effect>
<Rectangle Fill="White" Stroke="Black" Margin="37,89,118,98" />
</Border>
</Grid>
Two choices:
Option 1: Add a border element with the effect on it as a sibling of the border / rectangle element tree you have. Something like this:
<Grid>
<Border Margin="68,67,60,67"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="10">
<Border.Effect>
<DropShadowEffect />
</Border.Effect>
</Border>
<Border Margin="68,67,60,67"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="10">
<Rectangle Fill="White"
Stroke="Black"
Margin="37,89,118,98">
</Rectangle>
</Border>
</Grid>
Option 2: Put the rectangle as a sibling of the border element like this:
<Grid>
<Border Margin="68,67,60,67"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="10">
<Border.Effect>
<DropShadowEffect />
</Border.Effect>
</Border>
<Rectangle Fill="White"
Stroke="Black"
Margin="37,89,118,98">
</Rectangle>
</Grid>
NOTE: You will have to tweak the layout on the second solution to make the rectangle line up where you want it
I realise that your question has an answer, but it doesn't appear to have the simplest answer. The simplest answer to your question is for you to just colour the background of the control that you set the shadow on. Like so:
<Grid>
<Border Margin="68,67,60,67" Background="White" BorderBrush="Black"
BorderThickness="1" CornerRadius="10">
<Border.Effect>
<DropShadowEffect/>
</Border.Effect>
<Rectangle Fill="White" Stroke="Black" Margin="37,89,118,98" />
</Border>
</Grid>
And the result:
I tried going for a similar design to this toolbar in white:
This is what I used:
<Border CornerRadius="8" Background="White" Grid.Row="1">
<Border.Effect>
<DropShadowEffect ShadowDepth="3" Opacity="0.2"/>
</Border.Effect>
</Border>

Style a border with a different brush color for each corner

I have created a static resource defining the border of a specific item in my xaml, but I can't find a good way to define a unique color for each side!
xaml:
<Border Style="{StaticResource SidePanelBorder}">
<!-- rest of the xaml -->
</Border>
StaticResource:
<Style x:Key="SidePanelBorder">
<Setter Property="Control.BorderBrush" Value="#FF363636" />
<Setter Property="Control.BorderThickness" Value="1" />
</Style>
But I want to define one color for each side of the border, and eventually also a different Border thickness.
Any good techniques out there doing this?
Seems very hacky, but you could define borders within borders, and make only 1 side have a thickness. For example
<Border BorderThickness="0,0,0,10" BorderBrush="Green">
<Border BorderThickness="0,0,10,0" BorderBrush="Blue">
<Grid>
<Button>Hello</Button>
</Grid>
</Border>
</Border>
would give a green border on the bottom and a blue border to the right. Isn't the prettiest piece of Xaml though.
Another solution using one Border and a VisualBrush, allowing setting the Border's CornerRadius and BorderThickness:
<Border BorderThickness="10" CornerRadius="10" HorizontalAlignment="Right" Height="150" VerticalAlignment="Bottom" Width="150" Margin="0,0,92.666,42.667">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Path x:Name="ColoredBorderLeft" Data="M0,0 L0,0 1,0.5 L1,0.5 0,1" Fill="Blue" Stretch="Fill" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderRight" Data="M1,0 L1,0 0,0.5 L0,0.5 1,1" Fill="Red" Stretch="Fill" Grid.Column="1" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderTop" Data="M0,0 L0,0 0.5,1 L0.5,1 1,0" Fill="Green" Stretch="Fill" Grid.ColumnSpan="2"/>
<Path x:Name="ColoredBorderBottom" Data="M0,1 L0,1 0.5,0 L0.5,0 1,1" Fill="Yellow" Stretch="Fill" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
</Border>
The Grid is needed to prevent the tips of the triangle Paths to "push through" into the border.
The Path.Name's can be used for DataBinding or setting the color from code behind.
you can have a DockPanel and can put 4 Borders inside it, each docked to different side.
like:
<DockPanel LastChildFill="true">
<Border DockPanel.Dock="Left" Background="Red"/>
<Border DockPanel.Dock="Top" Background ="Blue"/>
<Border DockPanel.Dock="Right" Background ="Yellow"/>
<Border DockPanel.Dock="Bottom" Background ="Green"/>
<Grid>
...........your control here
</Grid>
</DockPanel>
If you use a Grid you can have Border's overlay on one another to achieve the same affect. Just set the border thickness of the border color you want to show and have the other border thickness be 0.
<UserControl.Resources>
<Style x:Key="GreenBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="1,1,1,0" />
</Style>
<Style x:Key="RedBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource GreenBorder}">
<!-- Content goes here -->
</Border>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource RedBorder}">
</Border>
</Grid>
This will give a Green border to the left, top and right borders, but a Red border to the bottom border of the Grid cell.
there's no easy way to do this without writing your own control or subclassing border

Resources