Silverlight CliptoBound property issue - silverlight

I am working on a Silverlight application and my problem is like this: I have a WrapPanel
and inside the WrapPanel I am adding a number of images. Then, I rotate these images either
by 90 degrees or -90 degrees.
So if my image size is 200 by 250, when I rotate it it will go out of the WrapPanel by 50 pixels. Is there a way to clip this image to the WrapPanel's actual bounds?

Sounds to me like you are using the RenderTransform to rotate the image. The problem is the transform is applied after the size and position of the image has been allocated hence the rotated image bleads outside of its original rectangle.
One solution is to use the LayoutTransfomer to effect that transform instead. This would cause the WrapPanel to re-flow the elements even moving the image to the next row. Here is an example using rectangles for simplicity:-
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{StaticResource TestData}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:LayoutTransformer RenderTransformOrigin="0.5, 0.5" Margin="5">
<toolkit:LayoutTransformer.LayoutTransform >
<RotateTransform Angle="0" />
</toolkit:LayoutTransformer.LayoutTransform>
<Rectangle Width="100" Height="150" Fill="Blue" MouseLeftButtonUp="Rectangle_MouseLeftButtonUp" />
</toolkit:LayoutTransformer>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
I've added code-behind:-
private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
LayoutTransformer lt = ((FrameworkElement)sender).Parent as LayoutTransformer;
RotateTransform rt = (RotateTransform)lt.LayoutTransform;
rt.Angle += 90;
lt.ApplyLayoutTransform();
}
My TestData items source is an arbitary List<T>, doesn't really matter what is in the list just that it contains a few items.
Using the ScrollViewer as dynamic clipping region
Note that its normal to place a WrapPanel in a ScrollViewer which naturally clips its content anywar.
Perhaps you really do want to clip the WrapPanel and you don't want any scrolling. Obviously you could apply a RectangleGeometry its Clip property but you would have to ensure the clip rectangle always matched the actual size of the WrapPanel. You could do that by monitoring its SizeChanged event and using a bit of code.
Another approach is to put it in a ScrollViewer then disable both scroll bars.

Just to add to AnthonyWJones answer above, which is entirely correct, you need to rotate during layout rather than render. Silverlight Panels do not clip their contents, in order to perform clipping you must set their Clip property to the required geometry manually.
The following blog post provides an attached behaviour that will automatically clip the bounds of a panel:
http://www.scottlogic.co.uk/blog/colin/2009/05/silverlight-cliptobounds-can-i-clip-it-yes-you-can/
You simply set the property as follows:
<Canvas util:Clip.ToBounds="true"
Grid.Column="1" Background="Aqua" Margin="20" >
<Ellipse Fill="Red" Canvas.Top="-10"
Canvas.Left="-10" Width="130" Height="130"/>
</Canvas>

Related

Control size of Canvas in ScrollViewer

I am writing a custom control for image display and interaction. Some of the features it needs is zooming and panning as well as drawing a custom region of interest, i.e. drawing a rectangle on top of the displayed frame with arbitrary position and size. Zooming and panning features are done and work nicely, I'm using a ScrollViewer, a scale transform and some event handlers for mouse clicking and moving in the C# code.
For the custom selection feature I want to add a canvas on top of the frame since that's the only way I know of that allows arbitrary positioning of a rectangle. In order for any shapes on the canvas to be mappable to image coordinates I need of course the canvas to exactly line up with the image so size and position must be the same. I also would like the image to keep adjusting to window resizes as it does without any canvas. Since Canvas doesn't automatically resize itself to anything I want to bind its dimensions to the size of the image like so (this is from the control template for the custom control):
<ScrollViewer x:Name="scrollViewer" Grid.Column="0" Grid.Row="1" CanContentScroll="True" PanningMode="Both" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<Grid>
<Image x:Name="screen" Panel.ZIndex="0" Cursor="Cross" Source="{TemplateBinding ColoredFrame}"/
<Canvas x:Name="testCanvas" Panel.ZIndex="1" Background="Transparent" Width="{Binding ActualWidth, ElementName=screen}" Height="{Binding ActualHeight, ElementName=screen}"/>
<Grid.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="{Binding RotationAngle, RelativeSource={RelativeSource TemplatedParent}}"/>
<ScaleTransform x:Name="scaleTransform" ScaleX="{Binding ZoomFactor, RelativeSource {RelativeSource TemplatedParent}}" ScaleY="{Binding ZoomFactor, RelativeSource={RelativeSource TemplatedParent}}"/>
</TransformGroup>
</Grid.LayoutTransform>
</Grid>
</ScrollViewer>
However, I am getting weird behaviour with this - when I use bindings for both Width and Height of the canvas to ActualWidth and ActualHeight of the image, like in the above code, it will automatically resize itself only when the size increases (when the user increases the size of the window) but not when it decreases. Any size increase is permanent and when decreasing the window again, one would have to scroll as the size of the image and canvas never decrease. This does not happen when I bind only one of the dimensions (width or height) and set the other one to a constant value. Binding modes (one way / two way) have no effect either.
I'm probably missing something really obvious but I've spent hours trying to figure this out and I am at a loss. Any help would be appreciated.
as far as I know Canvas does not have good sizing options. I think an Adorner will do what you try to achieve. For an example and additional information please have a look here: https://learn.microsoft.com/de-de/dotnet/framework/wpf/controls/adorners-overview
Happy coding
Tim

