Set footings Type using WPF

Hi All, @c.poupin

I’m implementing a pyrevit tool using MVVM (the logic may be similar when using Dynamo) that allows the user to select the desired Footing Type from a combobox dropdown list for each grid intersection point. For now, I’m focusing on assigning a Footing Type Instance to each position corresponding to an intersection point.

As you can see in the example below, the positions (i.e., intersection points) are correctly displayed in the UI, and I can add Footing Type Instances each time I click the Ajouter button. However, when I select a Type Instance for each position from the combobox dropdown lists, an exclamation mark appears for each selection. Additionally, I cannot determine whether those instances have been selected correctly because no message is printed when I click the Appliquer button.

Remark: In the Types comboboxes, I want to display the Name property for each FootingType instance while storing those instances for later use in the code. That’s why I used DisplayMemberPath="Name" in the xaml layout.

Please check my references below:

xaml layout
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Footings"
    Height="Auto" Width="460"
    SizeToContent="Height"
    ResizeMode="NoResize"
    WindowStartupLocation="CenterScreen">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="5*"/>
            <RowDefinition Height="85*"/>
            <RowDefinition Height="5*"/>
            <RowDefinition Height="5*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50*"/>
            <ColumnDefinition Width="30*"/>
            <ColumnDefinition Width="20*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="1" Grid.ColumnSpan="2" Text="Caracteristiques des semelles" FontWeight="Bold" FontSize="11" />
        <DataGrid x:Name="Symbols" Grid.Row="1" Grid.RowSpan="2" AutoGenerateColumns="False"
                Background="Transparent" GridLinesVisibility="None">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Position" Binding="{Binding Position}" Width="*" />
                <DataGridTemplateColumn Header="Type de Semelle" Width="2*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="Types" ItemsSource="{Binding LstTypes}"
                                      SelectedItem="{Binding SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                      DisplayMemberPath="Name">
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid >
        <StackPanel Grid.Row="1" Grid.Column="1">

            <Label Content="Longueur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
            <DockPanel>
                <TextBox x:Name="long_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                <Label Content="cm"/>
            </DockPanel>
            <Label Content="Largeur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
            <DockPanel>
                <TextBox x:Name="larg_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                <Label Content="cm"/>
            </DockPanel>
            <Label Content="Epaisseur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
            <DockPanel>
                <TextBox x:Name="ep_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                <Label Content="cm"/>
            </DockPanel>
        </StackPanel>

        <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Vertical">
            <Button Content="Ajouter" Width="85" Click="Add_Click" VerticalAlignment="Top" Margin="0,30,0,10"/>
            <Button Content="Supprimer" Width="85"  VerticalAlignment="Top" Margin="0,20,0,10"/>
            <Button Content="Supprimer tous" Width="85"  VerticalAlignment="Top" Margin="0,20,0,10"/>
        </StackPanel>
        <StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2">
            <Label Content="Contrainte de Béton :" VerticalAlignment="Center"/>
            <DockPanel>
                <TextBox x:Name="contrainte_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                <Label Content="MPA" />
            </DockPanel>
        </StackPanel>
        <StackPanel Grid.Row="3" Grid.ColumnSpan="3" Orientation="Horizontal" VerticalAlignment="Center" Width="210" Margin="0,5,0,0">
            <Button x:Name="Appliquer" Click="Appliquer_Click" Content="Appliquer" Width="100"/>
            <Button x:Name="Fermer" Click="Fermer_Click"  Content="Fermer" Width="100" Margin="10,0,0,0"/>
        </StackPanel>

    </Grid>
</Window>

Footing_script
# -*- coding: UTF-8 -*-
import wpf, clr, os, itertools, math
from pyrevit import forms, revit

from System.Windows import Window
from System import Array
from System.Collections.ObjectModel import ObservableCollection
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
from System.Windows.Controls import CheckBox, TextBlock, TextBox, ListBoxItem
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import StructuralType  # Add this import

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application
results = clr.Reference[IntersectionResultArray]()


