Why can a control draw outside its bounding box? - wpf

Here's an example image of what I mean: example
The gray rectangle is the bounding box of a control that draws the blue lines and dots in it's OnRender(...) method. The red ovals mark places where it happens.
Why is that possible?
How can it be avoided?

Here's the perfect answer to my second question, at least when using a rectangular shaped control:
<object ClipToBounds="True" />
More details on the MSDN.

https://msdn.microsoft.com/en-us/library/ms750441(v=vs.100).aspx has detailed information about the architectural design of WPF to answer why it is possible.
To avoid it you want to use the clip property of your element.
<Rectangle Fill="Yellow" Height="100" Width="200" StrokeThickness="2" Stroke="Black">
<Rectangle.Clip>
<EllipseGeometry Center="200,100" RadiusX="50" RadiusY="50" />
</Rectangle.Clip>
</Rectangle>
Check out http://msdn.microsoft.com/en-us/library/cc189065%28v=VS.95%29.aspx for more details.

Related

How to set children position dynamically in WPF

we have three control inside stackpannel now i want to change the control position dynamically .
we have searching Google but we don't find any solution.
please solve my problem.
thanx in advance.
If you are changing the position of elements inside a container then a StackPanel is the wrong container to use. It's designed to stack (!) elements on top of each other (or along side each other) with a spacing determined by the margins on each element.
If you want to move things around then you'll need a Canvas. With this you can then change the position of the elements on the canvas from code.
For example if you have the following:
<Canvas>
<Rectangle x:Name="Rectangle1" Canvas.Left="40" Canvas.Top="31"
Width="63" Height="41" Fill="Blue" />
<Ellipse x:Name="El;ipse1" Canvas.Left="130" Canvas.Top="79"
Width="58" Height="58" Fill="Blue" />
<Path x:Name="Path1" Canvas.Left="61" Canvas.Top="28"
Width="133" Height="98" Fill="Blue"
Stretch="Fill" Data="M61,125 L193,28"/>
</Canvas>
You can then change the position of elements like this:
Rectangle1.SetValue(Canvas.TopProperty, 45);
Rectangle1.SetValue(Canvas.LeftProperty, 60);

drag items in resizable canvas

I'm designing a spline editor which is composed of two parts (usercontrols)
The left control is the DesignerControl and the right one is the InfoControl. They share the same DataContext: DesignerVM with an
ObservableCollection<SplineVM> SplineVMList;
DesignerControl is an ItemsControl templated as a Canvas ("mycanvas") with ItemsSource bound to SplineVMList and ItemTemplate set as SplineControl.
InfoControl is a ListBox displaying the SplineVMs and a Grid displaying the SplineVMs' Properties.
In SplineControl I have a Canvas containing 4 draggable Points (Rectangle) and 2 Lines that will be bound to these Points. Everything works, I can drag my points, my lines move.
<UserControl>
<Canvas Width="300" Height="300" x:Name="mycanvas" Background="Transparent">
<Path x:Name="curve" Stroke="#F02828" StrokeThickness="3">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure>
<PathFigure.Segments>
<PathSegmentCollection x:Name="pathsegment"/>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
<Rectangle x:Name="curvePoint1" Width="10" Height="10" Canvas.Bottom="0" Canvas.Left="0" />
<Rectangle x:Name="curvePoint2" Width="10" Height="10" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE/4)}"/>
<Rectangle x:Name="curvePoint3" Width="10" Height="10" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE/2)}"/>
<Rectangle x:Name="curvepoint4" Width="10" Height="10" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(#VALUE)}"/>
<Line x:Name="line1" Stroke="#dfdfdf" StrokeThickness="1"/>
<Line x:Name="line2" Stroke="#dfdfdf" StrokeThickness="1"/>
</Canvas>
</UserControl>
My first problem is that I have to use a container (here a Canvas) to hold the path, rectangles and lines.
When i add a SplineControl in mycanvas, it's well placed and i can instantly drag my points but when i add another UserControl, it's placed above the previous one and i can't select the first Usercontrol's points.
I don't want to use IsHitTestVisible since I want to select points from the first userControl through the second.
My Second problem:
Since I use a Canvas to hold my things in the SplineControl, i can drag points outside of the canvas and still interact with it, and at first sight it was great.
But thinking it again, i'd like to resize my Canvas when i move a point to always have my point in the canvas. but also have my other points positions updated respecting the ratio of mycanvas.
Do you know any control that have this behavior and can replace my Canvas?
Should i use CustomControl instead of UserControl?
May I have to think again my project's conception?
Looking at your application details using Canvas makes sense to me. I have worked on a similar application and created a DesignerCanvas allowing items movement, resizing etc. and worked well.
First problem: I also faced similar problem and decided to add a delta to the position of my control's; so say location of first control is 0,0 then next control will be placed at 10,10; and next at 20,20; this way all the controls have visible area and are slectable(as soon as it's selected it comes to top).
Second problem: It's not a big problem to increase the width and height of Canvas when a control is dragged and making sure that control is not allowed to place outside Canvas. Will try to see if I can find the code related to that.
Look at this article series having similar implementation -
WPF Diagram Designer - Part 4, Part 3, Part 2, Part 1
In Fact I've found a way to get around my first problem of item selection through other items.
Canvas that were under the selected one were unclickable because the Color of every Canvas was set to Transparent. So I could see them but not interact with them.
Since I've set the Background property to Null (or haven't set it explicitly), I can click through and select the items under the top one.
It's a strange behavior of WPF...
Does someone have an explanation?
It will solve my 2nd problem too, 'cause I'll set my UserControl with the size I want and still select other UserControls in my View.
It's a dirty trick but I'll avoid the CustomPanel thing then.
thx anyway akjoshi and Danny Varod for your concern ^^
your answers were still useful since I'll use them in the other project I'm working on.
it's just that time is short on this one.

