diff --git a/Assembly/Assembly.vb b/Assembly/Assembly.vb index d244641..ce4c9ad 100644 --- a/Assembly/Assembly.vb +++ b/Assembly/Assembly.vb @@ -916,6 +916,14 @@ Public Class Assembly #Region "Lettura Assemblato" + Public Enum ResultReadingAssembInfo + MissingAssembInfo + ErrorInAssembInfo + CorrectAssembInfo + End Enum + + Public bOk As ResultReadingAssembInfo = ResultReadingAssembInfo.CorrectAssembInfo + Private FileContent() As String Shared Sub ReadDDFAssembly(sPathDDF As String, ByRef ReadAssembly As Assembly) BuiltReffCompo = False @@ -934,9 +942,9 @@ Public Class Assembly ' leggo riga per riga Dim IndexLine As Integer = 0 Map.refAssemblyManagerVM.LoadAssemblyInformation(ReadAssembly) - Dim bOk As Boolean = ReadAssembly.GetGeneralAssembly(IndexLine, sErrorInfo) + ReadAssembly.bOk = ReadAssembly.GetGeneralAssembly(IndexLine, sErrorInfo) '--------------------------------------------------------------------------------------------------------------------------------- - If bOk Then + If ReadAssembly.bOk <> ResultReadingAssembInfo.ErrorInAssembInfo Then If IndexLine = -1 Then ' 50163= Error in reading General Assembly in DDF sErrorInfo += String.Format(EgtMsg(50102), EgtMsg(50163) + vbCrLf) @@ -945,12 +953,15 @@ Public Class Assembly Map.refMainWindowVM.SelectedPage = MainWindowVM.ListPageEnum.nNothingSelected Else If ReadAssembly.GetDDFDoor(IndexLine, sErrorInfo) = -1 Then + ' errore nella lettura di un part: blocco tutto ReadAssembly = Nothing Map.refAssemblyManagerVM.CurrProject.SelAssemblyName.IsModified = False Map.refMainWindowVM.SelectedPage = MainWindowVM.ListPageEnum.nNothingSelected End If End If Else + ' 50163= Error in reading General Assembly in DDF + sErrorInfo += String.Format(EgtMsg(50102), EgtMsg(50163) + vbCrLf) ReadAssembly = Nothing Map.refAssemblyManagerVM.CurrProject.SelAssemblyName.IsModified = False Map.refMainWindowVM.SelectedPage = MainWindowVM.ListPageEnum.nNothingSelected @@ -962,7 +973,7 @@ Public Class Assembly End If End Sub - Private Function GetGeneralAssembly(ByRef IndexLine As Integer, sErrorInfo As String) As Boolean + Private Function GetGeneralAssembly(ByRef IndexLine As Integer, sErrorInfo As String) As ResultReadingAssembInfo IndexLine = SkipWhiteSpace(IndexLine) ' Size If IndexLine < FileContent.Count - 1 Then @@ -970,11 +981,11 @@ Public Class Assembly IndexLine = GetSize(IndexLine + 1) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.S_SIZE_INI + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If End If End If - If IndexLine = 0 Then Return True + If IndexLine = 0 Then Return ResultReadingAssembInfo.MissingAssembInfo IndexLine = SkipWhiteSpace(IndexLine) ' Light If IndexLine < FileContent.Count - 1 Then @@ -982,7 +993,7 @@ Public Class Assembly IndexLine = GetLight(IndexLine + 1) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.S_LIGHT + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If End If End If @@ -993,7 +1004,7 @@ Public Class Assembly IndexLine = GetOverlap(IndexLine + 1) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.S_OVERLAP + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If End If End If @@ -1004,7 +1015,7 @@ Public Class Assembly IndexLine = GetExterior(IndexLine + 1) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.K_EXTERIOR + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If End If End If @@ -1015,7 +1026,7 @@ Public Class Assembly IndexLine = GetProfiles(IndexLine + 1) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.S_PROFILES + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If End If End If @@ -1028,7 +1039,7 @@ Public Class Assembly SetDoorNumber(m_DoorNumber) If IndexLine = -1 Then sErrorInfo += String.Format(EgtMsg(50102), ConstIni.S_DOORS_ASSEMBY + vbCrLf) - Return False + Return ResultReadingAssembInfo.ErrorInAssembInfo End If ' se sono arrivato fino a qui significa che ormai è terminata la pagina If IsNothing(m_Exterior) OrElse Not m_Exterior Then @@ -1037,7 +1048,7 @@ Public Class Assembly End If End If End If - Return True + Return ResultReadingAssembInfo.CorrectAssembInfo End Function ' salta le righe che non contengono un metadata @@ -1270,17 +1281,17 @@ Public Class Assembly Local_PartDoor.Door.SwingTypeList = m_SwingTypeListSinlgeDoor Local_PartDoor.SetIsChecked(Local_Door.IsActive) ListPartDoor.Add(Local_PartDoor) - 'SetArrayPartDoor(Local_PartDoor, 0) Case ConstGen.PART_DO_ & "2" Local_PartDoor.Door = Local_Door - Local_PartDoor.Door.SwingTypeList = m_SwingTypeListRightDoor If Not IsNothing(Map.refAssemblyManagerVM.ReserchPartDoor("DO_1")) Then - 'GetArrayPartDoor(0).Door.SwingTypeList = m_SwingTypeListLeftDoor + Local_PartDoor.Door.SwingTypeList = m_SwingTypeListRightDoor Map.refAssemblyManagerVM.ReserchPartDoor("DO_1").Door.SwingTypeList = m_SwingTypeListLeftDoor + Else + Local_PartDoor.Door.SwingTypeList = m_SwingTypeListSinlgeDoor End If Local_PartDoor.SetIsChecked(Local_Door.IsActive) ListPartDoor.Add(Local_PartDoor) - 'SetArrayPartDoor(Local_PartDoor, 1) + Case ConstGen.PART_DO_ & "3" Local_PartDoor.Door = Local_Door Local_PartDoor.SetIsChecked(Local_Door.IsActive) @@ -1325,23 +1336,208 @@ Public Class Assembly End Select End While - If ListPartDoorOfDoor.Count < 1 Then - Map.refSceneManagerVM.RefreshBtn() - EgtZoom(ZM.ALL) - sErrorInfo = "The current ddf does not contain a door!" + ' controllo che gli oggetti che sono stati caricati + If Not ControlAssemblyInformation() Then Return -1 Else - If Not String.IsNullOrEmpty(sErrorInfo) Then - Part.FirstReadingEdge = True + Map.refAssemblyManagerVM.CurrProject.SelAssemblyName.IsModified = False + End If + ' scrivo in locale un a copia dei riferiemti ai part + SetPartDoorCurrAssembly() + If Not OptionModule.m_ConfigurationSoftware = ConfigType.Door Then + If Not ControlListDoor() Then + Return -1 + End If + If Not ControlSwing() Then + Return -1 End If End If BuiltReffCompo = True - SetPartDoorCurrAssembly() Map.refAssemblyManagerVM.CreateAssociation(Me) MatchCompo() Return 1 End Function + ' verifico che la dicitaura delle porte sia coerente + Private Function ControlListDoor() As Boolean + Dim WritingError As String = String.Empty + Select Case ListPartDoorOfDoor.Count + Case 1 + ' se ho una porta soltanto deve essere la prima + If IsNothing(m_Door1) Then + WritingError = EgtMsg(50506) + MessageBox.Show(WritingError, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + Else + Return True + End If + Case 2 + If ListPartDoorOfDoor.Count < 2 Then Return False + If IsNothing(m_Door1) OrElse IsNothing(m_Door2) Then + ' 50513=Impossible to find the first door (DO_1) or the second door (DO_2)! + WritingError = EgtMsg(50513) + MessageBox.Show(WritingError, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + If Not ListPartDoorOfDoor(0).Door.TypePart = "DO_1" Then + ' 50515=The first door is not realy the first, check the ddf. + WritingError = EgtMsg(50515) + MessageBox.Show(WritingError, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + Return True + End Select + Return False + End Function + + ' controllo che gli swing siano coerenti: + Private Function ControlSwing() As Boolean + Dim ErrorSwingMessages As String = String.Empty + Select Case ListPartDoorOfDoor.Count + Case 1 + Dim Local_Door1 As Part = ListPartDoorOfDoor(0).Door + Dim Local_Swing As String = Local_Door1.Swing + Local_Door1.SwingTypeList = OptionModule.m_SwingTypeListSinlgeDoor + If String.IsNullOrEmpty(Local_Swing) Then Return False + Dim Index As Integer = Local_Door1.SwingTypeList.FindIndex(Function(sValue) Trim(sValue) = Trim(Local_Swing)) + ' se Index = -1 significa che non è stata trovata nessuna corrispondenza + If Index < 0 Then + ' 50507=The swing is not a member of the swing list of single door. + ErrorSwingMessages = EgtMsg(50507) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + Local_Door1.Swing = Local_Door1.SwingTypeList(Index) + Return True + Case 2 + Dim Index_1 As Integer = -1 + Dim Index_2 As Integer = -1 + If Not IsNothing(m_Door1) Then + Index_1 = OptionModule.m_SwingTypeListLeftDoor.FindIndex(Function(sValue) Trim(sValue) = Trim(m_Door1.Swing)) + End If + If Not IsNothing(m_Door2) Then + Index_2 = OptionModule.m_SwingTypeListRightDoor.FindIndex(Function(sValue) Trim(sValue) = Trim(m_Door2.Swing)) + End If + ' Se uno swing è corretto e l'altro è sbagliato + If (Index_1 < 0 AndAlso Index_2 > -1) Then + ' 50508=The swing of left door is not a member of the swing list of left door. + ErrorSwingMessages = EgtMsg(50508) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + ElseIf (Index_1 > -1 AndAlso Index_2 < 0) Then + ' 50509=The swing of right door is not a member of the swing list of right door. + ErrorSwingMessages = EgtMsg(50509) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + ' se sono entrambi sbagliati provo a verificare che non siano invertite le ante + If Index_1 < 0 OrElse Index_2 < 0 Then + If Not IsNothing(m_Door2) Then + Index_1 = OptionModule.m_SwingTypeListLeftDoor.FindIndex(Function(sValue) Trim(sValue) = Trim(m_Door2.Swing)) + End If + If Not IsNothing(m_Door1) Then + Index_2 = OptionModule.m_SwingTypeListRightDoor.FindIndex(Function(sValue) Trim(sValue) = Trim(m_Door1.Swing)) + End If + ' verifico i risultati ottenuti + If (Index_1 > -1 AndAlso Index_2 > -1) Then + ' 50510=The swing of left door is on the right door and viceverse. + ErrorSwingMessages = EgtMsg(50510) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + ElseIf (Index_1 < 0 AndAlso Index_2 > -1) Then + ' 50511=The swing of left door is not a member of the swing list of left door. + ErrorSwingMessages = EgtMsg(50511) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + ElseIf (Index_1 > -1 AndAlso Index_2 < 0) Then + ' 50512=The swing of right door is not a member of the swing list of right door. + ErrorSwingMessages = EgtMsg(50512) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + Else + ' 50514=The swing are absolutly wrong! + ErrorSwingMessages = EgtMsg(50514) + MessageBox.Show(ErrorSwingMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + End If + Return True + End Select + Return False + End Function + + ' controllo degli oggetti caricati nell'assemblato + Private Function ControlAssemblyInformation() As Boolean + Dim ErrorMessages As String = String.Empty + Select Case bOk + Case ResultReadingAssembInfo.CorrectAssembInfo + If OptionModule.m_ConfigurationSoftware = ConfigType.Door Then + ' 50520=The current ddf is an assembly + ErrorMessages = EgtMsg(50520) + MessageBox.Show(ErrorMessages, EgtMsg(50144), MessageBoxButton.OK, MessageBoxImage.Warning) + Return True + End If + ' verifico se è un assemblato + If ListPartDoor.Count < 4 And ListPartDoorOfDoor.Count > 0 Then + ' 50516=The ddf does not contains jambs! + ErrorMessages = EgtMsg(50516) + Dim sVal As String = Utility.DoubleToString(ListPartDoor.Count, 0) + MessageBox.Show(String.Format(ErrorMessages, sVal), EgtMsg(50144), MessageBoxButton.OK, MessageBoxImage.Warning) + Return True + End If + If ListPartDoor.Count < 4 And ListPartDoorOfDoor.Count = 0 Then + ' 50517=The current ddf does not contains doors! + ErrorMessages = EgtMsg(50517) + MessageBox.Show(ErrorMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + Case ResultReadingAssembInfo.MissingAssembInfo + ' verifico se è un assemblato + If ListPartDoor.Count < 4 And ListPartDoorOfDoor.Count > 0 Then + If OptionModule.m_ConfigurationSoftware = ConfigType.Door Then Return True + ' 50518=The current ddf is not an assembly. + ErrorMessages = EgtMsg(50518) + MessageBox.Show(ErrorMessages, EgtMsg(50144), MessageBoxButton.OK, MessageBoxImage.Warning) + Return True + End If + ' verifico che se è una porta singola oppure un assemblato vecchio + If ListPartDoor.Count < 4 And ListPartDoorOfDoor.Count = 0 Then + ' 50519=Impossible to open a ddf with no doors. + ErrorMessages = EgtMsg(50519) + MessageBox.Show(ErrorMessages, EgtMsg(50101), MessageBoxButton.OK, MessageBoxImage.Error) + Return False + End If + If OptionModule.m_ConfigurationSoftware = ConfigType.Door AndAlso ListPartDoor.Count > 4 Then + ' 50520=The current ddf is an assembly + ErrorMessages = EgtMsg(50520) + MessageBox.Show(ErrorMessages, EgtMsg(50144), MessageBoxButton.OK, MessageBoxImage.Warning) + Return True + End If + Case Else + Return False + End Select + Return True + End Function + + Public Function ControlAssembly(Optional ByVal bSaving As Boolean = False) As String + ' se è una porta singola oppure le info generali non sono state caricate correttamente non controllo l'assemblato + If bSaving Then + If bOk <> ResultReadingAssembInfo.MissingAssembInfo Then Return String.Empty + Else + If bOk = ResultReadingAssembInfo.ErrorInAssembInfo OrElse bOk = ResultReadingAssembInfo.MissingAssembInfo Then Return String.Empty + End If + Dim WritingError As String = String.Empty + Dim DoorTypeList As String = String.Empty + If ListPartDoor.Count < 3 Then + For Each ItemPart In ListPartDoor + DoorTypeList &= " ," & ItemPart.Door.TypePart + Next + DoorTypeList = DoorTypeList.Remove(1, 1) + WritingError = String.Format(EgtMsg(50505), DoorTypeList) + End If + Return WritingError + End Function + #Region "Associazione delle compo lette da ddf" Public Sub MatchCompo() @@ -1654,8 +1850,18 @@ Public Class Assembly m_Door2 = ListPartDoor(IndexPartDoor).Door End If Next + NotifyPropertyChanged("JambExists") End Sub + Public ReadOnly Property JambExists As Boolean + Get + If Not IsNothing(m_JambL) AndAlso Not IsNothing(m_JambR) AndAlso Not IsNothing(m_JambT) Then + Return True + End If + Return False + End Get + End Property + ' gestisce la chimata delle funzioni a seconda della modifica eseguita #Region "DIMENSIONING" @@ -1743,6 +1949,23 @@ Public Class Assembly #Region "UpDateRefCompo" + Public Sub CreateAssembly() + If ListPartDoorOfDoor.Count < 1 Then Return + 'If Not JambExists Then SetDimension("JambIsCheked") + Select Case ListPartDoorOfDoor.Count + Case 1 + Dim IndexCompo As Integer = 0 + Dim EndIndex As Integer = m_Door1.CompoList.Count + For IndexCompo = 0 To EndIndex - 1 + CreateCompoOnJamb(m_Door1.CompoList(IndexCompo)) + Next + Case 2 + + Case Else + + End Select + End Sub + ' è chiamata ogni volta che viene modificato un parametro del general dell'assemblato Public Sub UpDateAllCompo() Dim IdexDoorNumber As Integer = 0 @@ -2212,9 +2435,9 @@ Public Class Assembly If Exterior Then ' se di tipo LeftHand - If m_JambL.SwingAlias.Name.Contains("LH") Then - If Not IsNothing(m_JambL) Then m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.HINGE & ConstGen.EXTERIOR - If Not IsNothing(m_JambR) Then m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.LOCK & ConstGen.EXTERIOR + If Not IsNothing(m_JambL) AndAlso Not IsNothing(m_JambR) AndAlso m_JambL.SwingAlias.Name.Contains("LH") Then + m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.HINGE & ConstGen.EXTERIOR + m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.LOCK & ConstGen.EXTERIOR Else If Not IsNothing(m_JambL) Then m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.LOCK & ConstGen.EXTERIOR If Not IsNothing(m_JambR) Then m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.HINGE & ConstGen.EXTERIOR @@ -2222,9 +2445,9 @@ Public Class Assembly If Not IsNothing(m_JambB) Then m_JambB.TypePart = ConstGen.PART_FRAME_BOTTOM & ConstGen.BOTTOM & ConstGen.EXTERIOR If Not IsNothing(m_JambT) Then m_JambT.TypePart = ConstGen.PART_FRAME_TOP & ConstGen.TOP & ConstGen.EXTERIOR Else - If m_JambL.SwingAlias.Name.Contains("LH") Then - If Not IsNothing(m_JambL) Then m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.HINGE - If Not IsNothing(m_JambR) Then m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.LOCK + If Not IsNothing(m_JambL) AndAlso Not IsNothing(m_JambR) AndAlso m_JambL.SwingAlias.Name.Contains("LH") Then + m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.HINGE + m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.LOCK Else If Not IsNothing(m_JambL) Then m_JambL.TypePart = ConstGen.PART_FRAME_LEFT & ConstGen.LOCK If Not IsNothing(m_JambR) Then m_JambR.TypePart = ConstGen.PART_FRAME_RIGHT & ConstGen.HINGE @@ -2309,7 +2532,7 @@ Public Class Assembly Next End Sub - ' iverte la prima anta con la seconda anta + ' inverte la prima anta con la seconda anta Public Sub ReverseDoor() If IsNothing(m_Door1) OrElse IsNothing(m_Door2) Then Return If Not m_Door1.SwingAlias.Name.Contains("I") Then Return diff --git a/Assembly/AssemblyPageV.xaml b/Assembly/AssemblyPageV.xaml index 3354b24..19fcfd3 100644 --- a/Assembly/AssemblyPageV.xaml +++ b/Assembly/AssemblyPageV.xaml @@ -34,7 +34,8 @@ - + @@ -140,7 +141,8 @@ - + @@ -249,7 +251,8 @@ - + - + + Margin="4" VerticalAlignment="Stretch" + IsEnabled="{Binding CurrAssembly.JambExists}"/> + - - + + diff --git a/ProjectManager/ProjectManagerVM.vb b/ProjectManager/ProjectManagerVM.vb index 8df3743..f9afc30 100644 --- a/ProjectManager/ProjectManagerVM.vb +++ b/ProjectManager/ProjectManagerVM.vb @@ -72,6 +72,7 @@ Public Class ProjectManagerVM Private m_cmdDuplica As ICommand Private m_cmdHardware As ICommand Private m_cmdRefreshDir As ICommand + Private m_cmdCreateAssembly As ICommand #Region "ToolTip" @@ -170,6 +171,14 @@ Public Class ProjectManagerVM Return EgtMsg(50413) End Get End Property + + Public ReadOnly Property CreateAssemblyToolTip As String + Get + ' Create Assembly + Return EgtMsg(50420) + End Get + End Property + #End Region ' ToolTip #End Region ' Fields & Properties @@ -1117,6 +1126,45 @@ Public Class ProjectManagerVM #End Region ' PrintCommand +#Region "CreateAssemblyCommand" + + Private m_VisibilityCreateAssembly As Visibility = Visibility.Collapsed + Public ReadOnly Property VisibilityCreateAssembly As Visibility + Get + If OptionModule.m_ConfigurationSoftware = ConfigType.Door Then Return Visibility.Collapsed + If Map.refMainWindowVM.SelectedPage = MainWindowVM.ListPageEnum.nDDFPage AndAlso + Not IsNothing(Map.refAssemblyManagerVM) AndAlso + Not IsNothing(Map.refAssemblyManagerVM.CurrProject) AndAlso + Not IsNothing(Map.refAssemblyManagerVM.CurrProject.SelAssemblyName) AndAlso + Not IsNothing(Map.refAssemblyManagerVM.CurrProject.SelAssemblyName.SelAssembly) AndAlso + Map.refAssemblyManagerVM.CurrProject.SelAssemblyName.SelAssembly.JambExists AndAlso + Not IsNothing(Map.refPartPageVM) AndAlso Not IsNothing(Map.refPartPageVM.CurrPart) AndAlso + Not IsNothing(Map.refPartPageVM.CurrPart.refPartDoor) AndAlso + Not IsNothing(Map.refPartPageVM.CurrPart.refPartDoor.Door) AndAlso + Map.refPartPageVM.CurrPart.refPartDoor.Door.TypePart.Contains("DO_") Then + m_VisibilityCreateAssembly = Visibility.Visible + Else + m_VisibilityCreateAssembly = Visibility.Collapsed + End If + Return m_VisibilityCreateAssembly + End Get + End Property + + Public ReadOnly Property CreateAssemblyCommand As ICommand + Get + If m_cmdCreateAssembly Is Nothing Then + m_cmdCreateAssembly = New Command(AddressOf CreateAssembly) + End If + Return m_cmdCreateAssembly + End Get + End Property + + Public Sub CreateAssembly() + Map.refAssemblyPageVM.CurrAssembly.CreateAssembly() + End Sub + +#End Region ' CreateAssemblyCommand + #End Region ' Commands Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged