Put Ellipse on the edge of a line in the same view - c#

Either it is super simple and I just didn't find the solution yet or I'll start to go insane soon.
I just want to draw a Line in an ItemsControl on a Canvas and that Line should have an Ellipse on both edges. Like this:
My ItemsControl looks like this:
<ItemsControl ItemsSource="{Binding Connections}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type systemEnvironmentViewModels:SystemEnvironmentConnectionViewModel}">
<systemEnvironment:ConnectionView/>
</DataTemplate>
<Style TargetType="ContentPresenter"> <!-- this does only make it worse -->
<Setter Property="Canvas.Left" Value="{Binding Connection.FirstElementCoordinate.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Connection.FirstElementCoordinate.Y}"/>
</Style>
</ItemsControl.Resources>
</ItemsControl>
And the View that is used as DataTemplate:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse x:Name="DependencyEllipse" Width="10" Height="10" Fill="Black" Grid.Column="0"/>
<Line X1="{Binding Connection.FirstElementCoordinate.X}" X2="{Binding Connection.SecondElementCoordinate.X}"
Y1="{Binding Connection.FirstElementCoordinate.Y}" Y2="{Binding Connection.SecondElementCoordinate.Y}"
Stroke="Black" StrokeThickness="5" x:Name="ConnectionLine" Grid.Column="1"/>
<Ellipse x:Name="SecondDependencyEllipse" Width="10" Height="10" Fill="Black" Grid.Column="2"/>
</Grid>
The Result does look nothing like I want and I know that using a Grid with columns probably doesn't work but it's the closest I can think of that allows to split the elements like I want.
Also the Style for the ContentPresenter seems to be wrong, because without it my Line gets drawn like I want but the Ellipses are wrong
Should I go for another ItemsControl just for the Ellipse?
Help is much appreciated.

Do not use an Ellipse and don't put elements in Grid columns.
Instead, use Path elements with EllipseGeometries. The following example assumes that the FirstElementCoordinate and SecondElementCoordinate properties are of type System.Windows.Point. If that is not the case, use an appropriate Binding Converter.
<Canvas>
<Line X1="{Binding Connection.FirstElementCoordinate.X}"
Y1="{Binding Connection.FirstElementCoordinate.Y}"
X2="{Binding Connection.SecondElementCoordinate.X}"
Y2="{Binding Connection.SecondElementCoordinate.Y}"
Stroke="Black" StrokeThickness="5"/>
<Path Fill="Black">
<Path.Data>
<EllipseGeometry Center="{Binding Connection.FirstElementCoordinate}"
RadiusX="5" RadiusY="5"/>
</Path.Data>
</Path>
<Path Fill="Black">
<Path.Data>
<EllipseGeometry Center="{Binding Connection.SecondElementCoordinate}"
RadiusX="5" RadiusY="5"/>
</Path.Data>
</Path>
</Canvas>

Related

Viewbox scales things too small at high resolutions and scale factors and they are virtually impossible to see

