Canvas with VisualBrush not rendering Image correctly - wpf

I've got a Canvas that I'm setting the Background property to a VisualBrush consisting of an Image. I'm data binding the Canvas' Width and Height to the Images' Width and Height. Here's the XAML:
<Canvas x:Name="imageCanvas" ClipToBounds="True" Width="{Binding ActualWidth, ElementName=dataImage}" Height="{Binding ActualHeight, ElementName=dataImage}"
Loaded="imageCanvas_Loaded" MouseMove="imageCanvas_MouseMove">
<Canvas.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Image x:Name="dataImage" ClipToBounds="True" RenderOptions.BitmapScalingMode="HighQuality" Stretch="Uniform" Source="{Binding DataImage, Mode=OneWay}" LayoutTransform="{Binding DataImageLayoutTransform, Mode=OneWay}"/>
</VisualBrush.Visual>
</VisualBrush>
</Canvas.Background>
</Canvas>
Note that Image is using a LayoutTransform. I'm using this to stretch the width of the image. If I disable the LayoutTransform binding and run I get the following (properly) rendered output:
However, when I add the LayoutTransform to stretch the width of the image by a factor of 6, the image looks stretched correctly but it is too small i.e. it is not filling the Canvas uniformly:
I suspect this may have something to do with binding the Canvas Height/Width to the image height width, but it's not clear what the exact problem is. Can anyone provide some insight? Thank you in advance.

Related

Canvas position to image pixel

I want to draw rectangles over an image to markup specific regions.
I saw this question: Draw Rectangle over Image and it worked. I get a rectangle over the image. Now i want to get the real pixel position on the image. I know i get some data over Canvas.Left, Canvas.Top but where is the relationship between Canvas and image position?
Thanks Lyror
To make this work you can easily put it in a viewbox like this:
<Viewbox> <!-- I will make this construct fit everywhere -->
<Grid> <!-- I will be exactly the size of the image -->
<Image Source="/MyImagegeWithResolutionOf1080x720p.jpg"
Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}"
Height="{Binding Source.PixelHeight, RelativeSource={RelativeSource Self}}"
Stretch="Fill"/>
<Canvas> <!-- use me to draw your stuff -->
<Rectangle Width="10" Height="10" Canvas.Bottom="360" Canvas.Left="540"/> <!-- I will be in the center -->
</Canvas>
</Grid>
</Viewbox>

Set Size of Rectangle to image size

i'm using this, to show icons that can change their color
<Rectangle Width="100"
Height="100"
Fill="{DynamicResource WhiteBrush">
<Rectangle.OpacityMask>
<ImageBrush Stretch="Uniform" ImageSource={x:Static ImageResources.MyImage}"/>
</Rectangle.OpacityMask>
</Rectangle>
The thing is, that not every Image looks good in 100x100 and if I do not fill these values, nothing is shown. What I wanted to achieve is, that the rectangle automatically takes the needed size of the Image, same as if I would use
<Image Source="{x:Static ImageResources.MyImage}"/>
Any Idea how to achieve this? I already tried to give the ImageBrush a name and reference the size from the rectangle, this doesn't work.
As a workaround I placed an Image at the same position with Visibility=Hidden and get the actualHeight and actualWidth for the Rectangle from there, but that's not an acceptable solution. Any hints welcome.
You could bind the Rectangle's Width and Height:
<Rectangle Fill="{DynamicResource WhiteBrush"
Width="{Binding Width, Source={x:Static ImageResources.MyImage}}"
Height="{Binding Height, Source={x:Static ImageResources.MyImage}}">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="{x:Static ImageResources.MyImage}"/>
</Rectangle.OpacityMask>
</Rectangle>

Canvas is clipped when size is too big

