I came across this website and was able to create a XAML window working from within Python Node. I am still exploring the implementation so it can be useful in Dynamo environment. Perhaps something like Datashape nodes but with WPF window instead of Windows Forms.
I have attached the dyn and the xaml in here. The window opens and the command works, however when I close this window, the entire Revit closes. I am sure the close event is somewhat tied with the Revit close event. I am wondering if someone would help me understand how I can register this window as a child of Revit so when I close it, it exit without closing Revit.
[MVVM.dyn (5.1 KB) ]
Copy and paste into a text file and rename it to WpfMvvmDemo.xaml. Also in the Python script please edit the path to this file so it is found by the script.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IronPython MVVM Demo"
Width="450"
SizeToContent="Height">
<Grid Margin="15" x:Name="grid1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" FontSize="24" Content="First Name:" />
<Label Grid.Row="0" Grid.Column="1" FontSize="24" Content="{Binding FirstName}" />
<Label Grid.Row="1" Grid.Column="0" FontSize="24" Content="Surname:" />
<Label Grid.Row="1" Grid.Column="1" FontSize="24" Content="{Binding Surname}" />
<Button Grid.Row="2" FontSize="24" Content="Change" Command="{Binding ChangeCommand}" />
</Grid>
Its because only one instance of the Application can be created per app domain, so when you close the form it calls close on the window which disposes of the Application and with it, Revit too. Instead, in your python use Show() instead:
xaml.Root.Show()
and remove the instantiation of the Application.
1 Like
In case anyone is looking to use the WPF with MVVM method in Python, here is a complete solution. note this method only works for Ironpython2
MVVM.dyn (7.2 KB)
XAML Code:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IronPython MVVM Demo" Width="450" SizeToContent="Height">
<Grid Margin="15" x:Name="grid1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" FontSize="24" Content="First Name:" />
<Label Grid.Row="0" Grid.Column="1" FontSize="24" Content="{Binding FirstName}" />
<Label Grid.Row="1" Grid.Column="0" FontSize="24" Content="Surname:" />
<Label Grid.Row="1" Grid.Column="1" FontSize="24" Content="{Binding Surname}" />
<Button Grid.Row="2" FontSize="24" Content="Change" Command="{Binding ChangeCommand}" />
</Grid>
</Window>
IRONPYTHON2 Code:
# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('RevitAPI')
clr.AddReference('ProtoGeometry')
clr.AddReference('PresentationFramework')
clr.AddReference('PresentationCore')
clr.AddReference('WindowsBase')
from System.Windows.Markup import XamlReader
from System.IO import FileStream, FileMode
#from viewmodel import ViewModel
import Autodesk
from Autodesk.Revit import *
from Autodesk.DesignScript.Geometry import *
#from Autodesk.Revit.UI.IExternalApplication import *
from System.IO import File
from System.Windows.Markup import XamlReader
from System.ComponentModel import INotifyPropertyChanged
from System.ComponentModel import PropertyChangedEventArgs
from System.Windows.Input import ICommand
from System.Windows import Application
from System.Windows.Media import Brushes
class XamlLoader(object):
def __init__(self, xamlPath):
stream = File.OpenRead(xamlPath)
self.Root = XamlReader.Load(stream)
def __getattr__(self, item):
"""Maps values to attributes.
Only called if there *isn't* an attribute with this name
"""
return self.Root.FindName(item)
class ViewModelBase(INotifyPropertyChanged):
def __init__(self):
self.propertyChangedHandlers = []
def RaisePropertyChanged(self, propertyName):
args = PropertyChangedEventArgs(propertyName)
for handler in self.propertyChangedHandlers:
handler(self, args)
def add_PropertyChanged(self, handler):
self.propertyChangedHandlers.append(handler)
def remove_PropertyChanged(self, handler):
self.propertyChangedHandlers.remove(handler)
class Command(ICommand):
def __init__(self, execute):
self.execute = execute
def Execute(self, parameter):
self.execute()
def add_CanExecuteChanged(self, handler):
pass
def remove_CanExecuteChanged(self, handler):
pass
def CanExecute(self, parameter):
return True
class ViewModel(ViewModelBase):
def __init__(self):
ViewModelBase.__init__(self)
self.FirstName = "Joe"
self.Surname = "Smith"
self.ChangeCommand = Command(self.change)
def change(self):
self.FirstName = "Dave"
self.Surname = "Brown"
self.RaisePropertyChanged("FirstName")
self.RaisePropertyChanged("Surname")
# def close(self):
# The inputs to this node will be stored as a list in the IN variables.
xamlFilePath = IN
print(xamlFilePath)
# Place your code below this line
# Load the XAML file
xaml_path = xamlFilePath[0]
#xamlFilePath
with FileStream(xaml_path, FileMode.Open) as fs:
window = XamlReader.Load(fs)
# Set the DataContext to the ViewModel
view_model = ViewModel()
window.DataContext = view_model
# Show the window
window.Show()
OUT= 0
3 Likes