IronPython, WPF, and button-free windows
So, how do we achieve something like this with WPF, using IronPython?

Assume Vista or later (if you want to support XP, do something different if System.Environment.OSVersion.Version.Major < 6)
Start with some XAML
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window2" Height="300" Width="400" MinWidth="400" MinHeight="100" WindowStyle="None" AllowsTransparency="True"> <Grid> <Grid Name="banner"> <Image Name="icon" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="20" Margin="4,4,0,0" /> <Label Height="28" Name="title" VerticalAlignment="Top" Background="WhiteSmoke" Margin="28,0,0,0">Label</Label> </Grid> <StackPanel Margin="0,28,0,0" Name="body" VerticalAlignment="Top" Height="230"> <TextBlock Height="100" Name="message" VerticalAlignment="Top" /> <Image Name="image" Stretch="None" Height="130" Width="400" /> </StackPanel> <StackPanel Name="buttons" Height="55" VerticalAlignment="Bottom" Orientation="Horizontal"> <!-- Button Height="23" Name="button1" Width="75" Margin="2">Button</Button --> </StackPanel> </Grid> </Window>
We can then use ctypes
to get at the glass APIs, and set up some re-usable types
from ctypes import * | |
from System.Windows.Interop import WindowInteropHelper, HwndSource | |
from System import IntPtr | |
from System.Windows.Media import Brushes, Colors, Brush | |
dwmapi = windll.dwmapi | |
class __MARGINS(Structure): | |
_fields_ = [("Left", c_int), ("Right", c_int), ("Top", c_int), ("Bottom", c_int)] | |
def ExtendGlassFrame(window, margin): | |
val = c_int() | |
result = dwmapi.DwmIsCompositionEnabled(byref(val)) | |
success = result == 0 and not (val == 0) | |
if not success: | |
return False | |
hwnd = WindowInteropHelper(window).Handle | |
if (hwnd == IntPtr.Zero): | |
return False | |
window.Background = Brushes.Transparent | |
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent | |
margins = __MARGINS(int(margin.Left), | |
int(margin.Right), | |
int(margin.Top), | |
int(margin.Bottom)) | |
result = dwmapi.DwmExtendFrameIntoClientArea(hwnd, byref(margins)) | |
return result == 0 | |
class __GlassHelper: | |
def __init__(self, w, t): | |
self.window = w | |
self.thickness = t | |
def ApplyGlass(self): | |
val = ExtendGlassFrame(self.window, self.thickness) | |
hwnd = WindowInteropHelper(self.window).Handle | |
HwndSource.FromHwnd(hwnd).AddHook(self.__wndproc) | |
def __wndproc(self, hwnd, msg, wparam, lparam, handled): | |
if msg == 0x031E: ## composition changed | |
handled.Value = True ## set ref argument | |
ExtendGlassFrame(self.window, self.thickness) | |
return IntPtr.Zero |
which we can then hook into our window (assume we have loaded the XAML into a variable window
)
# make the top region glass | |
window.AllowsTransparency = False | |
window.Background = Brushes.Transparent | |
window.FindName('title').Background = Brushes.Transparent | |
thickness = Thickness(0.0, float(window.FindName('title').Height), 0.0, 0.0) | |
helper = __GlassHelper(window, thickness) | |
window.SourceInitialized += lambda s,e : helper.ApplyGlass() | |
# make the content match what's shown in the taskbar | |
window.FindName('title').Content = window.Title | |
window.FindName('icon').Source = window.Icon | |
# makes the window movable by press and drag | |
window.MouseLeftButtonDown += lambda s,e : window.DragMove() |
Of course you now need to ensure you have some button or other control that will let you dismiss the window, now you no longer have the kiss of death available at top right.
The ref
parameter in the WndProc hook is the reason behind the previous post...
No comments :
Post a Comment