How do I properly draw and scale a Canvas as a WPF background to a control?

I have a StackPanel that needs to contain drawn background. Specifically, my StackPanel needs to have the ability to grow and the rectangle must grow with the StackPanel, but must remain pseudo-anchored to each side at a fixed position.
I've attempted to use the Canvas.Left, Canvas.Right, Canvas.Top and Canvas.Bottom attached properties, but so far they've not worked. Furthermore, this does seem to work when drawing within Canvas objects, just not when they are embedded within a VisualBrush set as a background. How can I accomplish drawing this resizable, rectangular background within my StackPanel?
Below is the state of my current code. I've tried various approaches but none seem to work.
My Code:
<StackPanel DockPanel.Dock="Right" Orientation="Vertical" VerticalAlignment="Center">
<StackPanel.Background>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<Canvas Background="Magenta" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
Rectangle Fill="#FFDDECF7" Canvas.Left="20" Canvas.Top="20" Canvas.Bottom="20" Canvas.Right="0"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</StackPanel.Background>
...
</StackPanel>
This currently doesn't render anything. I set the canvas background to magenta just so I could see if it were drawing, and I'm not even seeing that. Other attempts have drawn the canvase, however, the blue rectangle is always stretched to fill the window, regardless of attached canvas property settings.
Sample:
The image below is a sample of what I want. Again, I'm using an ugly Magenta color to show the offset of the internal, blue rectangle. As the StackPanel grows or shrinks, the rectangle needs to be affixed to the top, left, right and bottom.
My suggestion is to place the stackpanel inside a grid:
<Grid DockPanel.Dock="Right" VerticalAlignment="Center" Background="Magenta">
<Rectangle Margin="20" Fill="#FFDDECF7"/>
<StackPanel Orientation="Vertical">
no background...
</StackPanel>
</Grid>
Wrap Canvas into a ViewBox, then work on a ViewBox. As far as I know Canvas doesn't support scalling too well.

wpf histogram fidelity degrades when resized smaller