class FootingType:
    def __init__(self, d1, d2, d3):
        self.d1 = d1
        self.d2 = d2
        self.d3 = d3
        if d1 < d2:
            self._dim = [d1, d2, d3]
        else:
            self._dim = [d2, d1, d3]

    @property
    def H(self):
        return self._dim[0]

    @property
    def W(self):
        return self._dim[1]

    @property
    def T(self):
        return self._dim[2]

    @property
    def Name(self):
        return "{}cm x {}cm x {}cm".format(self.W, self.H, self.T)

    def __eq__(self, other):
        return self.H == other.H and self.W == other.W and self.T == other.T

    def __hash__(self):
        return hash(self.Name)

class ViewModelBase(INotifyPropertyChanged):
    
    def __init__(self):
        self._property_changed = None

    def add_PropertyChanged(self, handler):
        self._property_changed = handler

    def remove_PropertyChanged(self, handler):
        self._property_changed = None

    def notify_property_changed(self, property_name):
        if self._property_changed:
            self._property_changed(self, PropertyChangedEventArgs(property_name))

class Footings_VM(ViewModelBase):
    
    def __init__(self, Position):
        self._position = Position
        self._types = ObservableCollection[object]()
        self._selected_type = None

    @property
    def Position(self):
        return self._position

    @Position.setter
    def Position(self, value):
        self._position = value
        self.notify_property_changed('Position')

    @property
    def LstTypes(self):
        return self._types

    @LstTypes.setter
    def LstTypes(self, lst_value):
        self._types = ObservableCollection[object](lst_value)
        self.notify_property_changed('LstTypes')

    @property
    def SelectedType(self):
        return self._selected_type

    @SelectedType.setter
    def SelectedType(self, value):
        self._selected_type = value
        self.notify_property_changed('SelectedType')

    def AddType(self, footing_type):
        self._types.Add(footing_type)
        self.notify_property_changed('LstTypes')

class MyWindow(Window):
    
    def __init__(self):
        script_path = os.path.dirname(__file__)
        xaml_path = os.path.join(script_path, 'footings.xaml')
        wpf.LoadComponent(self, xaml_path)
        self.Data = ObservableCollection[Footings_VM]()
        symbols = self._grids_symbols()
        for s in symbols:
            foot = Footings_VM(s)
            self.Data.Add(foot)
        self.Symbols.ItemsSource = self.Data

    def _grids_symbols(self):
        Symbols = []
        grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids) \
            .WhereElementIsNotElementType().ToElements()
        for gd_a, gd_b in itertools.combinations(grids, 2):
            curveA = gd_a.Curve
            curveB = gd_b.Curve
            vectA = curveA.ComputeDerivatives(0.5, True).BasisX
            vectB = curveB.ComputeDerivatives(0.5, True).BasisX
            symA = gd_a.Name
            symB = gd_b.Name

            # check if lines are parallel
            if abs(vectA.CrossProduct(vectB).Z) < 0.01:
                # if true go to the next iteration
                continue
            results = clr.Reference[IntersectionResultArray]()
            result = curveA.Intersect(curveB, results)
            if result == SetComparisonResult.Overlap:
                
                if symA.isalpha() and symB.isdigit():
                    symbol = "{}-{}".format(symA, symB)
                elif symA.isdigit() and symB.isalpha():
                    symbol = "{}-{}".format(symB, symA)
                else:
                    continue  

                Symbols.append(symbol)

        return sorted(Symbols)

    def Add_Click(self, sender, e):
        try:
            # Get values from the text boxes
            long_value = float(self.long_value.Text)
            larg_value = float(self.larg_value.Text)
            ep_value = float(self.ep_value.Text)

            # Create a new FootingType instance
            foot = FootingType(long_value, larg_value, ep_value)

            # Add the FootingType instance to all Footings_VM instances in the Data collection
            for vm in self.Data:
                vm.AddType(foot)
                print("Footing Added: {}".format(foot.Name))  # Debugging



            self.long_value.Clear()
            self.larg_value.Clear()
            self.ep_value.Clear()


        except ValueError:
            forms.alert("Please enter valid numeric values for dimensions.")

    def Appliquer_Click(self, sender, e):

        selected_foot = [vm.SelectedType for vm in self.Data if vm.SelectedType is not None]
        if selected_foot:
            print("Selected Footings: {}".format([foot.Name for foot in selected_foot]))
        else:
            print("No footings selected.")
        self.Close()

    def Fermer_Click(self, sender, e):
        self.Close()