Aero white glow in WPF?

How do I add the Aero white glow behind a control in WPF?
I mean the glow like in a window's caption bar in Vista/7.
This unrelated question seems to have the answer...
Outer bevel effect on text in WPF
I just needed a rectangle behind my text block...
<Rectangle Height="{Binding ElementName=textBlock1, Path=ActualHeight}" HorizontalAlignment="{Binding ElementName=textBlock1, Path=HorizontalAlignment}" Margin="{Binding ElementName=textBlock1, Path=Margin}" VerticalAlignment="{Binding ElementName=textBlock1, Path=VerticalAlignment}" Width="{Binding ElementName=textBlock1, Path=ActualWidth}" Fill="White" Opacity="0.5">
<Rectangle.Stroke>
<SolidColorBrush />
</Rectangle.Stroke>
<Rectangle.Effect>
<BlurEffect Radius="10" KernelType="Gaussian" />
</Rectangle.Effect>
</Rectangle>
This is gonna do the trick for any control when you replace all the "textBlock"s in the code!
And it looks exactly the same as the aero glow!
Does this help? The effect can be quite subtle but it does look alright.
I tried messing about with Pixel Shaders in WPF using Shazzam but it was a lot of work to achieve the same effect or better. They use a lot of "under the hood" stuff (such as double-pass shaders) of which the techniques aren't available to the WPF api.

Precise OpacityMask