I have a grid with one row and some columns. In order to scale correctly the grid content according to the screen resolution and scale selected in the Windows OS, I use a Viewbox.
I also define a style that is applied to all Viewboxes:
<Style TargetType="{x:Type Viewbox}">
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="StretchDirection" Value="Both"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="OverridesDefaultStyle" Value="False"/>
<Setter Property="UseLayoutRounding" Value="False"/>
</Style>
And then I enclose every grid column within a Viewbox:
<Grid Focusable="True"
Height="Auto">
<Grid.ColumnDefinitions>
<!-- Animated image -->
<ColumnDefinition Width="Auto" />
<!-- Stack panel with icon and text -->
<ColumnDefinition Width="Auto" />
<!-- A vertical separator -->
<ColumnDefinition Width="Auto" />
<!-- A button -->
<ColumnDefinition Width="Auto" />
<!-- Empty dummy column to help distribute space correctly between columns so it will cause next columns to be horizontally right aligned. -->
<ColumnDefinition Width="*" />
<!-- Help button -->
<ColumnDefinition Width="Auto" />
<!-- Three dots button -->
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Viewbox Grid.Column="0">
<StackPanel Orientation="Horizontal"
Visibility="{Binding Path=IsAnimatedImgVisible, Converter={StaticResource BoolToVisibility}}">
<v:UserControlAnimatedImg x:Name="UcSpinner" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Label SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
VerticalAlignment="Center" HorizontalAlignment="Left"
Content="{Binding Path=StatusText}"/>
</StackPanel>
</Viewbox>
<Viewbox Grid.Column="1">
<StackPanel Orientation="Horizontal">
<Image
Margin="10,5"
Height="24"
Width="24"
MaxHeight="24"
MaxWidth="24"
VerticalAlignment="Center"
HorizontalAlignment="Center"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding Path=Icon}"/>
<Label
VerticalAlignment="Center"
HorizontalAlignment="Left"
Content="{Binding Path=StatusTxt}"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality">
</Label>
</StackPanel>
</Viewbox>
<Viewbox Grid.Column="2">
<Separator Margin="10,5,5,5"
Height="26"
Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}">
</Viewbox>
<Viewbox Grid.Column="3">
<Button Height="Auto"
Width="150"
Command="{Binding ButtonOnClick}">
<StackPanel Orientation="Horizontal">
<Image
Height="24"
Width="25"
MaxHeight="24"
MaxWidth="25"
VerticalAlignment="Center"
HorizontalAlignment="Center"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding ImageIcon}"/>
<TextBlock
Margin="10,2,5,2"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Text="{x:Static p:Resources.BtnText}"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality">
</TextBlock>
</StackPanel>
</Button>
</Viewbox>
<!-- Empty dummy space to help distribute space correctly between columns and
make next columns to be horizontally right aligned. This column will be blank
space.-->
<Viewbox Grid.Column="4">
<ContentControl />
</Viewbox>
<Viewbox Grid.Column="5">
<Button Height="25"
Width="25"
MaxHeight="25"
MaxWidth="25"
Margin="8,0,5,0"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{Binding BtnHlpClick}">
<Image HorizontalAlignment="Center"
VerticalAlignment="Center"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding ImageHelpIcon}"/>
</Button>
</Viewbox>
<Viewbox Grid.Column="6">
<StackPanel x:Name="PlaceHolder"
VerticalAlignment="Center"
Margin="2,0,12,0"
Width="27"
Height="Auto">
<Image HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="20"
Width="20"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding ImgThreeDotsIcon}">
<Image.ContextMenu>
<ContextMenu Placement="Bottom" Background="#FFFFFF">
<MenuItem Header="{x:Static p:Resources.mnutext}" Command="{Binding MenuOnClick}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>
</StackPanel>
</Viewbox>
The problem I am currently facing is that when I switch to high screen resolutions and also I switch to high scale factor (125%, 150%, 175%, 200%, and so on), then the content of the grid is getting smaller and smaller and it is practically impossible to see. The more screen resolution I select, the more smaller is the grid content. The more scale factor I select, the more smaller is the grid content. The problem is that the grid content gets too small and impossible to see.
How can I solve this?

Auto-size drawing container in WPF