I've developed a simple Histogram control that shows the distribution of grayscale colors (1 to 256 bins) in a live image. The control renders rectangles in an ItemsControl whose ItemsContainer is a ViewBox. Everything is working fine for the most part, however when I resize the grid column (using a GridSplitter) that is hosting the control the fidelity of my histogram begins to degrade.
Here's a couple shots of the histogram at its initial state, and then when it has been resized horizontally (notice the dark vertical lines in the forest of green rectangles...it gets worse the smaller I go):
Here's the XAML that renders the histogram:
<ItemsControl x:Name="_Histogram" Margin="1,3"
ItemsSource="{Binding HistogramCollection}"
VerticalAlignment="Bottom">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid>
<Viewbox Stretch="Fill" MaxHeight="100" >
<ItemsPresenter />
</Viewbox>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="LimeGreen"
Stretch="Fill"
Height="{Binding Bin}"
MinWidth="1"
StrokeThickness="0"
VerticalAlignment="Bottom"
HorizontalAlignment="Stretch"
RenderOptions.EdgeMode="Aliased"
UseLayoutRounding="True"
MouseEnter="Rectangle_MouseEnter"
MouseLeave="Rectangle_MouseLeave"
/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Any thoughts? My guess is that WPF is throwing out some Visuals in order to evenly distribute the rest.
I've experimented with using a Polygon in lieu of ItemsControl and Rectangles to distribute my bin points and the behavior goes away, however, I need the ability to MouseOver the histogram and show a popup (among other things, e.g. select a range of bins and update properties in my ViewModel). If you know of a way, using the Polygon approach, that I can hover in the green area and determine that I'm over a specific bin I'm definitely open to that.
UPDATE:
Removing RenderOptions.EdgeMode="Aliased" on the Rectangle XAML seems to solve the problem of black line glitches. Though it does render somewhat pale in the Designer (using d:DesignData), but it seems to render fine at runtime. I'm going to hold off on marking as "Answered" until others have had a chance to chime in. I'm really interested in the polygon approach because I get a crisper histogram when resizing to a larger width (the wider I make the histogram, the more pixelated the peaks get, i.e. the peaks are flat because of the width of the rectangle, which presents as a jagged plot instead of smooth curved plot). If only Point had a mouse over handler :D.

Align text vertical and horizontal center in stack panel?

I am trying to align a textblock vertically and horizontally center in a stack panel which is there in Listview but i am only able to get text vetically center but not horizontally. Plus the text is not getting wrapped. Here is the code that i have tried:
<ListBox Name="lstTiles" Margin="12,0,-12,0" Grid.Row="1" SelectionChanged="lstTiles_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="{StaticResource PhoneAccentBrush}" Width="145" Height="80" Margin="8,8,0,0" Orientation="Vertical" >
<TextBlock Text="{Binding Name}" Tag="{Binding ID}" Style="{StaticResource PhoneTextNormalStyle}" FontSize="15" TextWrapping="Wrap" TextAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How can i achieve text vertically, horizontally and textwrap?
It looks like since you have the orientation of the StackPanel set to Horizontal, you're putting textblocks next to each other rather than on top of each other. Since the StackPanel elements take the size of their children, you would be able to visualize this as a horizontal, side-by-side, listing of textblocks. Since each textblock takes the size of the text that is in it, you are going to see blocks that are of varying widths, so centering horizontally is going to have no effect.
You could use margins (a pain) to accomplish equal widths. I don't recommend this.
You could also put grids of a set width in the stack panel, and put the textblocks on the grid. You may be able to set the width of the textblocks to get the right effect, but I can't test this at the moment, and I don't remember if it will cause the text to stretch or not.
For text wrapping, I assume you're talking about within the textblock, and that's easy - just set the textblock's TextWrapping property to Wrap.
Try setting the HorizontalContentAlignment and VerticalContentAlignment properties on the listboxitem. I don't have my dev computer now, so I can't experiment, but here is a post that might help you: Silverlight 3: ListBox DataTemplate HorizontalAlignment. Look at both of the first two answers, and see which one might be most helpful in your situation, substituting center, of course, in place of left, top, or stretch.

Perfect Center on WPF Canvas

Since the canvas requires a Top/Left for placement, if you want to center something, is adding a grid at the proper Canvas.Top with HorizontalAlignment="Center" the best way to do it, or is there a better way?
This snip is a 150X300 canvas, with some content centered in a grid ....
<Canvas Width="150" Height="300">
<Grid Canvas.Top="75" Width="106" HorizontalAlignment="Center">
{whatever you want centered}
</Grid>
</Canvas>
Guy's solution works, but you may have to tweak z-order and visibility if you're juggling hit testing.
Another alternative is having the Grid inside the Canvas (as you've specified in your XAML) with the Height/Width set to (or bound to) the Height/Width of the Canvas. Then setting HorizontalAlignment/VerticalAlignment to Center for the contents of your Grid.
I'm not sure if this will meet your exact requirement, but if you put both the canvas and the content inside a grid as peers, it will get you a centered result:
<Grid>
<Canvas Width="150" Height="300"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="106" Content="Click"/>
</Grid>

Resources