How to access a Canvas inside a WPF UserControl? - wpf

I am a new to using WPF but very much like the idea of writing view code in xaml and backing code in a view-model. What I would want to do is extend use of the Canvas by associating it with a status bar that displays status text based on the contents of the Canvas and the mouse position (the stylized code below doesn't include this).
My approach has been to create a UserControl that contains the Canvas and put a ContentPresenter inside it, per https://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control.
I have two problems to solve:
1) What do I need to do to allow more than one child control in the same way that the Canvas allows more than one child control?
2) How do I access properties of the Canvas, such as Canvas.Left, from the main window code?
Thanks in advance for any suggestions you may have.
UserControl xaml code, UserControl code behind, and main window xaml code:
<UserControl x:Class="SO.CanvasUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SO"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Template>
<ControlTemplate TargetType="{x:Type local:CanvasUserControl}">
<Canvas Width="200" Height="100" Background="Green">
<ContentPresenter/>
</Canvas>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Code behind:
public partial class CanvasUserControl : UserControl
{
public CanvasUserControl()
{
InitializeComponent();
}
}
Main window:
<Window x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SO"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<!-- works as expected
<Canvas Width="200" Height="100" Background="Green">
<Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
<Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Red"/>
</Canvas>
-->
<!-- works as expected
<Canvas Width="200" Height="100" Background="Green" x:Name="MyCanvas">
<Line X1="{Binding ElementName=MyCanvas, Path=Left}" Y1="{Binding ElementName=MyCanvas, Path=Top}" X2="{Binding ElementName=MyCanvas, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvas, Path=ActualHeight}" Stroke="Red"/>
<Line X1="{Binding ElementName=MyCanvas, Path=ActualWidth}" Y1="{Binding ElementName=MyCanvas, Path=Top}" X2="{Binding ElementName=MyCanvas, Path=Left}" Y2="{Binding ElementName=MyCanvas, Path=ActualHeight}" Stroke="Red"/>
</Canvas>
-->
<!-- How do I add more than one child control as nested content for the Canvas?
<local:CanvasUserControl x:Name="MyCanvasUserControl">
<Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
<Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Green"/>
</local:CanvasUserControl>
-->
<!-- How do I access dependency properties of the Canvas?
<local:CanvasUserControl x:Name="MyCanvasUserControl">
<Line X1="{Binding ElementName=MyCanvasUserControl, Path=Left}" Y1="{Binding ElementName=MyCanvasUserControl, Path=Top}" X2="{Binding ElementName=MyCanvasUserControl, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvasUserControl, Path=ActualHeight}" Stroke="Red"/>
</local:CanvasUserControl>
-->
</Grid>
</Window>

Maybe I'm not fully understanding your problem, but it sounds like you just want to have a Canvas that has a predefined status bar inside of it. You can do this very easily by extending Canvas instead of UserControl. Here's a custom component that extends Canvas and has a status bar.
<Canvas x:Class="SO.CanvasUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SO"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border Width="525" Height="30" Background="Black" Canvas.Bottom="0">
<TextBlock Foreground="White" Text="Hello world" FontSize="16" />
</Border>
</Canvas>
Now add it to your main window and assign any children you like along with it. You will see the status bar is displayed along with all the children. Since the component extends Canvas, you can add as many children as you want and you can bind to Canvas dependency properties.
<Window x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SO"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<local:CanvasUserControl x:Name="MyCanvasUserControl" >
<Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
<Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Green"/>
<Line X1="{Binding ElementName=MyCanvasUserControl, Path=Left}" Y1="{Binding ElementName=MyCanvasUserControl, Path=Top}" X2="{Binding ElementName=MyCanvasUserControl, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvasUserControl, Path=ActualHeight}" Stroke="Red"/>
</local:CanvasUserControl>
</Window>

Just wanted to add that the MouseMove issue in the https://stackoverflow.com/a/42558704/9916025 answer above is due to the Background color not being set for the Canvas.
So to solve the problem with MouseMove you should add
<ItemsPanelTemplate>
<local:CustomCanvas Background="Transparent"
/>
</ItemsPanelTemplate>

Related

WPF- Need updated point values of polygon after applying render transform

