diff --git a/OpenUtau.Core/Commands/ExpCommands.cs b/OpenUtau.Core/Commands/ExpCommands.cs
index fd8b09635..100b5dff4 100644
--- a/OpenUtau.Core/Commands/ExpCommands.cs
+++ b/OpenUtau.Core/Commands/ExpCommands.cs
@@ -21,10 +21,20 @@ public ExpCommand(UVoicePart part) {
     }
 
     public class SetNoteExpressionCommand : ExpCommand {
+        static readonly HashSet<string> needsPhonemizer = new HashSet<string> {
+            Format.Ustx.ALT, Format.Ustx.CLR, Format.Ustx.SHFT, Format.Ustx.VEL
+        };
+
         public readonly UProject project;
         public readonly UTrack track;
         public readonly float?[] newValue;
         public readonly float?[] oldValue;
+        public override ValidateOptions ValidateOptions
+            => new ValidateOptions {
+                SkipTiming = true,
+                Part = Part,
+                SkipPhonemizer = !needsPhonemizer.Contains(Key),
+            };
         public SetNoteExpressionCommand(UProject project, UTrack track, UVoicePart part, UNote note, string abbr, float?[] values) : base(part) {
             this.project = project;
             this.track = track;
@@ -39,11 +49,21 @@ public SetNoteExpressionCommand(UProject project, UTrack track, UVoicePart part,
     }
 
     public class SetNotesSameExpressionCommand : ExpCommand {
+        static readonly HashSet<string> needsPhonemizer = new HashSet<string> {
+            Format.Ustx.ALT, Format.Ustx.CLR, Format.Ustx.SHFT, Format.Ustx.VEL
+        };
+
         public readonly UProject project;
         public readonly UTrack track;
         public readonly UNote[] notes;
         public readonly float? newValue;
         public readonly float?[][] oldValue;
+        public override ValidateOptions ValidateOptions
+            => new ValidateOptions {
+                SkipTiming = true,
+                Part = Part,
+                SkipPhonemizer = !needsPhonemizer.Contains(Key),
+            };
         public SetNotesSameExpressionCommand(UProject project, UTrack track, UVoicePart part, IEnumerable<UNote> notes, string abbr, float? value) : base(part) {
             this.project = project;
             this.track = track;
diff --git a/OpenUtau.Core/Ustx/UNote.cs b/OpenUtau.Core/Ustx/UNote.cs
index b900c52f7..7f7ff3245 100644
--- a/OpenUtau.Core/Ustx/UNote.cs
+++ b/OpenUtau.Core/Ustx/UNote.cs
@@ -220,6 +220,8 @@ public void SetExpression(UProject project, UTrack track, string abbr, float?[]
                 return;
             }
 
+            phonemeExpressions.RemoveAll(exp => exp.descriptor?.abbr == abbr);
+
             int indexes = phonemeIndexes.LastOrDefault() + 1;
             for (int i = 0; i < indexes; i++) {
                 if (i == 0 || phonemeIndexes.Contains(i)) {
@@ -229,21 +231,14 @@ public void SetExpression(UProject project, UTrack track, string abbr, float?[]
                     } else {
                         value = values.Last();
                     }
-
                     if (value == null) {
-                        phonemeExpressions.RemoveAll(exp => exp.descriptor?.abbr == abbr && exp.index == i);
                         continue;
                     }
-                    var phonemeExp = phonemeExpressions.FirstOrDefault(exp => exp.descriptor?.abbr == abbr && exp.index == i);
-                    if (phonemeExp != null) {
-                        phonemeExp.descriptor = trackExp.descriptor;
-                        phonemeExp.value = (float)value;
-                    } else {
-                        phonemeExpressions.Add(new UExpression(trackExp.descriptor) {
-                            index = i,
-                            value = (float)value,
-                        });
-                    }
+
+                    phonemeExpressions.Add(new UExpression(trackExp.descriptor) {
+                        index = i,
+                        value = (float)value,
+                    });
                 }
             }
         }
diff --git a/OpenUtau.Core/Ustx/UPhoneme.cs b/OpenUtau.Core/Ustx/UPhoneme.cs
index 6c46e5016..0d98d4ab0 100644
--- a/OpenUtau.Core/Ustx/UPhoneme.cs
+++ b/OpenUtau.Core/Ustx/UPhoneme.cs
@@ -190,7 +190,7 @@ public void SetExpression(UProject project, UTrack track, string abbr, float? va
             }
             var note = Parent.Extends ?? Parent;
             if (value == null) {
-                note.phonemeExpressions.RemoveAll(exp => exp.descriptor?.abbr == abbr && exp.index == index);
+                note.phonemeExpressions.RemoveAll(exp => exp.descriptor?.abbr == abbr && exp.index == index || (exp.index != null && !note.phonemeIndexes.Contains((int)exp.index)));
             } else {
                 var phonemeExp = note.phonemeExpressions.FirstOrDefault(exp => exp.descriptor?.abbr == abbr && exp.index == index);
                 if (phonemeExp != null) {
diff --git a/OpenUtau/Controls/NotePropertyExpression.axaml b/OpenUtau/Controls/NotePropertyExpression.axaml
index 7161fada8..0d702dd6d 100644
--- a/OpenUtau/Controls/NotePropertyExpression.axaml
+++ b/OpenUtau/Controls/NotePropertyExpression.axaml
@@ -13,6 +13,7 @@
             Name="slider"/>
     <ComboBox Grid.Column="1" Grid.ColumnSpan="4" ItemsSource="{Binding Options}"
               SelectedIndex="{Binding SelectedOption}" MinWidth="120" IsVisible="{Binding IsOptions}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
-              IsDropDownOpen="{Binding DropDownOpen, Mode=OneWayToSource}"/>
+              IsDropDownOpen="{Binding DropDownOpen, Mode=OneWayToSource}"
+              Name="comboBox"/>
   </Grid>
 </UserControl>
diff --git a/OpenUtau/Controls/NotePropertyExpression.axaml.cs b/OpenUtau/Controls/NotePropertyExpression.axaml.cs
index 35af94f6a..c8d3c379d 100644
--- a/OpenUtau/Controls/NotePropertyExpression.axaml.cs
+++ b/OpenUtau/Controls/NotePropertyExpression.axaml.cs
@@ -14,48 +14,39 @@ public NotePropertyExpression() {
             slider.AddHandler(PointerPressedEvent, SliderPointerPressed, RoutingStrategies.Tunnel);
             slider.AddHandler(PointerReleasedEvent, SliderPointerReleased, RoutingStrategies.Tunnel);
             slider.AddHandler(PointerMovedEvent, SliderPointerMoved, RoutingStrategies.Tunnel);
+            comboBox.AddHandler(PointerPressedEvent, OnComboBoxPointerPressed, RoutingStrategies.Tunnel);
         }
 
+        // textbox
         private string textBoxValue = string.Empty;
         void OnTextBoxGotFocus(object? sender, GotFocusEventArgs args) {
             Log.Debug("Note property textbox got focus");
-            if (sender is TextBox text) {
-                textBoxValue = text.Text ?? string.Empty;
+            if (sender is TextBox textBox) {
+                textBoxValue = textBox.Text ?? string.Empty;
             }
         }
         void OnTextBoxLostFocus(object? sender, RoutedEventArgs args) {
             Log.Debug("Note property textbox lost focus");
             if (sender is TextBox textBox && textBoxValue != textBox.Text) {
-                if (DataContext is NotePropertyExpViewModel ViewModel) {
-                    DocManager.Inst.StartUndoGroup();
-                    NotePropertiesViewModel.PanelControlPressed = true;
-                    ViewModel.SetNumericalExpressions(textBox.Text);
-                    NotePropertiesViewModel.PanelControlPressed = false;
-                    DocManager.Inst.EndUndoGroup();
-                }
+                SetNumericalExpressions(textBox.Text);
             }
         }
 
+        // slider
         void SliderPointerPressed(object? sender, PointerPressedEventArgs args) {
-            Log.Debug("Slider pressed");
+            Log.Debug("Note property slider pressed");
             if (sender is Control control) {
                 var point = args.GetCurrentPoint(control);
                 if (point.Properties.IsLeftButtonPressed) {
                     DocManager.Inst.StartUndoGroup();
                     NotePropertiesViewModel.PanelControlPressed = true;
                 } else if (point.Properties.IsRightButtonPressed) {
-                    if (DataContext is NotePropertyExpViewModel ViewModel) {
-                        DocManager.Inst.StartUndoGroup();
-                        NotePropertiesViewModel.PanelControlPressed = true;
-                        ViewModel.SetNumericalExpressions(null);
-                        NotePropertiesViewModel.PanelControlPressed = false;
-                        DocManager.Inst.EndUndoGroup();
-                    }
+                    SetNumericalExpressions(null);
                 }
             }
         }
         void SliderPointerReleased(object? sender, PointerReleasedEventArgs args) {
-            Log.Debug("Slider released");
+            Log.Debug("Note property slider released");
             if (NotePropertiesViewModel.PanelControlPressed) {
                 if (sender is Slider slider && DataContext is NotePropertyExpViewModel ViewModel) {
                     ViewModel.SetNumericalExpressions((float)slider.Value);
@@ -69,5 +60,26 @@ void SliderPointerMoved(object? sender, PointerEventArgs args) {
                 ViewModel.SetNumericalExpressions((float)slider.Value);
             }
         }
+
+        void OnComboBoxPointerPressed(object? sender, PointerPressedEventArgs args) {
+            Log.Debug("Note property textbox pressed");
+            if (sender is ComboBox comboBox) {
+                var point = args.GetCurrentPoint(comboBox);
+                if (point.Properties.IsRightButtonPressed) {
+                    SetNumericalExpressions(null);
+                    args.Handled = true;
+                }
+            }
+        }
+
+        private void SetNumericalExpressions(string? expression) {
+            if (DataContext is NotePropertyExpViewModel viewModel) {
+                DocManager.Inst.StartUndoGroup();
+                NotePropertiesViewModel.PanelControlPressed = true;
+                viewModel.SetNumericalExpressions(expression);
+                NotePropertiesViewModel.PanelControlPressed = false;
+                DocManager.Inst.EndUndoGroup();
+            }
+        }
     }
 }
diff --git a/OpenUtau/ViewModels/NotePropertiesViewModel.cs b/OpenUtau/ViewModels/NotePropertiesViewModel.cs
index 6306394c5..c46ad3431 100644
--- a/OpenUtau/ViewModels/NotePropertiesViewModel.cs
+++ b/OpenUtau/ViewModels/NotePropertiesViewModel.cs
@@ -189,7 +189,7 @@ private void AttachExpressions() {
 
                     foreach (NotePropertyExpViewModel exp in Expressions) {
                         exp.IsNoteSelected = true;
-                        var phonemeExpression = note.phonemeExpressions.FirstOrDefault(e => e.abbr == exp.abbr);
+                        var phonemeExpression = note.phonemeExpressions.FirstOrDefault(e => e.abbr == exp.abbr && e.index == 0);
                         if (phonemeExpression != null) {
                             if (exp.IsNumerical) {
                                 exp.Value = phonemeExpression.value;
@@ -470,12 +470,18 @@ public void SetVibratoEnable() {
         public void SetNumericalExpressionsChanges(string abbr, float? value) {
             if (AllowNoteEdit && Part != null && selectedNotes.Count > 0) {
                 var track = DocManager.Inst.Project.tracks[Part.trackNo];
+                if (track.TryGetExpression(DocManager.Inst.Project, abbr, out UExpression expression) && expression.value == value) {
+                    value = null;
+                }
                 DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, abbr, value));
             }
         }
         public void SetOptionalExpressionsChanges(string abbr, int? value) {
             if (!NoteLoading && Part != null && selectedNotes.Count > 0) {
                 var track = DocManager.Inst.Project.tracks[Part.trackNo];
+                if (track.TryGetExpression(DocManager.Inst.Project, abbr, out UExpression expression) && expression.value == value) {
+                    value = null;
+                }
                 DocManager.Inst.StartUndoGroup();
                 DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, abbr, value));
                 DocManager.Inst.EndUndoGroup();