I'm currently working on a small C# Project to visualize our seating order in the Office. As we have multiple, different rooms, the desk layout is created dynamically. The data comes from an XML where a jpg-image (room layout) is stored as a base64 encoded string. Moreover, the XML file contains a list of all desks with their absolute pixel-Position on the Image.
Until now, I managed to draw the different room-images, each into a seperate tab page and all the desks (which are stored in a List of a "desk"-object.) at their position on the Image. All this is done in the xaml-file of the window.
My Problem is now that the canvas does not size the children, i.e. Image and desks, according to the parent's (i.e. tab page's) size.
I already tried using a grid oder putting a viewbox aroud the canvas and writing a custom canvas class by overriding the MeasureOverride() function.
Here is a minimal working example of my xaml file:
<TabControl x:Name="tabControl" Grid.Column="0">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Canvas>
<Image
Source="{Binding Image}" >
</Image>
<ItemsControl ItemsSource="{Binding Docks}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Height="34"
BorderThickness="1"
Tag="{Binding Self}"
>
<TextBlock
Text="{Binding Caption}"
RenderTransformOrigin="0.5,0.5"
Width="{Binding DockWidth}"
TextAlignment="Center"
VerticalAlignment="Center"
TextWrapping="Wrap"
>
</TextBlock>
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform X="{Binding Position.Item1}" Y="{Binding Position.Item2}"/>
</TransformGroup>
</Border.RenderTransform>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Does anybody have an idea what I can do? I'm not bound to a canvas. If there is another good way to do the Job, just let me know.
Viewbox should work. For example this will resize correctly (if you have jpeg with 96 dpi, if no, I think you'll have to recalculate canvas sizes):
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Viewbox Stretch="Fill"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Canvas Width="{Binding ElementName=img, Path=ActualWidth}"
Height="{Binding ElementName=img, Path=ActualHeight}">
<Image Name="img"
Source="pack://siteoforigin:,,,/images/img1.jpg" />
<Canvas Canvas.Left="100"
Canvas.Top="50">
<Rectangle Width="50"
Height="100"
Fill="Beige" />
<TextBlock Text="AAA" />
</Canvas>
</Canvas>
</Viewbox>
</Grid>

WPF ScrollViewer scroll-bars not changing size

I am not a WPF expert so please excuse my inappropriate use of terms. I have a ScrollViewer where I am displaying a captured image. And I have a slider with which I am zooming the image in and out. Zooming works fine, but the scrollbars are not changing their size. Hence when the image goes beyond the boundaries, I cannot scroll and view it. As if the scrollbars become useless because they haven't changed their size. Here is my XAML:
The Slider:
<Slider DockPanel.Dock="Right" Width="100" VerticalAlignment="Center" Minimum="0.2" Maximum="5"
Interval="1" Value="{Binding ScaleFactor}"/>
The rest of XAML:
<Border BorderThickness="1" BorderBrush="Black" Grid.Row="1">
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
CanContentScroll="True">
<ItemsControl ItemsSource="{Binding ItemCollection}" Margin="0"
Width="{Binding Root.Boundary.Width}" Height="{Binding Root.Boundary.Height}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ScaleFactor}"
ScaleY="{Binding ScaleFactor}"CenterX="0" CenterY="0"/>
</Canvas.LayoutTransform>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Image x:Name="capturedImage"
Source="{Binding Image}"
Width="{Binding Boundary.Width}"
Height="{Binding Boundary.Height}"/>
<Path x:Name="captureContour"
Data="{Binding Outline}"
Stroke="Black" StrokeThickness="4" Opacity="0.5"
StrokeLineJoin="Round">
<Path.LayoutTransform>
<ScaleTransform ScaleX="{Binding OutlineScale.X}"
ScaleY="{Binding OutlineScale.Y}" CenterX="0"CenterY="0"/>
</Path.LayoutTransform>
</Path>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="capturedImage" Property="IsMouseOver"
Value="True">
<Setter TargetName="captureContour" Property="Stroke"
Value="Blue"/>
<Setter TargetName="captureContour" Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect/>
</Setter.Value>
</Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Border>
The issue is due to the Canvas used as ItemsPanel. as Canvas does not expand or collapse with the size of it's children so ScrollViewer does not detect the change.
As a quick solution change the ItemsPanel to Grid. Since your example does not seems to be using Canvas properties i.e. Canvas.Left or Canvas.Top, this change may not make any difference in the appearance.
example
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ScaleFactor}"
ScaleY="{Binding ScaleFactor}"
CenterX="0"
CenterY="0" />
</Grid.LayoutTransform>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
make sure to have HorizontalAlignment="Left" VerticalAlignment="Top" in grid otherwise it may appear weird when you zoom.
give it a try and see if this is what you are looking for.
OK guys, here is what needs to be done in order to make it work:
<ItemsControl ItemsSource="{Binding ItemCollection}" Margin="0" Width="{Binding CanvasWidth}"
Height="{Binding CanvasHeight}"/>

UserControl with Bounding Box Scaling Point boxes

