I am trying to replicate the display rearranger UI in display settings, in which there are button placed with respect to their physical location and can be rearranged with mouse-drag
I can get the screen bounds from the screen class
System.Windows.Forms.Screen
and create button with the specified bounds in a canvas, but they are too large (i.e. 1366 x 768) to be shown in a container, so I decided to use WPF's inbuilt ViewBox control
The problem with viewbox is it scales not only the bounds but also the rendered element
I can't control the BorderThickness, FontSize and Margin, they become extremely negligible to notice or too big when the canvas size is changed.
What I need is Scale just the size, but not everything. I want the borderthickness to have a fixed size, not scaled. In the settings one the BorderThickness of the buttons stay the same even after resizing. What ever I try I get this >
Here the Button's size is 1366 x 768, its borderthickness is 1 px (not clearly visible, I have to set it 4 px to make it clear), and have a font size of 240 px.
This Button is hosted within a canvas whose size is programmaticaly determined, the canvas is hosted in a viewbox which is hosted in a border. What should I do to just scale the size?
I have finally achieved it, I got rid of the viewbox and managed to do it through code
The Code :
Private Sub LoadScreens()
Dim ox As Integer = 0
Dim oy As Integer = 0
Dim canvaswidth As Integer = 0
Dim canvasheight As Integer = 0
Dim rect As New List(Of System.Drawing.Rectangle)
For i = 0 To Forms.Screen.AllScreens.Count - 1
Dim screen = Forms.Screen.AllScreens(i)
Dim r = screen.Bounds
rect.Add(r)
Next
For Each r In rect
If r.X < ox Then ox = r.X
If r.Y < oy Then oy = r.Y
Next
For Each r In rect
If r.X + r.Width + Math.Abs(ox) > canvaswidth Then canvaswidth = r.X + r.Width + Math.Abs(ox)
If r.Y + r.Height + Math.Abs(oy) > canvasheight Then canvasheight = r.Y + r.Height + Math.Abs(oy)
Next
ScreenCanvas.Children.Clear()
Dim scale = GetScale(TParent.ActualWidth, TParent.ActualHeight, canvaswidth, canvasheight)
ScreenCanvas.Width = canvaswidth * scale
ScreenCanvas.Height = canvasheight * scale
For i = 0 To rect.Count - 1
Dim r = rect(i)
Dim left = (r.X - ox) * scale
Dim top = (r.Y - oy) * scale
Dim nr As New Primitives.ToggleButton With {
.Width = r.Width * scale,
.Height = r.Height * scale,
.Content = New TextBlock() With {.Text = i + 1, .VerticalAlignment = VerticalAlignment.Center, .HorizontalAlignment = HorizontalAlignment.Center}
}
ScreenCanvas.Children.Add(nr)
Canvas.SetLeft(nr, left)
Canvas.SetTop(nr, top)
Next
End Sub
Private Function GetScale(w1 As Double, h1 As Double, w2 As Double, h2 As Double) As Double
Dim scaleHeight = h1 / h2
Dim scaleWidth = w1 / w2
Dim scale = Math.Min(scaleHeight, scaleWidth)
Return scale
End Function
The ToggleButtons are programmaticaly created, scaled, positioned and are added to the scaled canvas : ScreenCanvas which is hosted in a grid TParent.
The XAML :
<Border CornerRadius="10" Margin="40" Padding="40" Background="#22aaaaaa">
<Grid x:Name="TParent" Height="150" >
<Grid.Resources>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="White" BorderBrush="Red" BorderThickness="2">
<ContentPresenter TextBlock.Foreground="Black"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Canvas x:Name="ScreenCanvas" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Grid>
</Border>
Here's the final result :
Now I can control the borderthickness, cornerradius, margin :)
Edit :
I have updated the UI and added code to update their location and size when the grid size is changed.
Code :
Private Sub TParent_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles TParent.SizeChanged
If e.WidthChanged AndAlso _init Then
Dim canvaswidth = ScreenCanvas.ActualWidth
Dim canvasheight = ScreenCanvas.ActualHeight
Dim scale = GetScale(TParent.ActualWidth, TParent.ActualHeight, canvaswidth, canvasheight)
ScreenCanvas.Width = canvaswidth * scale
ScreenCanvas.Height = canvasheight * scale
For Each c As FrameworkElement In ScreenCanvas.Children
c.Width = c.ActualWidth * scale
c.Height = c.ActualHeight * scale
Dim top = Canvas.GetTop(c) * scale
Dim left = Canvas.GetLeft(c) * scale
Canvas.SetTop(c, top)
Canvas.SetLeft(c, left)
Next
End If
End Sub
UI :