3

I was working on IronPython before (with WPF for developing some GUIs), and recently I started to try pythonnet.

But I found that the xaml file that worked on IronPython does not work on CPython + pythonnet. In IronPython, I can define a Button.Click in the xaml file, but it seems not possible in CPython. I have tried to look up for the answers, but nothing relevant was found. So hopefully you could save me here...

Here is my main script:

import clr
clr.AddReference(r"wpf\PresentationFramework")
from System.IO import StreamReader
from System.Windows.Markup import XamlReader
from System.Windows import Application, Window
from System.Threading import Thread, ThreadStart, ApartmentState

class MyWindow(Window):
    def __init__(self):
        stream = StreamReader('test.xaml')
        window = XamlReader.Load(stream.BaseStream)
        Application().Run(window)

    def Button_Click(self, sender, e):
        print('Button has clicked')

if __name__ == '__main__':
    thread = Thread(ThreadStart(MyWindow))
    thread.SetApartmentState(ApartmentState.STA)
    thread.Start()
    thread.Join()

And here is the test.xmal:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="WpfApplication1" Height="300" Width="300"> 
    <Grid>
        <Button x:Name="BUTTON" Content="Button" HorizontalAlignment="Left" Margin="101,82,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" Background="#FFFF1616"/>
    </Grid>
</Window> 

The error message I got is:

Unhandled Exception: Python.Runtime.PythonException: XamlParseException : 'Failed to create a 'Click' from the text 'Button_Click'.' Line number '6' and line position '132'.

Strangly if I load the same xaml and keep the same Class structure in IronPython, the script works just fine:

import wpf
from System.Windows import Application, Window

class MyWindow(Window):
    def __init__(self):
        self.ui = wpf.LoadComponent(self, 'test.xaml')

    def Button_Click(self, sender, e):
        print('Button has clicked')

if __name__ == '__main__':
    Application().Run(MyWindow())

Thank you very much for help!

3
  • Yes, It works with IronPython. I just tested again. Commented Sep 24, 2018 at 14:32
  • It seems IronPython was somehow able to read your event handler definition while python.net did not. Can you try if defining Button_Click before __init__ works? If it does not that most probably you 'll have to attach event handler in code and not in xaml. Commented Sep 24, 2018 at 14:46
  • Thanks for the comment Dipen, It does not work though. I got the same error message. Commented Sep 24, 2018 at 15:02

1 Answer 1

2

you cannot import wpf in python using python.net package. But you can load the XAML file using PresentationFramework, even though presentationframework under python.net has some limitation. The XamlReader can't perform event handling, so we can manually add the event for that button in the python file, not in the XAML file.

here the gui.xaml code

<Window 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       Title="WpfApplication1" Height="300" Width="300">
    <Grid>
        <Label Content="Hello world" HorizontalAlignment="Left" Height="32" Margin="65,77,0,0" VerticalAlignment="Top" Width="119"/>
        <Button Name="button1" Content="Button" HorizontalAlignment="Left" Margin="65,145,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window> 

Then here the python code

import clr
clr.AddReference("wpf\PresentationFramework") 
from System.IO import *
from System.Windows.Markup import XamlReader
from System.Windows import *
from System.Threading import Thread, ThreadStart, ApartmentState
from System.Windows.Controls import *

class MyWindow(Window):
    def __init__(self):
        try:
            stream = StreamReader('gui.xaml')
            self.window = XamlReader.Load(stream.BaseStream)
            ButtoninXAML = LogicalTreeHelper.FindLogicalNode(self.window, "button1") 
            ButtoninXAML.Click +=  RoutedEventHandler(self.Button_Click)
            Application().Run(self.window)      
        except Exception as ex:
            print(ex)
    def Button_Click(self, sender, e):
        print('clicked')
        
        
if __name__ == '__main__':
    thread = Thread(ThreadStart(MyWindow))
    thread.SetApartmentState(ApartmentState.STA)
    thread.Start()
    thread.Join()
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer black perl. I tried something similar to your answer and it worked.
Thank you. I also needed to import LogicalTreeHelper and RoutedEventHandler from System.Windows import Application, Window, LogicalTreeHelper, RoutedEventHandler

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.