def main(doc=None):
    """Main entrypoint for the script."""
    doc = doc or revit.doc
    foundation = MyWindow()
    foundation.ShowDialog()

if __name__ == "__main__":
    main()

How can I fix this issue?

Thanks.

Hi,
I haven’t tested your code, but the first error I see is that you’ve omitted to initialize the super class (inheritance )

class Footings_VM(ViewModelBase):
    
    def __init__(self, Position):
        ViewModelBase.__init__(self) # need to init the super class 
        self._position = Position
        self._types = ObservableCollection[object]()
        self._selected_type = None
1 Like

I corrected this error and updated the code by adding debug points in the Appliquer_Click function. Then, I tested the code by adding two footing types, and it seems to be working for instance addition. However, no footing is selected, as shown in the output!

console output :

Footing Added: 100.0cm x 100.0cm x 20.0cm
Footing Added: 200.0cm x 200.0cm x 20.0cm
No footings selected.
updated code
# -*- coding: UTF-8 -*-
import wpf, clr, os, itertools, math
from pyrevit import forms, revit

from System.Windows import Window
from System import Array
from System.Collections.ObjectModel import ObservableCollection
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
from System.Windows.Controls import CheckBox, TextBlock, TextBox, ListBoxItem
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import StructuralType  # Add this import

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application
results = clr.Reference[IntersectionResultArray]()


class FootingType:
    def __init__(self, d1, d2, d3):
        self.d1 = d1
        self.d2 = d2
        self.d3 = d3
        if d1 < d2:
            self._dim = [d1, d2, d3]
        else:
            self._dim = [d2, d1, d3]

    @property
    def H(self):
        return self._dim[0]

    @property
    def W(self):
        return self._dim[1]

    @property
    def T(self):
        return self._dim[2]

    @property
    def Name(self):
        return "{}cm x {}cm x {}cm".format(self.W, self.H, self.T)

    def __eq__(self, other):
        return self.H == other.H and self.W == other.W and self.T == other.T

    def __hash__(self):
        return hash(self.Name)

class ViewModelBase(INotifyPropertyChanged):

    def __init__(self):
        self._property_changed = None

    def add_PropertyChanged(self, handler):
        self._property_changed = handler

    def remove_PropertyChanged(self, handler):
        self._property_changed = None

    def notify_property_changed(self, property_name):
        if self._property_changed:
            self._property_changed(self, PropertyChangedEventArgs(property_name))

class Footings_VM(ViewModelBase):

    def __init__(self, Position):
        ViewModelBase.__init__(self)
        self._position = Position
        self._types = ObservableCollection[object]()
        self._selected_type = ""

    @property
    def Position(self):
        return self._position

    @Position.setter
    def Position(self, value):
        self._position = value
        self.notify_property_changed('Position')

    @property
    def LstTypes(self):
        return self._types

    @LstTypes.setter
    def LstTypes(self, lst_value):
        self._types = ObservableCollection[object](lst_value)
        self.notify_property_changed('LstTypes')

    @property
    def SelectedType(self):
        return self._selected_type

    @SelectedType.setter
    def SelectedType(self, value):
        self._selected_type = value
        self.notify_property_changed('SelectedType')

    def AddType(self, footing_type):
        self._types.Add(footing_type)
        self.notify_property_changed('LstTypes')