I am using a canvas with the an ImageBrush to dispaly an image. I am setting the size of the canvas to the original size of the image so I can get the coordinates when I move the mouse etc.
The problem is that when I put the canvas in a control (Grid for example) with a smaller size the Canvas is clipped.
<Grid>
<Canvas Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" >
<Canvas.Background>
<ImageBrush ImageSource="{Binding Image, Converter={StaticResource imgConverter}}"/>
</Canvas.Background>
</Canvas>
</Grid>
Is there a way to keep the canvas size without being clipped?
I've been meaning to dig deeper in to the source to work out where the clipping occurs for a while now, but never get around to doing it. I've been using a not-so-nice trick of inserting a Canvas into the visual tree when this happens as a workaround.
There are a number of controls that clip the child visuals; Grid, StackPanel, etc. As I mentioned the usual quick fix is to use a Canvas after the container that causes the clip.
In your snippet are there more containers higher up the visual tree?
If the depth was actually something like this:-
<StackPanel>
<Grid>
<Canvas Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" >
<Canvas.Background>
<ImageBrush ImageSource="{Binding Image, Converter={StaticResource imgConverter}}"/>
</Canvas.Background>
</Canvas>
</Grid>
</StackPanel>
Then this might cause clipping. If you insert another Canvas further up the visual tree then this clipping is removed.
<StackPanel>
<Canvas>
<Grid>
<Canvas Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" >
<Canvas.Background>
<ImageBrush ImageSource="{Binding Image, Converter={StaticResource imgConverter}}"/>
</Canvas.Background>
</Canvas>
</Grid>
</Canvas>
</StackPanel>
This workaround can then become problematic if it alters other layout needs for other controls.

Stopping InkCanvas from resizing while drawing

In my application, I have an InkCanvas with an image in the background. When I draw on the InkCanvas to the bottom or left edge, the InkCanvas changes size to fit the sketch, which messes up the rendering of the image in the background. How do I stop the InkCanvas from resizing when strokes are applied outside its current size?
Current XAML:
<InkCanvas x:Name="DrawingArea"
Width="Auto"
Height="Auto"
ClipToBounds="True"
Background="{x:Null}" />
Background is set in code-behind
Without specifying a specific size, I found that binding the MaxWidth and MaxHeight to the ActualWidth and ActualHeight of the parent element keeps the InkCanvas from expanding outside the element.
<Grid x:Name="LayoutRoot">
<InkCanvas x:Name="DrawingArea"
MaxWidth="{Binding ActualWidth, ElementName=LayoutRoot}"
MaxHeight="{Binding ActualHeight, ElementName=LayoutRoot}"
Background="{x:Null}" />
...
</Grid>
You could hardcode the widths instead of using auto:
Width = "200"
Height = "200"

wpf stretch a line when resizing the canvas parent

I have a vertical line and a horizontal one that i want to resize when i dynamically resize my canvas parent. (landmark)
i'd like to have the horizontal line always 25 away from the left and right borders of my canvas and 13 away from the bottom border.
and the same for the vertical line, 25 away from the top and bottom borders and 13 from the left border.
Is there a simple solution?
May I have to change my canvas to another control?
Just stick the lines in a grid on top of your canvas to get the right behaviour
<Grid Width="600" Height="600">
<Canvas Background="LightBlue">
// stuff here
</Canvas>
<Grid>
<Rectangle Fill="Black" Height="1"
Stroke="Black" VerticalAlignment="Bottom" Margin="25,0,25,13"/>
<Rectangle Fill="Black"
HorizontalAlignment="Left" Stroke="Black" Width="1" Margin="13,25,0,25"/>
</Grid>
</Grid>
I would use Converters based on the ActualHeight and ActualWidth of your Canvas to set the height, width, and position of your Line objects
To avoid writing a bunch of individual converters, I have a MathConverter posted on my blog that can be used for all the calculations.
<Canvas x:Name="MyCanvas">
<!-- Horizontal Line: 25 from each side, and 13 from bottom -->
<!-- May need to adjust the Canvas.Top ConverterParameter based on Line height -->
<Line Height="1"
Canvas.Left="25"
Canvas.Top="{Binding ElementName=MyCanvas, Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=#VALUE-14}"
Width="{Binding ElementName=MyCanvas, Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=#VALUE-50}" ... />
<!-- Vertical Line: 25 from top and bottom, and 13 from left -->
<Line Canvas.Left="13" Canvas.Top="25"
Height="{Binding ElementName=MyCanvas, Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=#VALUE-50}" ... />
</Canvas>
Because these are all Bindings, they will get refreshed anytime the bound property changes (MyCanvas.ActualHeight and MyCanvas.ActualWidth)
Use Grid instead of Canvas in the case you need to set Margin.
For your lines to have space from the borders, go to Properties and use Margin in the Layout Area to set the spaces. For your horizontal line set the VerticalAlignment to Bottom and HorizontalAlignment to Stretch. The Margin shall be 25,0,25,13 in this case.
for your vertical line set the VerticalAlignment to Stretch an the HorizontalAlignment to Left. Margin should be 13,25,0,25
have luck

Resources