通过 PythonNet 使用 XAML 混合编程

在昨天的文章里,我介绍了如何利用 PythonNet 来从 Python 中调用 WPF 类库。这只是第一步。接下来将面临的挑战是,如何有效的应付复杂多变的 UI 设计呢?XAML 语言是用来解决这个问题的,它将界面设计与编程逻辑分割开来。但是 XAML 在 Visual Studio 中是通过很多工具来自动产生连接代码的。对于我们的纯手工打造的 Python 程序,如何能利用好 XAML 工具呢?

其实,WPF 类库的 Markup 名字空间下提供的 XamlReader 类就提供了动态加载 XAML 语言的功能。但关键是,用 XamlReader 语言加载的 XAML 语言中不能含有事件属性,你不能简单地在 Button 标签中添加 Click=”xxx” 属性。

这个问题的解决,其实是要求有一个良好的程序结构。在用 Visual Studio 生成的 C# 工程中,WPF 向导为我们自动配置好了这些,比如界面设计的 xxx.xaml 文件,界面的事件响应程序文件(也叫隐藏文件)xxx.xaml.cs ,以及自动生成的 xxx.g.cs 文件和 baml 文件。编译过程中,首先将 xaml 文件编译成 baml 文件,也就是二进制的格式,减少空间占有,然后将其打包进程序的资源里,在自动生成的 xxx.g.cs 文件中,程序从打包的资源里加载 baml ,然后安装事件响应程序。

所有这些自动的过程,在用 Python 编写的时候,只能改由我们手工打造了。这篇文章里,作者尝试着理清三种编程模式:1)纯代码打造;2)XAML 动态加载混合编码;3)XAML 编译成资源然后混合编码。有兴趣的朋友可以浏览一下。

好了,有了这些思路,我就开始编写一个简单的 XAML 动态加载的示范程序,这个程序的主界面将是一个 GridPanel 的布局控件,分成上下两格,放置两个按钮,按钮按下后会弹出对话框。

设计界面我选用免费的 Kaxaml :

接下来,定义一个 Python MainWindow 类,继承自 WPF Window 类:

class MainWindow(Window):
  def __init__(self):
    Window.__init__(self)

在构造函数中,首先利用 XamlReader 来动态加载 XAML 代码,得到一个根元素,这个根元素即是 Page:

    # dynamically create page from XAML
    xaml = """
    
        
      
        
        
      
      
      
      
    
    """
    page = XamlReader.Parse(xaml)

接下来,获取根元素中两格按钮的对象,存储到本对象的成员变量中,并安装事件委托:

    # connect Button1
    self.Button1 = LogicalTreeHelper.FindLogicalNode(page, "Button1")
    self.Button1.Click += self.Button1_Click

    # connect Button2
    self.Button2 = LogicalTreeHelper.FindLogicalNode(page, "Button2")
    self.Button2.Click += self.Button2_Click

最后,设置主窗口的一些基本属性,然后,将 XAML 加载出来的 Page 对象设为本窗体的 Content:

    # set main window properties, and install the page
    self.Title = "Python WPF App with XAML!"
    self.Width = 350
    self.Height = 300
    self.Content = page

下面是运行情况:

完整代码:

import clr
clr.AddReference("PresentationFramework.Classic, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
clr.AddReference("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

from System.Windows import Application
from System.Windows import Window
from System.Windows import MessageBox
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader
from System.Threading import Thread
from System.Threading import ApartmentState
from System.Threading import ThreadStart
from System import *

class MainWindow(Window):

  def __init__(self):
    Window.__init__(self)

    # dynamically create page from XAML
    xaml = """
    
        
      
        
        
      
      
      
      
    
    """
    page = XamlReader.Parse(xaml)

    # connect Button1
    self.Button1 = LogicalTreeHelper.FindLogicalNode(page, "Button1")
    self.Button1.Click += self.Button1_Click

    # connect Button2
    self.Button2 = LogicalTreeHelper.FindLogicalNode(page, "Button2")
    self.Button2.Click += self.Button2_Click

    # set main window properties, and install the page
    self.Title = "Python WPF App with XAML!"
    self.Width = 350
    self.Height = 300
    self.Content = page

  def Button1_Click(self, sender, e):
    MessageBox.Show(self.Button1.Content + " is clicked!")

  def Button2_Click(self, sender, e):
    MessageBox.Show(self.Button2.Content + " is clicked!")

def STAMain():
  app = Application()
  app.Run(MainWindow())

def main():
  t = Thread(ThreadStart(STAMain))
  t.ApartmentState = ApartmentState.STA
  t.Start()
  t.Join()

if __name__ == "__main__":
  main()


你可能感兴趣的:(button,import,wpf,python,properties)