Pour savoir ce qu'est le pattern MVVM (Model View ViewModel) je vous renvoie
à mon article précédent sur le sujet.
https://voltadynabim.blogspot.com/2023/03/dynamo-pythonwpf-avec-ironpython-et-mvvm.html
L'utilisation du MVVM passe par la création d'une classe qui hérite
de l'interface INotifyPropertyChanged, cela tombe bien, nous avons vu
comment implémenter une classe d'interface avec PythonNet dans un précédent
article.
https://voltadynabim.blogspot.com/2024/10/dynamo-python-pythonnet-et-les.html
Il est donc possible, avec PythonNet, de définir une classe appelable
depuis le CLR
Voici un Exemple d'une classe CLR (.Net)
class Temperature:
def __new__(cls, *args, **kwargs):
cls.args = args
cls.__namespace__ = "Temperature_tEfYX0DHE"
try:
module_type = __import__(cls.__namespace__)
return module_type._Temperature(*cls.args)
except ImportError as ex:
class _Temperature(System.IComparable):
__namespace__ = cls.__namespace__
def __init__(self, init_value):
self.__t = System.Int32(init_value)
self.__init_value = System.Int32(init_value)
super().__init__()
def CompareTo(self, obj):
if obj is None:
return System.Int32(1)
else:
return self.Celsius.CompareTo(obj.Celsius)
# add custom property
@clr.clrproperty(System.Int32)
def InitValue(self):
return self.__init_value
# define getter and setter
def get_Celsius(self):
return self.__t
def set_Celsius(self, value: System.Int32):
self.__t = System.Int32(value)
Celsius = clr.clrproperty(System.Int32, get_Celsius, set_Celsius)
# add custom method
@clr.clrmethod(System.Int32)
def ToFahrenheit(self):
return (self.Celsius * 1.8) + 32
@clr.clrmethod(System.Int32, System.Int32)
def Multiply(self, x):
return self.Celsius * x
return _Temperature(*cls.args)
Cependant, à partir la dernière version du moteur Dynamo
PythonNet3 (v1.1.0+) la Team Dynamo a rendu la syntaxe bien plus
simple.
class Temperature(System.IComparable):
__namespace__ = "Temperature_tEfYX0DHE"
def __init__(self, init_value):
self.__t = System.Int32(init_value)
self.__init_value = System.Int32(init_value)
super()
def CompareTo(self, obj):
if obj is None:
return System.Int32(1)
else:
return self.Celsius.CompareTo(obj.Celsius)
# add custom property
@clr.clrproperty(System.Int32)
def InitValue(self):
return self.__init_value
# define getter and setter
def get_Celsius(self):
return self.__t
def set_Celsius(self, value: System.Int32):
self.__t = System.Int32(value)
Celsius = clr.clrproperty(System.Int32, get_Celsius, set_Celsius)
# add custom method
@clr.clrmethod(System.Int32)
def ToFahrenheit(self):
return (self.Celsius * 1.8) + 32
@clr.clrmethod(System.Int32, System.Int32)
def Multiply(self, x):
return self.Celsius * x
Résumé de la syntaxe, les mots clés :__namespace__ = "MyNameSpace_"
: crée un nouveau type géré qui peut être utilisé à partir du
framework .Net
@clr.clrmethod(<input_Type>, <output_Type>)
: définit une méthode CLR
@clr.clrproperty(<output_Type>)
: définit une propriété CLR (seulement le getter), équivalent au décorateur
Python
@property
MyProperty = clr.clrproperty(<Type>, get_method, set_method)
: définit une propriété CLR ( getter et setter), équivalent à la méthode
Python property()
Dans le cadre d'une classe qui héritera
de l'interface
INotifyPropertyChanged
, on rajoutera les méthodes liées à
OnPropertyChanged
- Exemple avec un interverrouillage de 2 combobox
.......
import clr
import sys
import System
from System.Collections.Generic import List, Dictionary
from System.Collections.ObjectModel import ObservableCollection
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls
from System.Windows.Controls import *
import System.Windows.Controls.Primitives
from System.Collections.Generic import List
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
import time
import traceback
class ViewModel(INotifyPropertyChanged):
__namespace__ = "ViewModel_wOvcjxcQvgxpY" # rename it each edition
def __init__(self, lstItems):
super().__init__()
self._lstItems = ObservableCollection[System.Object](lstItems)
self._txtValue = System.String("")
self._itemA = self._lstItems.get_Item(0) if lstItems else None
self._itemB = self._lstItems.get_Item(1) if len(lstItems) > 1 else None
self._property_changed_handlers = []
# define getter and setter
def get_Items(self):
return self._lstItems
def set_Items(self, value):
self._lstItems = value
self.OnPropertyChanged("Items")
# apply clrproperty
Items = clr.clrproperty(ObservableCollection[System.Object], get_Items, set_Items)
def get_TxtValue(self):
return self._txtValue
def set_TxtValue(self, value):
self._txtValue = value
self.OnPropertyChanged("TxtValue")
# apply clrproperty
TxtValue = clr.clrproperty(System.String, get_TxtValue, set_TxtValue)
def get_ItemA(self):
return self._itemA
def set_ItemA(self, value):
if value == self._itemB:
self.ItemB = None
self._itemA = value
self.OnPropertyChanged("ItemA")
# apply clrproperty
ItemA = clr.clrproperty(System.String, get_ItemA, set_ItemA)
def get_ItemB(self):
return self._itemB
def set_ItemB(self, value):
if value == self._itemA :
self.ItemA = None
self._itemB = value
self.OnPropertyChanged("ItemB")
# apply clrproperty
ItemB = clr.clrproperty(System.String, get_ItemB, set_ItemB)
def OnPropertyChanged(self, property_name):
event_args = PropertyChangedEventArgs(property_name)
for handler in self._property_changed_handlers:
handler(self, event_args)
# Implementation of add/remove_PropertyChanged
def add_PropertyChanged(self, handler):
#print(handler)
if handler not in self._property_changed_handlers:
self._property_changed_handlers.append(handler)
def remove_PropertyChanged(self, handler):
#print(handler)
if handler in self._property_changed_handlers:
self._property_changed_handlers.remove(handler)
class MainForm(Window):
string_xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="IronPython WPF Form" Height="200" Width="300">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- ComboBox 1 -->
<Label Grid.Row="0" Grid.Column="0" Content="Select Option 1:" VerticalAlignment="Center"/>
<ComboBox Grid.Row="0" Grid.Column="1"
Name="ComboBox1" Margin="5" VerticalAlignment="Center"
ItemsSource="{Binding Items}"
SelectedItem="{Binding ItemA, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- ComboBox 2 -->
<Label Grid.Row="1" Grid.Column="0" Content="Select Option 2:" VerticalAlignment="Center"/>
<ComboBox Grid.Row="1" Grid.Column="1"
Name="ComboBox2" Margin="5" VerticalAlignment="Center"
ItemsSource="{Binding Items}"
SelectedItem="{Binding ItemB, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- Submit Button -->
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Submit"
Name="SubmitButton" Margin="5" Width="80" Height="30"
VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</Grid>
</Window>'''
def __new__(cls, *args):
reader = System.Xml.XmlReader.Create(System.IO.StringReader(MainForm.string_xaml))
window = System.Windows.Markup.XamlReader.Load(reader)
window.__class__ = cls
return window
def __init__(self, lstvalue):
super().__init__()
self.lstvalue = lstvalue
# out data
self.vm = ViewModel(lstvalue)
self.DataContext = self.vm
self.InitializeComponent()
def InitializeComponent(self):
#
self.button = LogicalTreeHelper.FindLogicalNode(self, "SubmitButton")
self.button.Click += self.ButtonClick
def ButtonClick(self, sender, e):
try:
self.Close()
except Exception as ex:
print(traceback.format_exc())
lstValues = IN[0]
out = []
my_window = MainForm(lstValues)
my_window.ShowDialog()
out.append(my_window.vm.ItemA)
out.append(my_window.vm.ItemB)
OUT = out
- Exemple avec un mapping d'ElementType / SousProjet via une DataGrid (ComboBox Column)
.
import clr
import sys
import System
from System.Collections.ObjectModel import ObservableCollection
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#Get Important vars
doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
sdkNumber = int(app.VersionNumber)
clr.AddReference('System.Data')
from System.Data import *
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls
from System.Windows.Controls import *
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
import time
import traceback
import itertools
class ViewModel(INotifyPropertyChanged): # INotifyPropertyChanged
__namespace__ = "ViewModel_jhggsbUbwQpY" # rename it each edition class
def __init__(self, elem_type, lst_Workset):
super().__init__()
self._elem_type = elem_type
self._SelectValue = lst_Workset[0] # set default workset
self._lst_Workset = ObservableCollection[DB.Workset](lst_Workset)
#
self._property_changed_handlers = []
self.PropertyChanged = None
@clr.clrproperty(DB.Element)
def ElementType(self):
return self._elem_type
@clr.clrproperty(System.String)
def Name(self):
return self._elem_type.get_Name()
@clr.clrproperty(System.String)
def FamilyName(self):
return self._elem_type.FamilyName
def get_SelectValue(self):
return self._SelectValue
def set_SelectValue(self, value):
if self._SelectValue != value:
self._SelectValue = value
self.OnPropertyChanged("SelectValue")
# Add SelectValue as a clr property
SelectValue = clr.clrproperty(DB.Workset, get_SelectValue, set_SelectValue)
@clr.clrproperty(ObservableCollection[DB.Workset])
def LstWorkset(self):
return self._lst_Workset
def OnPropertyChanged(self, property_name):
event_args = PropertyChangedEventArgs(property_name)
for handler in self._property_changed_handlers:
handler(self, event_args)
# Implementation of add/remove_PropertyChanged
def add_PropertyChanged(self, handler):
if handler not in self._property_changed_handlers:
self._property_changed_handlers.append(handler)
def remove_PropertyChanged(self, handler):
if handler in self._property_changed_handlers:
self._property_changed_handlers.remove(handler)
class MainWindow(Window):
string_xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Selection"
Height="700" MinHeight="700"
Width="700" MinWidth="780"
x:Name="MainWindow">
<Window.Resources>
</Window.Resources>
<Grid Width="auto" Height="auto">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Label
x:Name="label1"
Content="Selection"
Grid.Column="0" Grid.Row="0"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Margin="8,0,366.6,5"
Width="415" Height="25" />
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding}"
Grid.Column="0" Grid.Row="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="8,3,8,7"
SelectionUnit="Cell"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Family Name" Binding="{Binding FamilyName}" Width="*" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
<DataGridTextColumn Header="Category" Binding="{Binding ElementType.Category.Name}" Width="*" />
<DataGridTemplateColumn Header="Workset">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="Combobox"
ItemsSource="{Binding LstWorkset}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="200"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button
x:Name="buttonCancel"
Content="Annuler"
Grid.Column="0" Grid.Row="2"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Margin="18,13,0,10"
Height="30" Width="120">
</Button>
<Button
x:Name="buttonOK"
Content="OK"
Grid.Column="0" Grid.Row="2"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,12,22,10"
Height="30" Width="120">
</Button>
</Grid>
</Window>'''
def __init__(self, lst_wkset, lst_elems):
super().__init__()
self._lst_wkset = lst_wkset
self._lst_elems = lst_elems
self._set_elemTypeId = set(x.GetTypeId() for x in lst_elems if isinstance(x, FamilyInstance))
self._lst_elemType = [doc.GetElement(xId) for xId in self._set_elemTypeId if xId != ElementId.InvalidElementId]
#
#sort _lst_elemType by Name
self._lst_elemType= sorted(self._lst_elemType, key = lambda x : x.FamilyName)
#
# Create an ObservableCollection of MyDataViewModel objects
self.data = ObservableCollection[System.Object]()
for elem in self._lst_elemType:
self.data.Add(ViewModel(elem, self._lst_wkset))
#
self.pairLst = []
#
xr = XmlReader.Create(StringReader(MainWindow.string_xaml))
self.winLoad = XamlReader.Load(xr)
self.InitializeComponent()
def InitializeComponent(self):
try:
#self.winLoad.Resources["DataSetResource"] = self.dtSet
self.Content = self.winLoad.Content
#
self.dataGrid = LogicalTreeHelper.FindLogicalNode(self.winLoad, "dataGrid")
#
self.buttonCancel = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonCancel")
self.buttonCancel.Click += self.ButtonCancelClick
#
self.buttonOK = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonOK")
self.buttonOK.Click += self.ButtonOKClick
#
self.winLoad.Loaded += self.OnLoad
#
self.dataGrid.DataContext = self.data #self._tableDataType.DefaultView
self.winLoad.DataContext = self.data
# Set DataContext for the DataSet
except Exception as ex:
print(traceback.format_exc())
def OnLoad(self, sender, e):
print("UI loaded")
def ButtonCancelClick(self, sender, e):
self.outSelection = []
self.winLoad.Close()
def ButtonOKClick(self, sender, e):
try:
# get result from input Data (Binding)
self.pairLst = [[x.ElementType, x.SelectValue] for x in self.data]
self.winLoad.Close()
except Exception as ex:
print(traceback.format_exc())
lst_Elements = UnwrapElement(IN[0])
lst_Wkset = FilteredWorksetCollector(doc).OfKind(WorksetKind.UserWorkset).ToWorksets()
objWindow = MainWindow(lst_Wkset, lst_Elements)
objWindow.winLoad.ShowDialog()
OUT = objWindow.pairLst
Remarques et Astuces :
-
Vous pouvez utiliser l'interface
NotificationObject
de Dynamo.Core ce qui simplifie la syntaxe (uniquement possible avec PythonNet3)
- Ce n'est pas obligatoire, mais je vous conseille de renommer le
__namespace__
de votre classe ViewModel à chaque fois que vous modifiiez celle-ci
-
Si votre classe hérite de System.Windows.Window et que vous souhaitez que
l'objet généré par
XamlReader.Load(xr)
corresponde à votre instance de classe (self), 2 solutions :
-
redéfinir la classe avec le constructeur
__new__
(uniquement possible avec PythonNet3)
__init__
n'est pas le constructeur ☝
-
copier les propriétés de l'objet généré par
XamlReader.Load(xr)
à votre instance de classe (self)
-
Vous pouvez utiliser la librairie
DotNetProjects.Wpf.Extended.Toolkit
(Un fork OpenSource de wpftoolkit) qui est disponible dans le contexte Dyamo
https://github.com/dotnetprojects/WpfExtendedToolkit
- Exemple d'utilisation du constructeur __new__
import clr
import System
from System.Threading import Thread, ThreadStart, ApartmentState
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
clr.AddReference("DynamoCore")
from System.Windows import LogicalTreeHelper
from Dynamo.Core import NotificationObject
class ViewModel(NotificationObject):
__namespace__ = "ViewModel_zlcg" # rename it each edition class
def __init__(self):
super().__init__()
_text = "Hieee"
def get_Text(self):
return self._text
def set_Text(self, value):
self._text = value
self.RaisePropertyChanged("Text")
Text = clr.clrproperty(str, get_Text, set_Text)
class MyWindow(System.Windows.Window):
xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="82" Width="350">
<StackPanel Orientation="Horizontal" Margin="10">
<Label Content="Text:"/>
<Label Content="{Binding Text}"/>
<TextBox Margin="10 0" Width="150"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
<Button x:Name="Button" Content="Reverse" />
</StackPanel>
</Window>'''
def __new__(cls):
reader = System.Xml.XmlReader.Create(System.IO.StringReader(cls.xaml))
window = System.Windows.Markup.XamlReader.Load(reader)
window.__class__ = cls
return window
def __init__(self):
super().__init__()
self.vm = ViewModel()
self.DataContext = self.vm
LogicalTreeHelper.FindLogicalNode(self, "Button").Click += self.buttonClick
def buttonClick(self, sender, e):
self.DataContext.Text = self.DataContext.Text[::-1]
def main():
try:
window = MyWindow()
window.ShowDialog()
# print result
print(window.vm.Text)
except Exception as ex:
print(ex)
# create a STA thread to excecute in DynamoSandBox
thread = Thread(ThreadStart(main))
thread.SetApartmentState(ApartmentState.STA)
thread.Start()
thread.Join()
.
- Exemple de copie des propriétés de l'objet généré par XamlReader.Load(xr) à votre instance de classe (self)
import clr
import System
from System.Threading import Thread, ThreadStart, ApartmentState
clr.AddReference("System.Reflection")
from System.Reflection import BindingFlags
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
clr.AddReference("DynamoCore")
from System.Windows import LogicalTreeHelper
from Dynamo.Core import NotificationObject
class ViewModel(NotificationObject):
__namespace__ = "MyViewModel" # rename it each edition class
def __init__(self):
super().__init__()
_text = "Hieee"
def get_Text(self):
return self._text
def set_Text(self, value):
self._text = value
self.RaisePropertyChanged("Text")
Text = clr.clrproperty(str, get_Text, set_Text)
class MyWindow(System.Windows.Window):
xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="82" Width="350">
<StackPanel Orientation="Horizontal" Margin="10">
<Label Content="Text:"/>
<Label Content="{Binding Text}"/>
<TextBox Margin="10 0" Width="150"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
<Button x:Name="Button" Content="Reverse" />
</StackPanel>
</Window>'''
def __init__(self):
super().__init__()
xr = System.Xml.XmlReader.Create(System.IO.StringReader(MyWindow.xaml))
root = System.Windows.Markup.XamlReader.Load(xr)
# copy attributes from the root window to self
for prop_info in root.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance):
if prop_info.CanWrite:
try:
setattr(self, prop_info.Name, prop_info.GetValue(root))
except Exception as ex:
print(ex, f"{prop_info.Name=}")
self.vm = ViewModel()
self.DataContext = self.vm
LogicalTreeHelper.FindLogicalNode(self, "Button").Click += self.buttonClick
def buttonClick(self, sender, e):
self.DataContext.Text = self.DataContext.Text[::-1]
def main():
try:
window = MyWindow()
window.ShowDialog()
# print result
print(window.vm.Text)
except Exception as ex:
print(ex)
# create a STA thread to excecute in DynamoSandBox
thread = Thread(ThreadStart(main))
thread.SetApartmentState(ApartmentState.STA)
thread.Start()
thread.Join()
.
-
Exemple d'utilisation de la librairie
DotNetProjects.Wpf.Extended.Toolkit
System.Windows.Forms.NumericUpDown
import clr
import System
from System.Threading import Thread, ThreadStart, ApartmentState
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
clr.AddReference("DynamoCore")
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import LogicalTreeHelper
from Dynamo.Core import NotificationObject
clr.AddReference("DotNetProjects.Wpf.Extended.Toolkit")
class ViewModel(NotificationObject):
__namespace__ = "ViewModel_zlcxfQzcQg" ViewModel
def __init__(self):
super().__init__()
self._numeric_value = "1564"
def get_NumericValue(self):
return self._numeric_value
def set_NumericValue(self, value):
if self._numeric_value != value:
self._numeric_value = value
self.OnPropertyChanged("NumericValue")
# Add SelectValue as a clr property
NumericValue = clr.clrproperty(System.String, get_NumericValue, set_NumericValue)
class MyWindow(System.Windows.Window):
xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Height="200" Width="350">
<StackPanel Orientation="Vertical" Margin="10">
<xctk:IntegerUpDown Name="myUpDownControl" Margin="10" Value="{Binding NumericValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button x:Name="Button" Content="Close" Margin="10"/>
</StackPanel>
</Window>'''
def __init__(self):
super().__init__()
xr = System.Xml.XmlReader.Create(System.IO.StringReader(MyWindow.xaml))
root = System.Windows.Markup.XamlReader.Load(xr)
# copy attributes from the root window to self
for prop_info in root.GetType().GetProperties():
if prop_info.CanWrite:
try:
setattr(self, prop_info.Name, prop_info.GetValue(root))
except Exception as ex:
print(ex, f"{prop_info.Name=}")
self.vm = ViewModel()
self.DataContext = self.vm
LogicalTreeHelper.FindLogicalNode(self, "Button").Click += self.buttonClick
def buttonClick(self, sender, e):
self.Close()
def main():
try:
window = MyWindow()
window.ShowDialog()
globals()['results'] = window.vm.NumericValue
except Exception as ex:
print(ex)
results = None
# create a STA thread to excecute in DynamoSandBox
thread = Thread(ThreadStart(main))
thread.SetApartmentState(ApartmentState.STA)
thread.Start()
thread.Join()
OUT = results
https://dynamobim.org/pythonnet3-a-new-dynamo-python-to-fix-everything/
https://voltadynabim.blogspot.com/2023/03/dynamo-pythonwpf-avec-ironpython-et-mvvm.html
https://sametmax2.com/la-difference-entre-__new__-et-__init__-en-python/index.html
0 commentaires:
Enregistrer un commentaire