The primary caveat I'd add for IronPython (besides the fact that I haven't done as much testing on it as on the C# side) is that its type inference may get confused by too many overloaded generic methods. In these situations, pass in named parameters; for example:
Also, the heavy use of generics in WPF Composites seems to crash my version of Python Tools for Visual Studio
via Tools - Options - Text Editor - Python - General - uncheck Auto List Members and Parameter Information. This does make it a bit like flying blind, but I just keep up the C# version of the classes, methods, etc. for convenient reference, copying, and pasting.
Lastly, this demo app doesn't utilize best practices in terms of layout and positioning. I merely threw things into Grids for a ready example. You'd likely prefer to do better by leveraging DockPanels and by giving more thoughtful consideration as to how to manage re-sizing of elements as the entire window (screen) is resized.
# WPF Composites Iron Python Sample
# Import Statements
import sys
sys.path.append(r"C:\Python27\lib")
import wpf
import clr
import sys
import System
#print sys.path
clr.AddReferenceToFile ('FasterWPF.dll')
clr.AddReferenceToFile ("System.Reactive.Core.dll")
clr.AddReferenceToFile ("System.Reactive.Interfaces.dll")
clr.AddReferenceToFile ("System.Reactive.Linq.dll")
clr.AddReferenceToFile ("PresentationFramework.Aero.dll")
clr.AddReferenceByPartialName("Microsoft.Dynamic.dll")
import FasterWPF
from System.Collections.Generic import *
from System.Windows.Controls import *
from System.Windows.Media import *
from System.Collections import *
from System.Guid import NewGuid
from System.Windows import *
from FasterWPF import *
from System.IO import *
from System import *
#from System import Tuple
clr.ImportExtensions(FasterWPF)
StackPanel = System.Windows.Controls.StackPanel
Application = System.Windows.Application
Button = System.Windows.Controls.Button
Settings = FasterWPF.SettingsManager
Label = System.Windows.Controls.Label
Thickness = System.Windows.Thickness
Window = System.Windows.Window
Media = System.Windows.Media
Common = FasterWPF.CommonExt
Factory = FasterWPF.Factory
Input = System.Windows.Input
Double = System.Double
class MenuBuilder():
"""Class to Build File Menu"""
#MENU CONSTANTS
def BACKGROUND_COLOR(self):
return Media.Brushes.WhiteSmoke
def FOREGROUND_COLOR(self):
return Media.Brushes.Black
def MOUSEOVER_BACKGROUND_COLOR(self):
return Media.Brushes.LightBlue
def MOUSEOVER_FOREGROUND_COLOR(self):
return Media.Brushes.Black
def MOUSEOVER_BORDER_COLOR(self):
return Media.Brushes.Gray
def FONT_FAMILY(self):
return "Consolas"
def FONT_SIZE(self):
return 12
def FONT_WEIGHT(self):
return FontWeights.Bold
#SUB-MENU CONSTANTS
def SUBMENU_BACKGROUND_COLOR(self):
return Media.Brushes.WhiteSmoke
def SUBMENU_MOUSEOVER_BACKGROUND_COLOR(self):
return Media.Brushes.LightBlue
def SUBMENU_MOUSEOVER_BORDER_COLOR(self):
return Media.Brushes.Gray
def SUBMENU_FONT_SIZE(self):
return 11
def SUBMENU_FONT_FAMILY(self):
return "Consolas"
def SUBMENU_FONT_WEIGHT(self):
return FontWeights.Bold
#MENU ITEM 1
def MENU1_WIDTH(self):
return 85.00
def MENU1_HEIGHT(self):
return 20.00
def MENU1_BORDER_WIDTH(self):
return 83.00
def MENU1_BORDER_HEIGHT(self):
return 18.00
#MENU ITEM 2
def MENU2_WIDTH(self):
return 90.00
def MENU2_HEIGHT(self):
return 20.00
def MENU2_BORDER_WIDTH(self):
return 88.00
def MENU2_BORDER_HEIGHT(self):
return 18.00
#MENU ITEM 2 SUB-MENUS
def SUB_MENU_WIDTH(self):
return 239.00
def SUB_MENU_HEIGHT(self):
return 69.00
def SUB_MENU_ITEM_WIDTH(self):
return 243.00
def SUB_MENU_ITEM_HEIGHT(self):
return 20.00
def SUB_MENU_ITEM_BORDER_WIDTH(self):
return 234.00
def SUB_MENU_ITEM_BORDER_HEIGHT(self):
return 18.00
#MENU ITEM 3
def MENU3_WIDTH(self):
return 75.00
def MENU3_HEIGHT(self):
return 20.00
def MENU3_BORDER_WIDTH(self):
return 73.00
def MENU3_BORDER_HEIGHT(self):
return 18.00
def ToggleSubMenu(self, cnvs, guidToDisplay, guidToHide, subMenuBorder, left, top, width):
MenuItemExt.DisplaySubMenu(cnvs, guidToDisplay, subMenuBorder, left, top, width);
MenuItemExt.HideSubMenu(cnvs, guidToHide)
def BuildMenuItem1(self, menu, canvas):
#MENU ITEM 1
m1 = Factory.BeginMenuComposite("_FILE", menu.Item1, self.MOUSEOVER_BACKGROUND_COLOR(), self.BACKGROUND_COLOR(), self.MOUSEOVER_FOREGROUND_COLOR(), self.FOREGROUND_COLOR(), self.FONT_FAMILY(), self.FONT_SIZE(), self.FONT_WEIGHT(), FontStyles.Normal, Brushes.WhiteSmoke)
Factory.SetMouseOverBorderColor(m1, self.MOUSEOVER_BORDER_COLOR(), Brushes.Transparent)
Factory.AddMenuItem(m1, self.MENU1_BORDER_WIDTH(), self.MENU1_BORDER_HEIGHT(), lambda sender, event: System.Windows.MessageBox.Show("Clicked File!") )
Factory.EndMenuComposite(m1)
return self
def BuildMenuItem2(self, menu, canvas):
cnvsguid1 = Guid.NewGuid().ToString()
cnvsguid2 = Guid.NewGuid().ToString()
cnvsguid3 = Guid.NewGuid().ToString()
#SUB-MENU 1
#Step 1: create a Border to hold the sub-menu and set it to hide itself on mouse-out
subMenuBorder = Border()
subMenuBorder.Initialize(self.SUB_MENU_WIDTH(), self.SUB_MENU_HEIGHT(), ContainerType.HorizontalPanel, Brushes.Gray, Thickness(1), Brushes.WhiteSmoke)
subMenuBorder.BeginSettings[Border]().SetItemBorderSettings[Border](self.SUB_MENU_WIDTH(), self.SUB_MENU_HEIGHT(), Brushes.LightGray, Thickness(1)).EndSettings[Border]()
subMenuBorder.SubscribeEventOnParent[Border]("MouseLeave", Input.MouseEventHandler( lambda sender, event: MenuItemExt.HideSubMenu(canvas, cnvsguid2) ) ) #a: On mouse out, hide sub-menu
#Step 2: create the submenu and set its orientation to vertical
subMenu1 = Factory.BeginMenuSettings(self.SUB_MENU_WIDTH(), self.SUB_MENU_HEIGHT(), ContainerType.HorizontalPanel, Brushes.WhiteSmoke)
#Settings.SetItemBorderSettings[Menu](subMenu1, 173.0, 20.0, Brushes.Silver, Thickness(1))
Factory.EndMenuSettings(subMenu1)
FasterWPF.MenuItemExt.SetMenuOrientationToVertical(subMenu1.Item1)
#Step 3: add the individual menuitems to the submenu
subMenuItem1 = Factory.BeginMenuComposite(self.SUB_MENU_ITEM_WIDTH(), self.SUB_MENU_ITEM_HEIGHT(),"_Choice #1 Alt+E+C", subMenu1.Item1, self.SUBMENU_MOUSEOVER_BACKGROUND_COLOR(), self.SUBMENU_BACKGROUND_COLOR(), Brushes.Black, Brushes.Black, self.SUBMENU_FONT_FAMILY(), self.SUBMENU_FONT_SIZE(), self.SUBMENU_FONT_WEIGHT(), FontStyles.Normal, Brushes.WhiteSmoke)
Factory.SetMouseOverBorderColor(subMenuItem1, self.SUBMENU_MOUSEOVER_BORDER_COLOR(), Brushes.Transparent)
Factory.AddMenuItem(subMenuItem1, self.SUB_MENU_ITEM_BORDER_WIDTH(), self.SUB_MENU_ITEM_BORDER_HEIGHT(), lambda sender, event: MessageBox.Show("Clicked Choice #1!"))
Factory.EndMenuComposite(subMenuItem1)
subMenuItem2 = Factory.BeginMenuComposite(self.SUB_MENU_ITEM_WIDTH(), self.SUB_MENU_ITEM_HEIGHT(), "_Another #2 Alt+E+A", subMenu1.Item1, self.SUBMENU_MOUSEOVER_BACKGROUND_COLOR(), self.SUBMENU_BACKGROUND_COLOR(), Brushes.Black, Brushes.Black, self.SUBMENU_FONT_FAMILY(), self.SUBMENU_FONT_SIZE(), self.SUBMENU_FONT_WEIGHT(), FontStyles.Normal, Brushes.WhiteSmoke)
Factory.SetMouseOverBorderColor(subMenuItem2, self.SUBMENU_MOUSEOVER_BORDER_COLOR(), Brushes.Transparent)
Factory.AddMenuItem(subMenuItem2, self.SUB_MENU_ITEM_BORDER_WIDTH(), self.SUB_MENU_ITEM_BORDER_HEIGHT(), lambda sender, event: MessageBox.Show("Clicked Choice #2!"))
Factory.EndMenuComposite(subMenuItem2)
subMenuItem3= Factory.BeginMenuComposite(self.SUB_MENU_ITEM_WIDTH(), self.SUB_MENU_ITEM_HEIGHT(), "_One More #3 Alt+E+O", subMenu1.Item1, self.SUBMENU_MOUSEOVER_BACKGROUND_COLOR(), self.SUBMENU_BACKGROUND_COLOR(), Brushes.Black, Brushes.Black, self.SUBMENU_FONT_FAMILY(), self.SUBMENU_FONT_SIZE(), self.SUBMENU_FONT_WEIGHT(), FontStyles.Normal, Brushes.WhiteSmoke)
Factory.SetMouseOverBorderColor(subMenuItem3, self.SUBMENU_MOUSEOVER_BORDER_COLOR(), Brushes.Transparent)
Factory.AddMenuItem(subMenuItem3, self.SUB_MENU_ITEM_BORDER_WIDTH(), self.SUB_MENU_ITEM_BORDER_HEIGHT(), lambda sender, event: MessageBox.Show("Clicked Choice #3!"))
Factory.EndMenuComposite(subMenuItem3)
#Step 4: add the sub-menu to the Border
s1 = Common.BeginComposite(subMenuBorder)
Common.AddExisting[Menu, Border](s1, 0, 0, subMenu1.Item1)
Common.EndComposite[Border, BorderArgs](s1, None)
#Step 5: create another main level menu item and add the Border to it via wiring On Click event
#MENU ITEM 2 with Sub-Menu
m2 = Factory.BeginMenuComposite(self.MENU2_WIDTH(), self.MENU2_HEIGHT(), "_EDIT", menu.Item1, self.MOUSEOVER_BACKGROUND_COLOR(), self.BACKGROUND_COLOR(), self.MOUSEOVER_FOREGROUND_COLOR(), self.FOREGROUND_COLOR(), self.FONT_FAMILY(), self.FONT_SIZE(), self.FONT_WEIGHT(), FontStyles.Normal, Brushes.WhiteSmoke)
Factory.SetMouseOverBorderColor(m2, self.MOUSEOVER_BORDER_COLOR(), Brushes.Transparent)
Factory.AddMenuItem(m2, self.MENU2_BORDER_WIDTH(), self.MENU2_BORDER_HEIGHT(), lambda sender, eventargs: (self.ToggleSubMenu(canvas, cnvsguid2, cnvsguid3, subMenuBorder, 89.0, 21.0, 500))) #b: On click, display sub-menu
Factory.EndMenuComposite(m2)
return self
# WPF Composites Iron Python Sample
# Import Statements
import sys
sys.path.append(r"C:\Python27\lib")
import wpf
import clr
import sys
import System
#print sys.path
clr.AddReferenceToFile ('FasterWPF.dll')
clr.AddReferenceToFile ("System.Reactive.Core.dll")
clr.AddReferenceToFile ("System.Reactive.Interfaces.dll")
clr.AddReferenceToFile ("System.Reactive.Linq.dll")
clr.AddReferenceToFile ("PresentationFramework.Aero.dll")
clr.AddReferenceByPartialName("Microsoft.Dynamic.dll")
import FasterWPF
import MenuBuilder
import Travel
from System.Collections.Generic import *
from System.Windows.Controls import *
from System.Windows.Media import *
from System.Collections import *
from System.Guid import NewGuid
from System.Windows import *
from FasterWPF import *
from System.IO import *
from System import *
#from System import Tuple
clr.ImportExtensions(FasterWPF)
StackPanel = System.Windows.Controls.StackPanel
Application = System.Windows.Application
Button = System.Windows.Controls.Button
Settings = FasterWPF.SettingsManager
Label = System.Windows.Controls.Label
Thickness = System.Windows.Thickness
Window = System.Windows.Window
Common = FasterWPF.CommonExt
Factory = FasterWPF.Factory
Media = System.Windows.Media
Double = System.Double
Docs = System.Windows.Documents
class MyWindow(Window):
def loadTravelOnButtonClick(self, border1):
travx=Travel.TravelEntity(1)
travx.set_counter(0)
travx.set_attraction("Lake Michigan")
travx.set_parking_garages([Travel.ParkingGarage(0, 'Blue'), Travel.ParkingGarage(1, 'Green')])
Common.GetChildFromComposite[TextBox, Border](border1, 0, 1).Text=travx.get_attraction()
Common.GetChildFromComposite[TextBox, Border](border1, 1, 1).Text=travx.get_parking_garages()[0].get_name()
def __init__(self):
mgguid0 = Guid.NewGuid().ToString()
mgguid1 = Guid.NewGuid().ToString()
winguid0 = Guid.NewGuid().ToString()
bdrguid1 = Guid.NewGuid().ToString()
# Window
self.Height = 1000.0
self.Width = 500.0
self.Title = 'Welcome to IronPython'
self.WindowState = WindowState.Maximized
FasterWPF.WindowExt.Initialize(self, ContainerType.ContentControlPanel)
a = Settings.BeginSettings[Window](self)
Settings.SetItemBorderSettings(a, Double.NaN, Double.NaN, Brushes.Gray, Thickness(2))
Settings.EndSettings[Window](a)
#Canvas - for displaying dialogs and sub-menus on top of
cnvs = Canvas();
FasterWPF.CanvasExt.Initialize(cnvs, ContainerType.ContentControlPanel);
cz = Settings.BeginSettings[Canvas](cnvs)
Settings.SetItemBorderSettings[Canvas](cz, Double.NaN, Double.NaN, Brushes.Gray, Thickness(0))
Settings.EndSettings[Canvas](cz)
#Top-Level Menu Bar
mnu = Factory.BeginMenuSettings(Double.NaN, 20.0, FasterWPF.ContainerType.HorizontalPanel, Media.Brushes.WhiteSmoke)
Settings.SetItemBorderSettings[Menu](mnu, Double.NaN, 20.0, Brushes.Silver, Thickness(2))
Factory.EndMenuSettings(mnu)
mBlder = MenuBuilder.MenuBuilder()
mBlder.BuildMenuItem1(mnu, cnvs)
mBlder.BuildMenuItem2(mnu, cnvs)
# Main Grid
mainGrid = Grid()
#mainGrid.ShowGridLines = True
#Add a Main Grid with 2 Rows and 1 Column
FasterWPF.GridExt.Initialize(source=mainGrid, width=1275.0, height=1090.0, minRowHeight=20.0, containerType=ContainerType.Grid, NumRows=2, NumColumns=1, colWidthToRepeat=1, unitTypeToRepeat=GridUnitType.Star, rowHeightToRepeat=900.0, rowUnitTypeToRepeat= GridUnitType.Auto, firstRowHeightOverride=25.0, firstRowUnitTypeToRepeat=GridUnitType.Pixel)
#These grid dimensions will be applied to the Grid housing the FileMenu
mainGrid.SetCompositeGridDimensions[Grid](NumRows=1, NumColumns=1, colWidthToRepeat=1, unitTypeToRepeat=GridUnitType.Auto, rowHeightToRepeat=1, rowUnitTypeToRepeat=GridUnitType.Auto);
#These grid dimensions will be applied to the Grid holding everything else
varyCompositeGrid = Settings.CompositeGridDimensions[Grid](width=1275.0, height=900.0, chain=mainGrid, NumRows=9, NumColumns=3, colWidthToRepeat=0.33, unitTypeToRepeat=GridUnitType.Star, rowHeightToRepeat=100.00, rowUnitTypeToRepeat=GridUnitType.Pixel)
b = Settings.BeginSettings[Grid](mainGrid)
Settings.SetItemBorderSettings[Grid](b, 1275.0, 1090.0, Brushes.Gray, Thickness(2))
Settings.SetFontOnLabel[Grid](b, 0, 0, "Segoe UI", 50.00, FontWeights.Bold, FontStyles.Normal)
Settings.SetAttachedProperty[Label, Grid](b, "ColumnSpan", 0, 0, lambda obj: obj.SetValue(Grid.ColumnSpanProperty, 3))
Settings.SetAttachedProperty[Border, Grid](b, "RowSpan", 2, 0, lambda obj: obj.SetValue(Grid.RowSpanProperty, 4))
Settings.Set[Border, Grid](b, 1,0, "HorizontalAlignment", HorizontalAlignment.Left)
Settings.SetTextColorForLabel[Grid](b, 0, 0, Brushes.White)
Settings.SetVerticalAlignment[Grid](b, 1, 0, VerticalAlignment.Center)
Settings.SetImageHorizontalAlignment[Grid](b, 1, 0, HorizontalAlignment.Left)
Settings.SetImageMargin[Grid](b, 1, 0, Thickness(0))
Settings.SetImageStretch[Grid](b, 1, 0, Stretch.Fill)
Settings.SetTextWrapping[Grid](b, 4, 0, System.Windows.TextWrapping.WrapWithOverflow)
Settings.EndSettings[Grid](b)
#1st Composite Added to MainGrid
c1 = Common.BeginComposite[Grid](mainGrid)
Common.AddExisting[Menu, Grid](c1, 0, 0, mnu.Item1) #Add Menu
Common.EndComposite[Grid, GridArgs](c1, GridArgs(0, 0))
wrapGrid = Border()
wrapGrid.Initialize(System.Double.NaN, System.Double.NaN, ContainerType.Grid, Brushes.Black, Thickness(2), Brushes.WhiteSmoke, CornerRadius(3), Thickness(7), Thickness(3))
wrapGrid.SetCompositeGridDimensions[Border](NumRows=8, NumColumns=2, colWidthToRepeat=150.00, unitTypeToRepeat=GridUnitType.Pixel, rowHeightToRepeat=25.00, rowUnitTypeToRepeat=GridUnitType.Pixel);
wg = Settings.BeginSettings[Border](wrapGrid)
Settings.Set[Label, Border](wg, "Margin", Thickness(1, 1, 1, 3))
Settings.Set[Label, Border](wg, "MinHeight", 35.0)
Settings.Set[TextBox, Border](wg, "MinHeight", 35.0)
Settings.Set[TextBox, Border](wg, 5, 1, "Text", "Default Hotel")
Settings.Set[TextBox, Border](wg, "Margin", Thickness(1, 1, 1, 3))
Settings.EndSettings[Border](wg)
w1 = Common.BeginComposite[Border](wrapGrid, bdrguid1)
Common.AddLabel[Border](w1, 0, 0, "Counter", Brushes.LightGray)
Common.AddLabel[Border](w1, 1, 0, "Attraction", Brushes.LightGray)
Common.AddLabel[Border](w1, 2, 0, "Location", Brushes.LightGray)
Common.AddLabel[Border](w1, 3, 0, "ParkingGarages", Brushes.LightGray)
Common.AddLabel[Border](w1, 4, 0, "Flight", Brushes.LightGray)
Common.AddLabel[Border](w1, 5, 0, "Hotel", Brushes.LightGray)
Common.AddLabel[Border](w1, 6, 0, "Phone", Brushes.LightGray)
Common.AddLabel[Border](w1, 7, 0, "Currency", Brushes.LightGray)
Common.AddAnything[TextBox, Border](w1, 0, 1)
Common.AddAnything[TextBox, Border](w1, 1, 1)
Common.AddAnything[TextBox, Border](w1, 2, 1)
Common.AddAnything[TextBox, Border](w1, 3, 1)
Common.AddAnything[TextBox, Border](w1, 4, 1)
Common.AddAnything[TextBox, Border](w1, 5, 1)
Common.AddAnything[TextBox, Border](w1, 6, 1)
Common.AddAnything[TextBox, Border](w1, 7, 1)
border1=Common.EndComposite[Border, BorderArgs](w1, None)
#2nd Composite Added to MainGrid
c2 = Common.BeginComposite[Grid](mainGrid, mgguid1)
Common.AddLabel[Grid](c2, 0, 0, "Travel Application", Media.Brushes.Navy)
Common.AddText[Grid](c2, 1, 2, "Escape on a relaxing vacation!")
Common.AddText[Grid](c2, 4, 1, "This is a child TextBlock\n in a grid composite at row 4 col 1")
#Common.ShowGridLinesOnGridComposite[Grid](c2)
Common.AddImage[Grid](c2, 1, 1, self.GetCompleteFilePath("Images\\lake.bmp"), UriKind.Absolute, 230.0, 2, 1)
FasterWPF.FlatDesignStyle.AddFlatButton[Grid](c2, 1, 0, 155.00, 57.00, "Click Here To\nLoad Travel Record", Brushes.Navy, Thickness(2), Brushes.WhiteSmoke, CornerRadius(3), Thickness(2), Thickness(100, 0,0,0), Brushes.LightSlateGray, Brushes.White,
lambda o, ev: self.loadTravelOnButtonClick(border1) )
Common.AddExisting[Border, Grid](c2, 2, 0, wrapGrid)
border2=Common.EndComposite[Grid, GridArgs](c2, GridArgs(1, 0), varyCompositeGrid)
border2.VerticalAlignment=VerticalAlignment.Top
border2.Background=Brushes.White
#Need to vertically align the grid inside of its Border
container2 = Common.GetContainerFromComposite[Grid, Grid](mainGrid, border2)
container2.VerticalAlignment=VerticalAlignment.Top
textBlock1 = Common.GetTextBlock(mainGrid, mgguid1, 1, 2)
textBlock1.VerticalAlignment=VerticalAlignment.Center
d = FontSettings()
d.FontFamily="Arial"
d.FontSize = 23
d.FontStyle = FontStyles.Italic
d.FontWeight = FontWeights.Bold
e = FontFamily("Tahoma")
FasterWPF.FontExt.ApplyFontSettings(d, e, textBlock1)
#Window contains a built-in Decorator but adding my own here to be explicit.
#This is for displaying red rectangles on top of fields to indicate invalid, required fields.
ad = Docs.AdornerDecorator()
ad.Initialize(Double.NaN, Double.NaN, ContainerType.ContentControlPanel)
adz = Settings.BeginSettings[Docs.AdornerDecorator](ad)
Settings.SetItemBorderSettings[Docs.AdornerDecorator](adz, Double.NaN, Double.NaN, Brushes.Gray, Thickness(2))
Settings.EndSettings[Docs.AdornerDecorator](adz)
#Add MainGrid to Decorator
g = Common.BeginComposite[Docs.AdornerDecorator](ad)
Common.AddExisting[Grid, Docs.AdornerDecorator](g, 0, 0, mainGrid)
Common.EndComposite[Docs.AdornerDecorator, AdornerDecoratorArgs](g, None)
#Add Decorator to Canvas
h = Common.BeginComposite[Canvas](cnvs)
Common.AddExisting[Docs.AdornerDecorator, Canvas](h, 0, 0, ad)
Common.EndComposite[Canvas, CanvasArgs](h, CanvasArgs(0, 0, 1))
#Add Canvas to Window
i = Common.BeginComposite[Window](self)
Common.AddExisting[Canvas, Window](i, 0, 0, cnvs)
Common.EndComposite[Window, WindowArgs](i, WindowArgs())
def GetCompleteFilePath(self, relativepath):
directoryName = System.IO.Path.GetDirectoryName(__file__)
fileName = System.IO.Path.Combine(directoryName, relativepath)
return fileName
if __name__ == '__main__':
Application().Run(MyWindow())
# Simple.Foo.Overloads[IEnumerable[str]](["alpha", "bravo", "charlie"])
#wpf.LoadComponent(self, 'WPFCompositesIronPythonSample.xaml')