I need to add labels for vertices of triangle that is created using polygon in WPF and labels should move according to triangle. I have initial points of vertices (0,0), (0,100) & (100,100) but it does not change after applying render transform (rotate, scale & translate).
So, please help me to calculate new coordinates of polygon (triangle in this example).
<Window
x:Class="WpfApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp11"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Canvas>
<Polygon
Points="0,0 0,100 100,100"
Stroke="Black"
StrokeThickness="1"
RenderTransformOrigin="0.5,0.5">
<Polygon.RenderTransform>
<TransformGroup>
<!--<ScaleTransform
ScaleX="5" />-->
<!--<TranslateTransform
X="20"
Y="20" />-->
<!--<RotateTransform
Angle="45" />-->
</TransformGroup>
</Polygon.RenderTransform>
</Polygon>
<Label Canvas.Left="0" Canvas.Top="0" Content="V1" />
<Label
Canvas.Left="0"
Canvas.Top="100"
Content="V2" />
<Label
Canvas.Left="100"
Canvas.Top="100"
Content="V3" />
</Canvas>
</Grid>
</Window>

WPF: How to Style my form with Transperancy levels

I want to implement this kind of Window:
So currently i have this Style :
<Window x:Class="CGTransparent.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AboutDlg"
Opacity="0.75"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
AllowsTransparency="True" Height="300"
Width="500"
ShowInTaskbar="False"
Background="#00000000">
<Window.Resources>
<LinearGradientBrush x:Key="GradientBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Black" Offset="0.1" />
<GradientStop Color="#202020" Offset="0.25" />
<GradientStop Color="#303030" Offset="0.50" />
<GradientStop Color="#404040" Offset="0.75" />
<GradientStop Color="#505050" Offset="1.0" />
</LinearGradientBrush>
</Window.Resources>
<Border CornerRadius="15" DockPanel.Dock="Top" Background="{DynamicResource GradientBrush}" Margin="0" Padding="0" BorderBrush="Black" BorderThickness="0">
<Grid Margin="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="300" />
</Grid.RowDefinitions>
</Grid>
</Border>
</Window>
Result (ignore the tiger...):
Any idea how to achieve this example Style ?
Update:
<Window x:Class="app.Forms.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PacketPlayer.Forms"
mc:Ignorable="d"
WindowStartupLocation="CenterOwner"
AllowsTransparency="True" WindowStyle="None"
Title="Window1" Height="300" Width="300">
<Border BorderBrush="Transparent" BorderThickness="1" CornerRadius="20">
<Grid>
<Image Source="C:\Users\racr\Desktop\download.jpg" Stretch="Fill" Margin="-60">
<Image.Effect>
<BlurEffect KernelType="Gaussian" Radius="60" />
</Image.Effect>
</Image>
<Border CornerRadius="60" Margin="30" Background="#7F000000">
<TextBlock Foreground="White"
FontSize="20" FontWeight="Light" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Run Text="Hello World" FontSize="48"/>
<LineBreak/>
<Run Text="walterlv.github.io"/>
</TextBlock>
</Border>
</Grid>
</Border>
</Window>
Result:
enter image description here
You cannot simulate your original image with only GradientBrush, you should blur an image with a large amount of blur radius.
Options to simulate it
It's sad to tell you that you cannot implement the iOS blur style exactly as it shows for you.
But, we have three other methods to simulate this kind of style (on Windows 10) and each has its advantages and disadvantages.
Call the Windows internal API SetWindowCompositionAttribute. You can get a lightly blurred transparent Window but this transparency is much less than the iOS one.
Add a BlurEffect to the window background image. You can get a more similar visual effect like the iOS one with very poor performance. But in this way, the background image is fixed and cannot be updated when the window moves.
Use UWP instead of WPF and use the AcrylicBrush. You can get a high-performance blur transparent window. But you should try the UWP Application development.
How to implement them
SetWindowCompositionAttribute API
Calling SetWindowCompositionAttribute API is not very easy, so I've written a wrapper class for easier usage. You can use my class by writing only a simple line in the XAML file or in the cs file.
<Window x:Class="CGTransparent.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:interop="clr-namespace:Walterlv.Demo.Interop"
mc:Ignorable="d" Title="AboutDlg" Height="350" Width="525"
interop:WindowBlur.IsEnabled="True"
Background="Transparent">
</Window>
Or you can use it in the cs file like this:
public class Window1 : Window
{
public Window1()
{
InitializeComponent();
WindowBlur.SetIsEnabled(this, true);
}
}
Just add my wrapper class into your project. It's a very long class so I pasted into GitHub: https://gist.github.com/walterlv/752669f389978440d344941a5fcd5b00.
I also write a post for its usage, but it's not in English: https://walterlv.github.io/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html
WPF BlurEffect
Just set the Effect property of a WPF UIElement.
<Window x:Class="MejirdrituTeWarqoudear.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True" WindowStyle="None"
Width="540" Height="360">
<Grid>
<Image Source="YourImageFile.jpg" Stretch="Fill" Margin="-60">
<Image.Effect>
<BlurEffect KernelType="Gaussian" Radius="60" />
</Image.Effect>
</Image>
<Border CornerRadius="60" Margin="30" Background="#7F000000">
<TextBlock Foreground="White"
FontSize="20" FontWeight="Light" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Run Text="Hello World" FontSize="48"/>
<LineBreak/>
<Run Text="walterlv.github.io"/>
</TextBlock>
</Border>
</Grid>
</Window>
Notice that it has a very poor performance.
UWP AcyclicBrush
You can read Microsoft's documents Acrylic material - UWP app developer | Microsoft Docs for more details about how to write an AcylicBrush.
Update
You can add a RectangleGeometry to clip your UIElement into a rounded rectangle.
<Window x:Class="MejirdrituTeWarqoudear.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="540" Height="360">
<Grid>
<Grid.Clip>
<RectangleGeometry RadiusX="60" RadiusY="60" Rect="30 30 480 300" />
</Grid.Clip>
<Image Source="High+Sierra.jpg" Stretch="Fill" Margin="-60">
<Image.Effect>
<BlurEffect KernelType="Gaussian" Radius="60" />
</Image.Effect>
</Image>
<Border Background="#7F000000">
<TextBlock Foreground="White"
FontSize="20" FontWeight="Light" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Run Text="Hello World" FontSize="48"/>
<LineBreak/>
<Run Text="walterlv.github.io"/>
</TextBlock>
</Border>
</Grid>
</Window>