I want to make a UserControl like this image;
There are 9 boxes, top one is for rotating, while other one is for scaling purposes. I tried to make this with grid and background image, 2 rows, and small boxes with StackPanel, but have to provide lots of negative and positive margins to achieve this result. Is there any easy way to do so need help.
Thanks.
Something like this?
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="Height" Value="11"/>
<Setter Property="Width" Value="11"/>
<!-- For hittestvisibility -->
<Setter Property="Fill" Value="Transparent"/>
</Style>
</Grid.Resources>
<Line X1="0" Y1="1" X2="0" Y2="0" Stretch="Fill" Stroke="Black" />
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Height="200" Width="200" Margin="-5">
<Rectangle Margin="5" Height="Auto" Width="Auto" Fill="{x:Null}"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
<!-- The Content -->
<Rectangle Width="Auto" Height="Auto" Margin="20" Fill="Green"/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
Text="Content"/>
</Grid>
</Grid>
The terminology is Bounding Box

Shapes don't appear to be reacting to setting ZIndex?

Currently, I have three different types of objects that I draw to the screen (I'm using a ZoomableCanvas, if that makes a difference): beacons (concentric blue circles), tables (black rectangles), and debugRectangles (gold rectangles. The objects are displayed/layered on the Z-axis according to the order in which they're added to the ItemSource, but it's not always possible for me to add shapes in Z-ordering.
This image shows how it looks, depending on the order of objects being added. I'd like for the shapes to respect the Panel.ZIndexes I've set, and in doing so, look like the top image (except with the gold rectangle in the back).
<Style.Triggers>
<!-- Gold rectangles drawn here (color set in code) -->
<DataTrigger Binding="{Binding type}" Value="rectangle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Rectangle Fill="{Binding fill}" Stroke="{Binding border}" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="-1"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- Black rectangles drawn here -->
<DataTrigger Binding="{Binding type}" Value="tableBlock">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Rectangle Fill="{Binding fill}" Stroke="Black" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="50"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- Blue circles drawn here -->
<DataTrigger Binding="{Binding type}" Value="beacon">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Ellipse Fill="DodgerBlue" Width="{Binding outerRadius}" Height="{Binding outerRadius}" Panel.ZIndex="97"/>
<Ellipse Fill="SkyBlue" Width="{Binding innerRadius}" Height="{Binding innerRadius}" Panel.ZIndex="98"/>
<TextBlock Text="{Binding id}" HorizontalAlignment="Center" VerticalAlignment="Center" Panel.ZIndex="99"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
Within a template, they follow the order (I can rearrange the components of a beacon), but relative to each other, no dice. Can anyone identify the issue?
ZoomableCanvas relies upon Panel to render, which means it uses the standard ordering for ZIndex.
<Window x:Class="ZoomableApplication2.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">
<DockPanel>
<ZoomableCanvas>
<Rectangle Fill="Green" Height="250" Width="250" />
<Rectangle Fill="Red" Height="200" Width="400" Panel.ZIndex="2" />
<Rectangle Fill="Blue" Height="400" Width="200" />
</ZoomableCanvas>
</DockPanel>
</Window>
The problem you are having is that your visual tree looks like this:
ZoomableCanvas
Grid
Ellipse with Panel.ZIndex="98"
Since Ellipse is a child of Grid, the ZOrder doesn't affect the ZoomableCanvas, but instead sets the ZIndex of the Ellipse relative to the other children of the grid. In order to change the layering of the ZoomableCanvas, you need to set the ZOrder property on the child of the ZoomableCanvas – the Grid:
ZoomableCanvas
Grid with Panel.ZIndex="98"
Ellipse
Example:
<DataTemplate>
<Grid Panel.ZIndex="100">
<Rectangle Fill="{Binding fill}" Stroke="Black" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="50"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
If you are using a ListBox, you end up with additional levels in your tree:
ZoomableCanvas
ListBoxItem with ItemContainerStyle setting Panel.ZIndex="98"
Grid
Ellipse
Example of use:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Panel.ZIndex" Value="98" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
If you have multiple varying ZIndexes, you could expose a property on the data item and bind to that, or use a ItemContainerStyleSelector to do the logic in code-behind.
You can also use Snoop to look at the created tree to identify these sorts of issues.
Have you tried setting the Panel.ZIndex on the Grids?

Categories