class MyWindow(Window):

    def __init__(self):
        script_path = os.path.dirname(__file__)
        xaml_path = os.path.join(script_path, 'footings.xaml')
        wpf.LoadComponent(self, xaml_path)
        self.Data = ObservableCollection[Footings_VM]()
        symbols = self._grids_symbols()
        for s in symbols:
            foot = Footings_VM(s)
            self.Data.Add(foot)
        self.Symbols.ItemsSource = self.Data

    def _grids_symbols(self):
        Symbols = []
        grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids) \
            .WhereElementIsNotElementType().ToElements()
        for gd_a, gd_b in itertools.combinations(grids, 2):
            curveA = gd_a.Curve
            curveB = gd_b.Curve
            vectA = curveA.ComputeDerivatives(0.5, True).BasisX
            vectB = curveB.ComputeDerivatives(0.5, True).BasisX
            symA = gd_a.Name
            symB = gd_b.Name

            # check if lines are parallel
            if abs(vectA.CrossProduct(vectB).Z) < 0.01:
                # if true go to the next iteration
                continue
            results = clr.Reference[IntersectionResultArray]()
            result = curveA.Intersect(curveB, results)
            if result == SetComparisonResult.Overlap:

                if symA.isalpha() and symB.isdigit():
                    symbol = "{}-{}".format(symA, symB)
                elif symA.isdigit() and symB.isalpha():
                    symbol = "{}-{}".format(symB, symA)
                else:
                    continue

                Symbols.append(symbol)

        return sorted(Symbols)

    def Add_Click(self, sender, e):
        try:
            # Get values from the text boxes
            long_value = float(self.long_value.Text)
            larg_value = float(self.larg_value.Text)
            ep_value = float(self.ep_value.Text)

            # Create a new FootingType instance
            foot = FootingType(long_value, larg_value, ep_value)

            # Add the FootingType instance to all Footings_VM instances in the Data collection
            for vm in self.Data:
                vm.AddType(foot)

            print("Footing Added: {}".format(foot.Name))  # Debugging



            self.long_value.Clear()
            self.larg_value.Clear()
            self.ep_value.Clear()


        except ValueError:
            forms.alert("Please enter valid numeric values for dimensions.")

    def Appliquer_Click(self, sender, e):
        selected_foot = [vm.SelectedType for vm in self.Data if isinstance(vm.SelectedType, FootingType)]
        if selected_foot:
            print("Selected Footings: {}".format([foot.Name for foot in selected_foot]))
        else:
            print("No footings selected.")
        self.Close()

    def Fermer_Click(self, sender, e):
        self.Close()

def main(doc=None):
    """Main entrypoint for the script."""
    doc = doc or revit.doc
    foundation = MyWindow()
    foundation.ShowDialog()