Rounded Corners on Canvas dissappear when Element is dragged to the border

i have this code:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ScrollViewer Background="#FFADB9CD" >
<Grid>
<Border Name="mask" Height="{Binding ElementName=cnvsEtikett, Path=Height}" Width="{Binding ElementName=cnvsEtikett, Path=Width}" Background="White" CornerRadius="6"/>
<Canvas Height="100" Name="cnvsEtikett" Width="200" Background="White" ClipToBounds="True">
<Canvas.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}" />
</Canvas.OpacityMask>
<TextBlock Height="23.2" Text="TextBlock" Canvas.Left="63" Canvas.Top="41" />
</Canvas>
</Grid>
</ScrollViewer>
</Grid>
</Window>
I want the canvas to have rounded corners, which works until i drag the textblock to either side. then the corners dissappear. this is a simple recreation of the window in my programm, there i use drag and drop operations to move the textblock. I really need those rounded corners, but i am absolutely clueless of how i could solve this problem!
any ideas??
edit: it seems as if the canvas is stretched as the textblock is moved to the edge ( because the corner radius changes too!)
Just thought of another solution..
<Border ClipToBounds="True" CornerRadius="6" Background="White" Name="brdEtikett" Height="200" Width="200" >
<Canvas Name="cnvsEtikett" Background="Transparent" />
</Border>
set the background of the border to the color you want on the canvas and make the canvas transparent!!
this way you don't have to put padding or margin on the elements!
Best bet is probably to put the canvas inside the border (I typically put a small margin on panels inside of rounded corner borders to keep stuff from sticking out). This way, the border will expand or contract automatically as the canvas does (provided it is set to auto height width).
Border can have a single content child, in this case the Canvas with content of its own.
EDIT
Doesn't this achieve the same thing?
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfApplication8.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ScrollViewer Background="#FFADB9CD" >
<Grid>
<Border x:Name="mask" Background="White" CornerRadius="6" HorizontalAlignment="Center" VerticalAlignment="Center">
<Canvas Height="100" x:Name="cnvsEtikett" Width="200" ClipToBounds="True" d:LayoutOverrides="Margin">
<TextBlock Height="23.2" Text="TextBlock" Canvas.Left="59" Canvas.Top="31" />
</Canvas>
</Border>
</Grid>
</ScrollViewer>
</Grid>
</Window>
Gong Solutions Drag Drop Helper library

How to create a circle with a hole in a circle in WPF?

