Files
egtwpflib48/EgtWPFLib48/EgtMainWindow.vb
T
Emmanuele Sassi 37371ca9d6 EgtWPFLib48 2.5c1 :
- primo rilascio
2023-03-14 15:37:31 +01:00

431 lines
17 KiB
VB.net

' Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
'
' Step 1a) Using this custom control in a XAML file that exists in the current project.
' Add this XmlNamespace attribute to the root element of the markup file where it is
' to be used:
'
' xmlns:MyNamespace="clr-namespace:EgtWPFLib5"
'
'
' Step 1b) Using this custom control in a XAML file that exists in a different project.
' Add this XmlNamespace attribute to the root element of the markup file where it is
' to be used:
'
' xmlns:MyNamespace="clr-namespace:EgtWPFLib5;assembly=EgtWPFLib5"
'
' You will also need to add a project reference from the project where the XAML file lives
' to this project and Rebuild to avoid compilation errors:
'
' Right click on the target project in the Solution Explorer and
' "Add Reference"->"Projects"->[Browse to and select this project]
'
'
' Step 2)
' Go ahead and use your control in the XAML file. Note that Intellisense in the
' XML editor does not currently work on custom controls and its child elements.
'
' <MyNamespace:EgtMainWindow/>
'
Imports Microsoft.Win32
Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Forms
Imports System.Windows.Input
Imports System.Windows.Interop
Public Class EgtMainWindow
Inherits Window
Private _hwndSource As HwndSource
Private isMouseButtonDown As Boolean
Private isManualDrag As Boolean
Private mouseDownPosition As System.Windows.Point
Private positionBeforeDrag As System.Windows.Point
Private previousScreenBounds As System.Windows.Point
Public Property WindowRoot As Grid
Public Property LayoutRoot As Grid
Public Property MinimizeButton As System.Windows.Controls.Button
Public Property MaximizeButton As System.Windows.Controls.Button
Public Property RestoreButton As System.Windows.Controls.Button
Public Property CloseButton As System.Windows.Controls.Button
Public Property HeaderBar As Grid
Public Property HeightBeforeMaximize As Double
Public Property WidthBeforeMaximize As Double
Public Property PreviousState As WindowState
Shared Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(EgtMainWindow), New FrameworkPropertyMetadata(GetType(EgtMainWindow)))
End Sub
Public Sub New()
Dim currentDPIScaleFactor As Double = CDbl(SystemHelper.GetCurrentDPIScaleFactor())
Dim screen As Screen = Screen.FromHandle((New WindowInteropHelper(Me)).Handle)
AddHandler MyBase.SizeChanged, AddressOf Me.OnSizeChanged
AddHandler MyBase.StateChanged, AddressOf Me.OnStateChanged
AddHandler MyBase.Loaded, AddressOf Me.OnLoaded
Dim workingArea As Rectangle = screen.WorkingArea
MyBase.MaxHeight = CDbl((workingArea.Height + 16)) / currentDPIScaleFactor
AddHandler SystemEvents.DisplaySettingsChanged, AddressOf Me.SystemEvents_DisplaySettingsChanged
Me.AddHandler(Window.MouseLeftButtonUpEvent, New MouseButtonEventHandler(AddressOf Me.OnMouseButtonUp), True)
Me.AddHandler(Window.MouseMoveEvent, New System.Windows.Input.MouseEventHandler(AddressOf Me.OnMouseMove))
End Sub
Public Sub New(Owner As Window)
Me.New()
Me.Owner = Owner
End Sub
Public Function GetRequiredTemplateChild(Of T As DependencyObject)(ByVal childName As String) As T
Return CType(MyBase.GetTemplateChild(childName), T)
End Function
Public Overrides Sub OnApplyTemplate()
Me.WindowRoot = Me.GetRequiredTemplateChild(Of Grid)("WindowRoot")
Me.LayoutRoot = Me.GetRequiredTemplateChild(Of Grid)("LayoutRoot")
Me.MinimizeButton = Me.GetRequiredTemplateChild(Of System.Windows.Controls.Button)("MinimizeButton")
Me.MaximizeButton = Me.GetRequiredTemplateChild(Of System.Windows.Controls.Button)("MaximizeButton")
Me.RestoreButton = Me.GetRequiredTemplateChild(Of System.Windows.Controls.Button)("RestoreButton")
Me.CloseButton = Me.GetRequiredTemplateChild(Of System.Windows.Controls.Button)("CloseButton")
Me.HeaderBar = Me.GetRequiredTemplateChild(Of Grid)("PART_HeaderBar")
If Me.LayoutRoot IsNot Nothing AndAlso Me.WindowState = WindowState.Maximized Then
Me.LayoutRoot.Margin = GetDefaultMarginForDpi()
End If
If Me.CloseButton IsNot Nothing Then
AddHandler Me.CloseButton.Click, AddressOf CloseButton_Click
End If
If Me.MinimizeButton IsNot Nothing Then
AddHandler Me.MinimizeButton.Click, AddressOf MinimizeButton_Click
End If
If Me.RestoreButton IsNot Nothing Then
AddHandler Me.RestoreButton.Click, AddressOf RestoreButton_Click
End If
If Me.MaximizeButton IsNot Nothing Then
AddHandler Me.MaximizeButton.Click, AddressOf MaximizeButton_Click
End If
If Me.HeaderBar IsNot Nothing Then
Me.HeaderBar.[AddHandler](Grid.MouseLeftButtonDownEvent, New MouseButtonEventHandler(AddressOf Me.OnHeaderBarMouseLeftButtonDown))
End If
MyBase.OnApplyTemplate()
End Sub
Protected Overrides Sub OnInitialized(ByVal e As EventArgs)
AddHandler SourceInitialized, AddressOf OnSourceInitialized
MyBase.OnInitialized(e)
End Sub
' Proprietà che permette di impostare il nome del progetto
Public Shared ReadOnly ProjectNameProperty As DependencyProperty = DependencyProperty.Register("ProjectName", GetType(String), GetType(EgtMainWindow), New PropertyMetadata(""))
Public Property ProjectName() As Double
Get
Return CType(GetValue(ProjectNameProperty), Double)
End Get
Set(ByVal value As Double)
SetValue(ProjectNameProperty, value)
End Set
End Property
' Proprietà che permette di impostare la path del progetto
Public Shared ReadOnly ProjectPathProperty As DependencyProperty = DependencyProperty.Register("ProjectPath", GetType(String), GetType(EgtMainWindow), New PropertyMetadata(""))
Public Property ProjectPath() As Double
Get
Return CType(GetValue(ProjectPathProperty), Double)
End Get
Set(ByVal value As Double)
SetValue(ProjectPathProperty, value)
End Set
End Property
' Proprietà che permette di impostare uno UserControl per aggiungere elementi alla TitleBar
Public Shared ReadOnly TitlePanelProperty As DependencyProperty = DependencyProperty.Register("TitlePanel", GetType(Controls.Panel), GetType(EgtMainWindow), New PropertyMetadata(Nothing))
Public Property TitlePanel() As Controls.Panel
Get
Return DirectCast(GetValue(TitlePanelProperty), Controls.Panel)
End Get
Set(ByVal value As Controls.Panel)
SetValue(TitlePanelProperty, value)
End Set
End Property
' Proprietà che permette di impostare l'altezza della TitleBar
Public Shared ReadOnly CloseCommandProperty As DependencyProperty = DependencyProperty.Register("CloseCommand", GetType(ICommand), GetType(EgtMainWindow), New PropertyMetadata(Nothing))
Public Property CloseCommand() As ICommand
Get
Return DirectCast(GetValue(CloseCommandProperty), ICommand)
End Get
Set(ByVal value As ICommand)
SetValue(CloseCommandProperty, value)
End Set
End Property
Protected Overridable Sub OnHeaderBarMouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
If isManualDrag Then
Return
End If
Dim position As System.Windows.Point = e.GetPosition(Me)
Dim headerBarHeight As Integer = 36
Dim leftmostClickableOffset As Integer = 50
If position.X - Me.LayoutRoot.Margin.Left <= leftmostClickableOffset AndAlso position.Y <= headerBarHeight Then
If e.ClickCount <> 2 Then
Me.OpenSystemContextMenu(e)
Else
MyBase.Close()
End If
e.Handled = True
Return
End If
If e.ClickCount = 2 AndAlso MyBase.ResizeMode = ResizeMode.CanResize Then
Me.ToggleWindowState()
Return
End If
If MyBase.WindowState = WindowState.Maximized Then
Me.isMouseButtonDown = True
Me.mouseDownPosition = position
Else
Try
Me.positionBeforeDrag = New System.Windows.Point(MyBase.Left, MyBase.Top)
Me.DragMove()
Catch
End Try
End If
End Sub
Protected Sub ToggleWindowState()
If MyBase.WindowState <> WindowState.Maximized Then
MyBase.WindowState = WindowState.Maximized
Else
MyBase.WindowState = WindowState.Normal
End If
End Sub
Private Sub MaximizeButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.ToggleWindowState()
End Sub
Private Sub RestoreButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.ToggleWindowState()
End Sub
Private Sub MinimizeButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.WindowState = WindowState.Minimized
End Sub
Private Sub CloseButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
If IsNothing(CloseCommand) Then
Me.Close()
End If
End Sub
Private Shadows Sub OnSourceInitialized(ByVal sender As Object, ByVal e As EventArgs)
_hwndSource = CType(PresentationSource.FromVisual(Me), HwndSource)
End Sub
Private Sub SetMaximizeButtonsVisibility(ByVal maximizeButtonVisibility As Visibility, ByVal reverseMaximizeButtonVisiility As Visibility)
If Me.MaximizeButton IsNot Nothing Then
Me.MaximizeButton.Visibility = maximizeButtonVisibility
End If
If Me.RestoreButton IsNot Nothing Then
Me.RestoreButton.Visibility = reverseMaximizeButtonVisiility
End If
End Sub
Private Sub OpenSystemContextMenu(ByVal e As MouseButtonEventArgs)
Dim position As System.Windows.Point = e.GetPosition(Me)
Dim screen As System.Windows.Point = Me.PointToScreen(position)
Dim num As Integer = 36
If position.Y < CDbl(num) Then
Dim handle As IntPtr = (New WindowInteropHelper(Me)).Handle
Dim systemMenu As IntPtr = NativeUtils.GetSystemMenu(handle, False)
If MyBase.WindowState <> WindowState.Maximized Then
NativeUtils.EnableMenuItem(systemMenu, 61488, 0)
Else
NativeUtils.EnableMenuItem(systemMenu, 61488, 1)
End If
Dim num1 As Integer = NativeUtils.TrackPopupMenuEx(systemMenu, NativeUtils.TPM_LEFTALIGN Or NativeUtils.TPM_RETURNCMD, Convert.ToInt32(screen.X + 2), Convert.ToInt32(screen.Y + 2), handle, IntPtr.Zero)
If num1 = 0 Then
Return
End If
NativeUtils.PostMessage(handle, 274, New IntPtr(num1), IntPtr.Zero)
End If
End Sub
Protected Overridable Function GetDefaultMarginForDpi() As Thickness
Dim currentDPI As Integer = SystemHelper.GetCurrentDPI()
Dim thickness As Thickness = New Thickness(8, 8, 8, 8)
If currentDPI = 120 Then
thickness = New Thickness(7, 7, 7, 6)
ElseIf currentDPI = 144 Then
thickness = New Thickness(7, 7, 7, 3)
ElseIf currentDPI = 168 Then
thickness = New Thickness(7, 7, 7, 2)
ElseIf currentDPI = 192 Then
thickness = New Thickness(6, 6, 0, 0)
ElseIf currentDPI = 240 Then
thickness = New Thickness(6, 6, 0, 0)
End If
Return thickness
End Function
Protected Overridable Function GetFromMinimizedMarginForDpi() As Thickness
Dim currentDPI As Integer = SystemHelper.GetCurrentDPI()
Dim thickness As Thickness = New Thickness(7, 7, 5, 7)
If currentDPI = 96 Then
thickness = New Thickness(8, 8, 8, 8)
ElseIf currentDPI = 120 Then
thickness = New Thickness(7, 7, 7, 6)
ElseIf currentDPI = 144 Then
thickness = New Thickness(7, 7, 7, 3)
ElseIf currentDPI = 168 Then
thickness = New Thickness(7, 7, 7, 2)
ElseIf currentDPI = 192 Then
thickness = New Thickness(6, 6, 2, 2)
ElseIf currentDPI = 240 Then
thickness = New Thickness(6, 6, 0, 0)
End If
Return thickness
End Function
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim MyHandle As IntPtr = (New WindowInteropHelper(Me)).Handle
'If Environment.OSVersion.Version.Major >= 6 Then
' DwmSetWindowAttribute(MyHandle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 1, 4)
'End If
Dim screen As Screen = Screen.FromHandle(MyHandle)
Dim width As Double = CDbl(screen.WorkingArea.Width)
Dim workingArea As Rectangle = screen.WorkingArea
Me.previousScreenBounds = New System.Windows.Point(width, CDbl(workingArea.Height))
End Sub
Private Sub SystemEvents_DisplaySettingsChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim screen As Screen = Screen.FromHandle((New WindowInteropHelper(Me)).Handle)
Dim width As Double = CDbl(screen.WorkingArea.Width)
Dim workingArea As Rectangle = screen.WorkingArea
Me.previousScreenBounds = New System.Windows.Point(width, CDbl(workingArea.Height))
Me.RefreshWindowState()
End Sub
Private Sub OnSizeChanged(ByVal sender As Object, ByVal e As SizeChangedEventArgs)
If MyBase.WindowState = WindowState.Normal Then
Me.HeightBeforeMaximize = MyBase.ActualHeight
Me.WidthBeforeMaximize = MyBase.ActualWidth
Return
End If
If MyBase.WindowState = WindowState.Maximized Then
Dim screen As Screen = Screen.FromHandle((New WindowInteropHelper(Me)).Handle)
If Me.previousScreenBounds.X <> CDbl(screen.WorkingArea.Width) OrElse Me.previousScreenBounds.Y <> CDbl(screen.WorkingArea.Height) Then
Dim width As Double = CDbl(screen.WorkingArea.Width)
Dim workingArea As Rectangle = screen.WorkingArea
Me.previousScreenBounds = New System.Windows.Point(width, CDbl(workingArea.Height))
Me.RefreshWindowState()
End If
End If
End Sub
Private Shadows Sub OnStateChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim screen As Screen = Screen.FromHandle((New WindowInteropHelper(Me)).Handle)
Dim thickness As Thickness = New Thickness(0)
If Me.WindowState <> WindowState.Maximized Then
Dim currentDPIScaleFactor As Double = CDbl(SystemHelper.GetCurrentDPIScaleFactor())
Dim workingArea As Rectangle = screen.WorkingArea
Me.MaxHeight = CDbl((workingArea.Height + 16)) / currentDPIScaleFactor
Me.MaxWidth = Double.PositiveInfinity
If Me.WindowState <> WindowState.Maximized Then
Me.SetMaximizeButtonsVisibility(Visibility.Visible, Visibility.Collapsed)
End If
Else
thickness = Me.GetDefaultMarginForDpi()
If Me.PreviousState = WindowState.Minimized OrElse Me.Left = Me.positionBeforeDrag.X AndAlso Me.Top = Me.positionBeforeDrag.Y Then
thickness = Me.GetFromMinimizedMarginForDpi()
End If
Me.SetMaximizeButtonsVisibility(Visibility.Collapsed, Visibility.Visible)
End If
Me.LayoutRoot.Margin = thickness
Me.PreviousState = Me.WindowState
End Sub
Private Shadows Sub OnMouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs)
If Not Me.isMouseButtonDown Then
Return
End If
Dim currentDPIScaleFactor As Double = CDbl(SystemHelper.GetCurrentDPIScaleFactor())
Dim position As System.Windows.Point = e.GetPosition(Me)
System.Diagnostics.Debug.WriteLine(position)
Dim screen As System.Windows.Point = MyBase.PointToScreen(position)
Dim x As Double = Me.mouseDownPosition.X - position.X
Dim y As Double = Me.mouseDownPosition.Y - position.Y
If Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2)) > 1 Then
Dim actualWidth As Double = Me.mouseDownPosition.X
If Me.mouseDownPosition.X <= 0 Then
actualWidth = 0
ElseIf Me.mouseDownPosition.X >= MyBase.ActualWidth Then
actualWidth = Me.WidthBeforeMaximize
End If
If MyBase.WindowState = WindowState.Maximized Then
Me.ToggleWindowState()
Me.Top = (screen.Y - position.Y) / currentDPIScaleFactor
Me.Left = (screen.X - actualWidth) / currentDPIScaleFactor
Me.CaptureMouse()
End If
Me.isManualDrag = True
Me.Top = (screen.Y - Me.mouseDownPosition.Y) / currentDPIScaleFactor
Me.Left = (screen.X - actualWidth) / currentDPIScaleFactor
End If
End Sub
Private Sub OnMouseButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
Me.isMouseButtonDown = False
Me.isManualDrag = False
Me.ReleaseMouseCapture()
End Sub
Private Sub RefreshWindowState()
If MyBase.WindowState = WindowState.Maximized Then
Me.ToggleWindowState()
Me.ToggleWindowState()
End If
End Sub
End Class