if __name__ == "__main__":
    main()```

Thanks.

self._selected_type must be same type of self._types ( ObservableCollection[FootingType] ), so at initialization self._selected_type can’t be a string

a workaround is to create a dummy object

code IronPython3 for Dynamo tested on Revit 2025

there’s probably still room for improvement

import clr  
import sys
import System
from System import Array
from System.Collections.Generic import List, KeyValuePair
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
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)

clr.AddReference("IronPython.Wpf")
clr.AddReference('System.Core')
clr.AddReference('System.Xml')
clr.AddReference('PresentationCore')
clr.AddReference('PresentationFramework')
#clr.AddReferenceByPartialName("WindowsBase")

from System.IO import StringReader
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.ComponentModel import INotifyPropertyChanged
from System.ComponentModel import PropertyChangedEventArgs
from System.Windows import Window
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
#from System.Windows.Controls import CheckBox, TextBlock, TextBox, ListBoxItem

import wpf
import itertools
import traceback

class FootingType(System.Object):
    def __init__(self, d1, d2, d3):
        self.d1 = d1
        self.d2 = d2
        self.d3 = d3
        if d1 < d2:
            self._dim = [d1, d2, d3]
        else:
            self._dim = [d2, d1, d3]

    @property
    def H(self):
        return self._dim[0]

    @property
    def W(self):
        return self._dim[1]

    @property
    def T(self):
        return self._dim[2]

    @property
    def Name(self):
        if 0 == self.W == self.H == self.T:
            return "??"
        else:
            return "{}cm x {}cm x {}cm".format(self.W, self.H, self.T)

    def __eq__(self, other):
        return self.H == other.H and self.W == other.W and self.T == other.T

    def __hash__(self):
        return hash(self.Name)

class ViewModelBase(INotifyPropertyChanged):

    def __init__(self):
        self.propertyChangedHandlers = []
    #
    # define a method to raise the PropertyChanged event
    def RaisePropertyChanged(self, propertyName):
        # create a PropertyChangedEventArgs object with the name of the changed property
        args = PropertyChangedEventArgs(propertyName)
        for handler in self.propertyChangedHandlers:
            # invoke each of the registered property changed handlers with the ViewModelBase instance and the event arguments
            handler(self, args)
    #
    # define a method to add a property changed handler
    def add_PropertyChanged(self, handler):
        self.propertyChangedHandlers.append(handler)
    #
    # define a method to remove a property changed handler
    def remove_PropertyChanged(self, handler):
        self.propertyChangedHandlers.remove(handler)
        
        
class Footings_VM(ViewModelBase):

    def __init__(self, Position):
        ViewModelBase.__init__(self)
        self._position = Position
        self._types = ObservableCollection[FootingType]() 
        self._types.Add(FootingType(0, 0, 0)) # create dummy object 
        self._selected_type = self._types[0] # assign the dummy object at initialisation 

    @property
    def Position(self):
        return self._position

    @Position.setter
    def Position(self, value):
        self._position = value
        self.RaisePropertyChanged('Position')

    @property
    def LstTypes(self):
        return self._types

    @LstTypes.setter
    def LstTypes(self, lst_value):
        self._types = ObservableCollection[object](lst_value)
        self.RaisePropertyChanged('LstTypes')

    @property
    def SelectedType(self):
        return self._selected_type

    @SelectedType.setter
    def SelectedType(self, value):
        self._selected_type = value
        self.RaisePropertyChanged('SelectedType')

    def AddType(self, footing_type):
        self._types.Add(footing_type)
        self.RaisePropertyChanged('LstTypes')

class MyWindow(Window):
    
    xaml_str = '''
    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Footings"
        Height="Auto" Width="460"
        SizeToContent="Height"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen">
        <Grid Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="5*"/>
                <RowDefinition Height="85*"/>
                <RowDefinition Height="5*"/>
                <RowDefinition Height="5*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50*"/>
                <ColumnDefinition Width="30*"/>
                <ColumnDefinition Width="20*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="1" Grid.ColumnSpan="2" Text="Caracteristiques des semelles" FontWeight="Bold" FontSize="11" />
            <DataGrid x:Name="Symbols" Grid.Row="1" Grid.RowSpan="2" AutoGenerateColumns="False" ItemsSource="{Binding}"
                    Background="Transparent" GridLinesVisibility="None">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Position" Binding="{Binding Position}" Width="*" />
                    <DataGridTemplateColumn Header="Type de Semelle" Width="2*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox x:Name="Types" 
                                          ItemsSource="{Binding LstTypes}"
                                          SelectedItem="{Binding SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                          DisplayMemberPath="Name">
                                </ComboBox>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid >
            <StackPanel Grid.Row="1" Grid.Column="1">
    
                <Label Content="Longueur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
                <DockPanel>
                    <TextBox x:Name="long_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                    <Label Content="cm"/>
                </DockPanel>
                <Label Content="Largeur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
                <DockPanel>
                    <TextBox x:Name="larg_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                    <Label Content="cm"/>
                </DockPanel>
                <Label Content="Epaisseur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
                <DockPanel>
                    <TextBox x:Name="ep_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                    <Label Content="cm"/>
                </DockPanel>
            </StackPanel>
    
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Vertical">
                <Button Content="Ajouter" Width="85" Click="Add_Click" VerticalAlignment="Top" Margin="0,30,0,10"/>
                <Button Content="Supprimer" Width="85"  VerticalAlignment="Top" Margin="0,20,0,10"/>
                <Button Content="Supprimer tous" Width="85"  VerticalAlignment="Top" Margin="0,20,0,10"/>
            </StackPanel>
            <StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2">
                <Label Content="Contrainte de Béton :" VerticalAlignment="Center"/>
                <DockPanel>
                    <TextBox x:Name="contrainte_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
                    <Label Content="MPA" />
                </DockPanel>
            </StackPanel>
            <StackPanel Grid.Row="3" Grid.ColumnSpan="3" Orientation="Horizontal" VerticalAlignment="Center" Width="210" Margin="0,5,0,0">
                <Button x:Name="Appliquer" Click="Appliquer_Click" Content="Appliquer" Width="100"/>
                <Button x:Name="Fermer" Click="Fermer_Click"  Content="Fermer" Width="100" Margin="10,0,0,0"/>
            </StackPanel>
    
        </Grid>
    </Window>
    '''

    def __init__(self):
        wpf.LoadComponent(self, StringReader(MyWindow.xaml_str))
        self.xData = ObservableCollection[Footings_VM]()
        symbols = self._grids_symbols()
        print(symbols)
        for s in symbols:
            foot = Footings_VM(s)
            self.xData.Add(foot)
        #self.Symbols.ItemsSource = self.Data
        self.DataContext = self.xData

    def _grids_symbols(self):
        Symbols = []
        grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids) \
            .WhereElementIsNotElementType().ToElements()
        for gd_a, gd_b in itertools.combinations(grids, 2):
            curveA = gd_a.Curve
            curveB = gd_b.Curve
            vectA = curveA.ComputeDerivatives(0.5, True).BasisX
            vectB = curveB.ComputeDerivatives(0.5, True).BasisX
            symA = gd_a.Name
            symB = gd_b.Name

            # check if lines are parallel
            if abs(vectA.CrossProduct(vectB).Z) < 0.01:
                # if true go to the next iteration
                continue
            results = clr.Reference[IntersectionResultArray]()
            result = curveA.Intersect(curveB, results)
            if result == SetComparisonResult.Overlap:

                if symA.isalpha() and symB.isdigit():
                    symbol = "{}-{}".format(symA, symB)
                elif symA.isdigit() and symB.isalpha():
                    symbol = "{}-{}".format(symB, symA)
                else:
                    continue

                Symbols.append(symbol)

        return sorted(Symbols)

    def Add_Click(self, sender, e):
        try:
            # Get values from the text boxes
            long_value = float(self.long_value.Text)
            larg_value = float(self.larg_value.Text)
            ep_value = float(self.ep_value.Text)

            # Create a new FootingType instance
            foot = FootingType(long_value, larg_value, ep_value)

            # Add the FootingType instance to all Footings_VM instances in the Data collection
            for vm in self.xData:
                vm.AddType(foot)

            print("Footing Added: {}".format(foot.Name))  # Debugging
            self.long_value.Clear()
            self.larg_value.Clear()
            self.ep_value.Clear()
        except Exception as ex:
            print(traceback.format_exc())

    def Appliquer_Click(self, sender, e):

        selected_foot = [[vm.Position, vm.SelectedType.Name] for vm in self.xData if vm.SelectedType.Name != "??"]
            
        if selected_foot:
            print(f"Selected Footings: {selected_foot}")
        else:
            print("No footings selected.")
        self.Close()

    def Fermer_Click(self, sender, e):
        self.Close()

foundation = MyWindow()
foundation.ShowDialog()
3 Likes

@c.poupin

Sorry for the late feedback.

As usual, you have the magic trick that reveals the issue! If I had spent hours on this, I wouldn’t have had the intuition to use a dummy object.
The issue is now resolved by implementing a dummy object, just as you suggested.

Now, I’m wondering if I can use WPF’s RelayCommand to handle the Ajouter, Supprimer, and Supprimer tous button actions to improve the code. What do you think?

I would only implement the clear method, avoiding deleting the dummy object

you’ll have to reset the comboboxes to their default values

Note
It might be a good exercise to port this code to PythonNet3

1 Like