I have created a UserControl which is a ring by superposing 2 circles the small circle being blank and the second behind the smallest being colored.
In my WPF app, I want to put several rings but the small circle does hide other rings. I'd like to see through it and also capture mouse event for ring behind other rings otherwise it's not real rings. Is it possible ?
I tried OpacityMask for small ellipse as pointed by answer to http://social.msdn.microsoft.com/forums/en-US/wpf/thread/551201d1-c5b3-4e17-ae63-625cfbb8bcc4 but still can't see ring behind hole:
<UserControl x:Class="MyUserControls.MyRing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="150" SizeChanged="UserControl_SizeChanged">
<Grid Height="150" Name="Grid" Width="150" MouseMove="ellipse1_MouseMove">
<Ellipse Fill="Red" Height="150" Width="150" HorizontalAlignment="Left" Margin="0,0,0,0" Name="ellipse1" Stroke="Black" VerticalAlignment="Top" >
<Ellipse.OpacityMask>
<RadialGradientBrush>
<GradientStop Color="#FFB94444" Offset="0.496"/>
<GradientStop Color="#00FFFFFF" Offset="0.491"/>
</RadialGradientBrush>
</Ellipse.OpacityMask>
</Ellipse>
<Ellipse Fill="White" Height="100" Width="100" Margin="25,25,25,0" Name="ellipse2" Stroke="Black" VerticalAlignment="Top" />
</Grid>
</UserControl>
It looks like you already found your answer (a few years ago), but for anyone else looking to do this, you may want to check out CompositeGeometry:
http://msdn.microsoft.com/en-us/library/ms751808.aspx#combindgeometriessection
Such as:
<Path Fill="Red" Stroke="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Xor">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="75" RadiusY="75" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Then IsHitTestVisible="False" should prevent mouse interference, when needed.
You could just create a circle with a transparent background and a StrokeThickness in the UserControl.
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Ellipse Width="50" Height="50" Stroke="Blue" StrokeThickness="10" Fill="Transparent"></Ellipse>
</Grid>
</UserControl>
EDIT:
You can set a gradient brush to the stroke as below. You could replace the LinearGradientBrush with any other type of brush as you wish.
<Ellipse Width="50" Height="50" StrokeThickness="10" Fill="Transparent">
<Ellipse.Stroke>
<LinearGradientBrush>
<GradientStop Offset="0" Color="Red"/>
<GradientStop Offset="1" Color="Green"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
Check out this How to create a doughnut with a transparent center?.
Code with path and button under it:
<Grid x:Name="LayoutRoot" Width="109" Height="109">
<Button Content="Below" Width="100" Height="100" />
<Path Fill="#FF1F96D8" Stroke="#FF000000"
Width="109" HorizontalAlignment="Left" Stretch="None"
Data="M54.5,32.5 C41.245167,32.5 30.5,43.021309 30.5,56
30.5,68.978691 41.245167,79.5 54.5,79.5 C67.754837,79.5
78.5,68.978691 78.5,56 C78.5,43.021309 67.754837,32.5
54.5,32.5 z M54.5,0.5 C84.32338,0.5 108.5,24.676624
108.5,54.5 C108.5,84.32338 84.32338,108.5 54.5,108.5
24.676624,108.5 0.5,84.32338 0.5,54.5 0.5,24.676624
24.676624,0.5 54.5,0.5 z" Height="109" />
</Grid>

How to attach the alpha value of an element to a slider?

Is it possible to bind the alpha value of an element to a slider?
For example, this is code that allows the slider to change the height and top position of the element, but what is the syntax to control the alpha value in the Background attribute of the border?
<Window x:Class="WpfApplication25.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">
<Grid Background="Tan">
<StackPanel>
<Canvas>
<Border Background="#{Binding ElementName=theSlider, Path=Value}ffff00"
Canvas.Left="40"
Canvas.Top="{Binding ElementName=theSlider, Path=Value}"
CornerRadius="5"
BorderBrush="Brown"
BorderThickness="1"
>
<Rectangle
Height="{Binding ElementName=theSlider, Path=Value}"
Width="50"
/>
</Border>
</Canvas>
</StackPanel>
<Slider Name="theSlider" HorizontalAlignment="Left" Width="200" Minimum="10" Maximum="200" Cursor="Hand"/>
</Grid>
</Window>
Bind to the "Opacity" property, don't forget to set the slider minimum to 0 and maximum to 1

Resources