如何为PowerShell XAML窗体添加顶部栏拖动移动功能
PowerShell XAML无标题栏窗体拖动解决方案
问题说明
窗体设置WindowStyle="None"后,默认的标题栏拖动功能失效,需手动为自定义顶部栏添加拖动逻辑。
解决步骤
通过调用Windows API实现拖动功能,需在现有代码中添加以下关键部分:
1. 引入Windows API函数
在加载XAML之前添加,用于发送系统拖动指令:
Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); } "@
2. 绑定顶部栏拖动事件
在获取控件的代码段中,添加顶部栏的鼠标按下事件处理:
$topBar = $form.FindName("Top_Bar") # 顶部栏拖动逻辑 $topBar.Add_MouseLeftButtonDown({ [Win32]::ReleaseCapture() [Win32]::SendMessage($form.Handle, 0x0112, 0xF012, 0) })
修改后的完整代码
Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationCore # 引入Windows API实现拖动 Add-Type @" using System; using System.Runtime.InteropServices; public class Win32 { [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); } "@ # Load the XAML definition $xaml = @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="450" Width="487" x:Name="MainWindow" WindowStyle="None" ResizeMode="NoResize" Background="#181735"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid x:Name="Top_Bar" Background="#0f0f2d" Grid.ColumnSpan="2"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Button x:Name="BuTClose" FontFamily="arial" Content="X" Margin="204,0,0,0" VerticalAlignment="Center" Height="25" Width="25" FontSize="16" Grid.Column="1"> <Button.Style> <Style TargetType="Button"> <Setter Property="TextElement.FontFamily" Value="Arial"/> <Setter Property="Background" Value="#fe6584"/> <Setter Property="Cursor" Value="Hand"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#FF4c70"/> </Trigger> </Style.Triggers> </Style> </Button.Style> <Button.Template> <ControlTemplate TargetType="Button"> <Border Width="20" Height="20" CornerRadius="12" Background="{TemplateBinding Background}"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/> </Border> </ControlTemplate> </Button.Template> </Button> </Grid> <ListBox x:Name="AccountListBox" Background="#0f0f2d" Margin="152,64,151,0" Height="323" VerticalAlignment="Top" Grid.Row="1"/> <TextBlock Text="Clean AD Unlock" Foreground="White" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Arial" FontWeight="SemiBold"/> <Button x:Name="PopulateButton" Content="Check" Foreground="White" FontSize="14" FontFamily="arial" FontWeight="Medium" Margin="-97,64,239,336" Grid.Row="1"> <Button.Style> <Style TargetType="Button"> <Setter Property="TextElement.FontFamily" Value="Arial"/> <Setter Property="Background" Value="#fe6584"/> <Setter Property="Cursor" Value="Hand"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#FF4c70"/> </Trigger> </Style.Triggers> </Style> </Button.Style> <Button.Template> <ControlTemplate TargetType="Button"> <Border Width="100" Height="25" CornerRadius="12" Background="{TemplateBinding Background}"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/> </Border> </ControlTemplate> </Button.Template> </Button> <Button x:Name="UnlockButton" Content="Unlock" Foreground="White" FontSize="14" FontFamily="arial" FontWeight="Medium" Margin="-97,118,239,282" Grid.Row="1"> <Button.Style> <Style TargetType="Button"> <Setter Property="TextElement.FontFamily" Value="Arial"/> <Setter Property="Background" Value="#fe6584"/> <Setter Property="Cursor" Value="Hand"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#FF4c70"/> </Trigger> </Style.Triggers> </Style> </Button.Style> <Button.Template> <ControlTemplate TargetType="Button"> <Border Width="100" Height="25" CornerRadius="12" Background="{TemplateBinding Background}"> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/> </Border> </ControlTemplate> </Button.Template> </Button> <Rectangle HorizontalAlignment="Left" Height="190" Margin="-2,234,0,0" Grid.Row="1" VerticalAlignment="Top" Width="30" RenderTransformOrigin="0.5,0.5" RadiusX="15" RadiusY="15" Fill="#FF39FF14" Opacity="0.75"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="46.572"/> <TranslateTransform/> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle HorizontalAlignment="Left" Height="207" Margin="456,23,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="0.5,0.5" RadiusX="15" RadiusY="15" Fill="#FF39FF14" Grid.Row="1" Opacity="0.75"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="46.572"/> <TranslateTransform/> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle HorizontalAlignment="Left" Height="182" Margin="452,-12,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="0.5,0.5" RadiusX="15" RadiusY="15" Fill="#FFFF10F0" Grid.RowSpan="2" Opacity="0.75"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="46.572"/> <TranslateTransform/> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle HorizontalAlignment="Left" Height="300" Margin="-47,303,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="0.5,0.5" RadiusX="15" RadiusY="15" Fill="#FFFF10F0" Grid.Row="1" Opacity="0.75"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="46.572"/> <TranslateTransform/> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> </Grid> </Window> "@ # Create a PowerShell XML reader and a XAML form object $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader] $xaml) $form = [Windows.Markup.XamlReader]::Load($reader) # Retrieve the controls from the form $accountListBox = $form.FindName("AccountListBox") $populateButton = $form.FindName("PopulateButton") $unlockButton = $form.FindName("UnlockButton") $BuTClose = $form.FindName("BuTClose") $topBar = $form.FindName("Top_Bar") # 顶部栏拖动逻辑 $topBar.Add_MouseLeftButtonDown({ [Win32]::ReleaseCapture() [Win32]::SendMessage($form.Handle, 0x0112, 0xF012, 0) }) # Add an event handler for the Populate Button $populateButton.Add_Click({ #$lockedAccounts = Get-ADUser -Filter {Enabled -eq $false} -SearchBase "OU=Stenger Users,DC=stengerlaw,DC=com" -SearchScope Subtree $lockedAccounts = search-adaccount -lockedout -server dc | select SamAccountName, Name $accountListBox.Items.Clear() foreach ($account in $lockedAccounts) { $accountListBox.Items.Add($account.SamAccountName) } }) # Add an event handler for the Unlock Button $unlockButton.Add_Click({ $selectedAccount = $accountListBox.SelectedItem if ($selectedAccount) { $adminCreds = Get-Credential -Message "Enter your admin credentials" try { $adAccount = Get-ADUser -Identity $selectedAccount if ($adAccount) { Unlock-ADAccount -Identity $adAccount -Credential $adminCreds [System.Windows.MessageBox]::Show("The account '$selectedAccount' has been unlocked.", "Account Unlocked", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information) } else { [System.Windows.MessageBox]::Show("The account '$selectedAccount' was not found.", "Account Not Found", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error) } } catch { [System.Windows.MessageBox]::Show("An error occurred while unlocking the account. Error: $($_.Exception.Message)", "Error", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error) } } else { [System.Windows.MessageBox]::Show("No account selected.", "No Account Selected", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) } }) $BuTClose.Add_Click( { $form.Close() } ) # Retrieve the controls from the form $Form = $form.FindName("MainWindow") $form.ShowDialog() | Out-Null
关键代码解释
ReleaseCapture():释放当前线程的鼠标捕获,确保拖动指令能正确发送到窗体SendMessage($form.Handle, 0x0112, 0xF012, 0):向窗体发送WM_SYSCOMMAND系统消息,其中0xF012对应标题栏拖动的系统指令,模拟原生拖动行为
内容的提问来源于stack exchange,提问作者Jari Carlson
相关产品推荐
相关产品推荐