Suppose I need to set an opacity mask on a WPF control that highlights a portion of it in precise position (suppose a 50x50 square at (50;50) position). To do that I create a DrawingGroup containing 2 GeometryDrawing objects: 1 semi-transparent rectangle for the whole actual size of the control and 1 opaque rectangle for highlighted area. Then I create a DrawingBrush from this DrawingGroup, set it's Stretch property to None and set this brush as OpacityMask of the control that needs to be masked.
All this works fine while nothing is "sticking" out of bounds of said control. But if control draws something outside of it's bounds the outer point becomes a starting point from where opacity mask is applied (if the brush is aligned to that side) and the whole mask shifts by that distance resulting in unexpected behavior.
I can't seem to find a way to force mask to be applied from control's bounds or at least get the actual bounds of the control (including sticking parts) so I can adjust my mask accordingly.
Any ideas highly appreciated!
Update: Here's a simple test-case XAML and screenshots demonstrating the issue:
We have 2 nested Borders and Canvas in the last one with the above mentioned square:
<Border Padding="20" Background="DarkGray" Width="240" Height="240">
<Border Background="LightBlue">
<Canvas>
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White"
/>
</Canvas>
</Border>
</Border>
Here's how it looks:
(source: ailon.org)
Now we add an OpacityMask to the second border so that every part of it except our square is semi-transparent:
<Border.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,200,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="50,50,50,50" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>
Everything looks as expected:
(source: ailon.org)
And now we add a line to the canvas that sticks 10 pixels out on the left of our border:
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"
/>
And the mask shifts 10 pixels to the left:
(source: ailon.org)
Update2: As a workaround I add a ridiculously large transparent rectangle outside of bounds and adjust my mask accordingly but that is a really nasty workaround.
Update3: Note: The canvas with rectangle and line is there just as an example of some object that has something outside of it bounds. In context of this sample it should be treated as some sort of a black box. You can't change it's properties to solve the general issue. This would be the same as just moving the line so it doesn't stick out.
Interesting issue indeed - here's what I've figured: The effect you are experiencing seems to be determined by the Viewport concept/behavior of TileBrush (see Viewbox too for the complete picture). Apparently the implicit bounding box of a FrameworkElement (i.e. the Canvas in your case) is affected/expanded by elements sticking out of bounds in a subtle way, that is, the dimensions of the box expand but the coordinate system of the box does not scale, rather expands too into the out of bounds direction.
It might be easier to illustrate that graphically, but due to time constraints I'll just offer a solution first and will explain the steps I've taken for the moment in order to get you started:
Solution:
<Border Background="LightBlue" Width="198" Height="198">
<Border.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="-10,0,220,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">...</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>
<Canvas x:Name="myGrid">...</Canvas>
</Border>
Please note that I've adjusted units by +/- 2 pixels here and there for pixel precision without knowing where the offset originates, but I think this can be ignored for the purpose of the example and resolved later if need be.
Explanation:
To simplify the illustration one should usually make all related implied/auto properties explicit first.
The inner border receives auto dimensions of 198 from the outer border (240 - 20 padding - 2 pixels deduced by experiment; don't know their origin, but ignorable right now), that is if you specify this as follows nothing should change, while using other values yields graphical changes:
<Border Background="LightBlue" Width="198" Height="198">...</Border>
Further the default implied Viewport and ViewportUnits like so:
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top"
Viewport="0,0,1,1" ViewportUnits="RelativeToBoundingBox">...</DrawingBrush>
You are enforcing the DrawingBrush size by overriding Stretch with None, while keeping the position and dimension of the base tile at default and relative to its bounding box. In addition you (understandably) are overriding AlignmentX/AlignmentY, which determine the placement within the base tile, that is within its bounding box. Resetting those to their defaults of Center is already telling: The mask shifts accordingly, meaning it has to be smaller than the bounding box, else their would be nothing to center within.
This can be taken further by changing ViewportUnits to Absolute, which will yield no graphics at all until the units are properly adjusted of course; again, by experiment the following explicit values are matching the auto ones, while using other values yields graphical changes:
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush>
Now the opacity mask already aligns properly with the control. Obviously there is one problem left though, as the mask is clipping the line now, which is no surprise given its size and the absence of any Stretch effect. Adjusting its size and position accordingly resolves this:
<RectangleGeometry Rect="-10,0,220,200" />
and
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">...</DrawingBrush>
Finally the opacity mask matches the control bounds as desired!
Supplement:
The required offsets determined by deduction and experiment in the explanation above can be retrieved at runtime by means of the VisualTreeHelper Class:
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Depending on your visual element composition and needs you may need to factor in the LayoutInformation Class and build the union of both to get the all-encompassing bounding box:
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Rect layoutSlot = LayoutInformation.GetLayoutSlot(myGrid);
Rect boundingBox = descendantBounds;
boundingBox.Union(layoutSlot);
See the following links for more details on both topics:
Windows Presentation Foundation
Graphics Rendering Overview,
especially VisualTreeHelper
Class
The Layout System, especially
Element Bounding Boxes
On your Canvas object add ClipToBounds="True".
<Canvas ClipToBounds="True">
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White" />
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"/>
</Canvas>
One workaround that may be more ideal than your current one would be to simply apply the OpacityMask at a higher level. Using this demo code for example, you could remove the mask from the Border and apply it to the Window instead. With a bit of tweaking it fits properly:
<Window.OpacityMask>
<DrawingBrush AlignmentX="Left" AlignmentY="Top" Stretch="None">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,300,300"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="92,82,50,50"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Window.OpacityMask>
You would have to write some code to move the mask when the Window is resized, and for that reason you may be better off generating the mask dynamically in the code-behind.
My question for you is, why do you need to handle geometries that go outside the bounds of your Canvas?
Since you have parts that stick out from the control, one idea is to separate control image from the control mask.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Border Padding="20" Background="DarkGray" Width="240" Height="240"> <!-- user container -->
<Grid> <!-- the control -->
<Border Background="LightBlue" HorizontalAlignment="Stretch"> <!-- control mask-->
<Canvas>
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White"
/>
<Canvas.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top" TileMode="None">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,200,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="50,50,50,50" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.OpacityMask>
</Canvas>
</Border>
<Canvas> <!-- control image-->
<Line X1="-10" Y1="150" X2="120" Y2="150" Stroke="Red" StrokeThickness="2"/>
</Canvas>
</Grid>
</Border>
</Window>

Create a transparent hole inside a window's background - WPF

I have a window with these values:
WindowState="Maximized"
AllowsTransparency="True"
Opacity="0.5"
WindowStyle="None"
This window is coming on top of other window (as a pop-up) with content on it on a specific location.
I have a new requirement. This window have to show a rectangle area from the window below. In other words, i have to set a "hole" in this window which will be totally transparent (without the opacity value).
Until this moment i couldn't figure out a way to make this transparent hole.
Hope to get an idea...
I found a kind of solution for it:
this is the pop-up window that on top of another window, and containing a hole to the other window in a desired place:
Window's header:
WindowState="Maximized"
AllowsTransparency="True"
WindowStyle="None"
Window's content:
<Window.Background >
<SolidColorBrush x:Name="BackgroundBrush" Color="WhiteSmoke" Opacity="0" ></SolidColorBrush>
</Window.Background>
<Canvas x:Name="ContectHolder" >
<Path Stroke="Black" Fill="WhiteSmoke" Opacity="0.8">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1 >
<RectangleGeometry Rect="0,0,2000,2000" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="75,75,400,900" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
try to avoid AllowsTransparency=true, it is very buggy and slow.
you can PInvoke SetWindowRgn to create a a window of any shape:
Use CreateRectRgn twice, once for the window bounding rectangle and once for the hole.
Use CombineRgn with RGN_AND as the 4th parameter to get a region with an hole in it
Call SetWindowRgn to apply the region to the window
Don't forget to delete all the regions except for the one you passed to SetWindowRgn

Resources