diff --git a/Netch/Forms/BindingForm.cs b/Netch/Forms/BindingForm.cs index 37333e51..e4fbf0f2 100644 --- a/Netch/Forms/BindingForm.cs +++ b/Netch/Forms/BindingForm.cs @@ -5,6 +5,7 @@ namespace Netch.Forms; [Fody.ConfigureAwait(true)] public class BindingForm : Form { + // validation actions private readonly Dictionary> _checkActions = new(); private readonly Dictionary> _saveActions = new(); @@ -13,7 +14,7 @@ public class BindingForm : Form BindTextBox(control, check, save, value); } - protected void BindTextBox(TextBoxBase control, Func check, Action save, object value) + protected virtual void BindTextBox(TextBoxBase control, Func check, Action save, object value) { control.Text = value.ToString(); _checkActions.Add(control, @@ -21,7 +22,7 @@ public class BindingForm : Form { try { - return check.Invoke((T)Convert.ChangeType(s, typeof(T))); + return check((T)Convert.ChangeType(s, typeof(T))); } catch { @@ -29,7 +30,7 @@ public class BindingForm : Form } }); - _saveActions.Add(control, c => save.Invoke((T)Convert.ChangeType(((TextBoxBase)c).Text, typeof(T)))); + _saveActions.Add(control, c => save((T)Convert.ChangeType(((TextBoxBase)c).Text, typeof(T)))); } protected void BindCheckBox(CheckBox control, Action save, bool value) @@ -48,7 +49,7 @@ public class BindingForm : Form protected void BindRadioBox(RadioButton control, Action save, bool value) { control.Checked = value; - _saveActions.Add(control, c => save.Invoke(((RadioButton)c).Checked)); + _saveActions.Add(control, c => save(((RadioButton)c).Checked)); } protected void BindListComboBox(ComboBox comboBox, Action save, IEnumerable values, T value) where T : notnull @@ -62,7 +63,7 @@ public class BindingForm : Form comboBox.ValueMember = nameof(TagItem.Value); comboBox.DisplayMember = nameof(TagItem.Text); - _saveActions.Add(comboBox, c => save.Invoke(((TagItem)((ComboBox)c).SelectedItem).Value)); + _saveActions.Add(comboBox, c => save(((TagItem)((ComboBox)c).SelectedItem).Value)); Load += (_, _) => { comboBox.SelectedItem = tagItems.SingleOrDefault(t => t.Value.Equals(value)); }; } @@ -71,20 +72,28 @@ public class BindingForm : Form if (values != null) control.Items.AddRange(values); - _saveActions.Add(control, c => save.Invoke(((ComboBox)c).Text)); - _checkActions.Add(control, check.Invoke); + _checkActions.Add(control, check); + _saveActions.Add(control, c => save(((ComboBox)c).Text)); Load += (_, _) => { control.Text = value; }; } - protected List GetCheckFailedControls() + protected List GetInvalidateValueControls() { - return _checkActions.Where(pair => !pair.Value.Invoke(pair.Key.Text)).Select(pair => pair.Key).ToList(); + return _checkActions.Keys.Where(c => !Validate(c)).ToList(); + } + + protected bool Validate(Control c) + { + if (!_checkActions.ContainsKey(c)) + throw new ArgumentException(); + + return _checkActions[c](c.Text); } protected void SaveBinds() { foreach (var pair in _saveActions) - pair.Value.Invoke(pair.Key); + pair.Value(pair.Key); } } \ No newline at end of file diff --git a/Netch/Forms/SettingForm.Designer.cs b/Netch/Forms/SettingForm.Designer.cs index 7fd82605..84049753 100644 --- a/Netch/Forms/SettingForm.Designer.cs +++ b/Netch/Forms/SettingForm.Designer.cs @@ -31,6 +31,7 @@ namespace Netch.Forms /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.TabControl = new System.Windows.Forms.TabControl(); this.GeneralTabPage = new System.Windows.Forms.TabPage(); this.PortGroupBox = new System.Windows.Forms.GroupBox(); @@ -112,6 +113,7 @@ namespace Netch.Forms this.AioDNSListenPortTextBox = new System.Windows.Forms.TextBox(); this.ControlButton = new System.Windows.Forms.Button(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components); this.TabControl.SuspendLayout(); this.GeneralTabPage.SuspendLayout(); this.PortGroupBox.SuspendLayout(); @@ -123,6 +125,7 @@ namespace Netch.Forms this.OtherTabPage.SuspendLayout(); this.AioDNSTabPage.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit(); this.SuspendLayout(); // // TabControl @@ -476,7 +479,7 @@ namespace Netch.Forms this.WinTUNGroupBox.Controls.Add(this.ProxyDNSCheckBox); this.WinTUNGroupBox.Location = new System.Drawing.Point(6, 6); this.WinTUNGroupBox.Name = "WinTUNGroupBox"; - this.WinTUNGroupBox.Size = new System.Drawing.Size(420, 175); + this.WinTUNGroupBox.Size = new System.Drawing.Size(450, 175); this.WinTUNGroupBox.TabIndex = 0; this.WinTUNGroupBox.TabStop = false; // @@ -562,8 +565,8 @@ namespace Netch.Forms // // ProxyDNSCheckBox // - this.ProxyDNSCheckBox.DataBindings.Add((new System.Windows.Forms.Binding("Visible", UseCustomDNSCheckBox, "Checked", true))); this.ProxyDNSCheckBox.AutoSize = true; + this.ProxyDNSCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Visible", this.UseCustomDNSCheckBox, "Checked", true)); this.ProxyDNSCheckBox.Location = new System.Drawing.Point(175, 139); this.ProxyDNSCheckBox.Name = "ProxyDNSCheckBox"; this.ProxyDNSCheckBox.Size = new System.Drawing.Size(89, 21); @@ -642,7 +645,7 @@ namespace Netch.Forms this.KCPGroupBox.Controls.Add(this.congestionCheckBox); this.KCPGroupBox.Location = new System.Drawing.Point(9, 75); this.KCPGroupBox.Name = "KCPGroupBox"; - this.KCPGroupBox.Size = new System.Drawing.Size(427, 204); + this.KCPGroupBox.Size = new System.Drawing.Size(447, 204); this.KCPGroupBox.TabIndex = 3; this.KCPGroupBox.TabStop = false; this.KCPGroupBox.Text = "KCP"; @@ -967,6 +970,11 @@ namespace Netch.Forms this.flowLayoutPanel1.Size = new System.Drawing.Size(480, 400); this.flowLayoutPanel1.TabIndex = 0; // + // errorProvider + // + this.errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.errorProvider.ContainerControl = this; + // // SettingForm // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); @@ -1000,6 +1008,7 @@ namespace Netch.Forms this.AioDNSTabPage.ResumeLayout(false); this.AioDNSTabPage.PerformLayout(); this.flowLayoutPanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -1088,5 +1097,6 @@ namespace Netch.Forms private System.Windows.Forms.CheckBox FilterTCPCheckBox; private System.Windows.Forms.CheckBox FilterUDPCheckBox; private System.Windows.Forms.CheckBox DNSProxyCheckBox; + private ErrorProvider errorProvider; } } \ No newline at end of file diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs index acf90882..f9f15ff6 100644 --- a/Netch/Forms/SettingForm.cs +++ b/Netch/Forms/SettingForm.cs @@ -31,10 +31,7 @@ public partial class SettingForm : BindingForm i => Global.Settings.DetectionTick = i, Global.Settings.DetectionTick); - BindTextBox(StartedPingIntervalTextBox, - _ => true, - i => Global.Settings.StartedPingInterval = i, - Global.Settings.StartedPingInterval); + BindTextBox(StartedPingIntervalTextBox, _ => true, i => Global.Settings.StartedPingInterval = i, Global.Settings.StartedPingInterval); object[]? stuns; try @@ -88,11 +85,10 @@ public partial class SettingForm : BindingForm BindCheckBox(FilterDNSCheckBox, b => Global.Settings.Redirector.FilterDNS = b, Global.Settings.Redirector.FilterDNS); + // TODO validate Redirector AioDNS DNS BindTextBox(DNSHijackHostTextBox, s => true, s => Global.Settings.Redirector.DNSHost = s, Global.Settings.Redirector.DNSHost); - BindCheckBox(ChildProcessHandleCheckBox, - s => Global.Settings.Redirector.FilterParent = s, - Global.Settings.Redirector.FilterParent); + BindCheckBox(ChildProcessHandleCheckBox, s => Global.Settings.Redirector.FilterParent = s, Global.Settings.Redirector.FilterParent); BindCheckBox(DNSProxyCheckBox, b => Global.Settings.Redirector.DNSProxy = b, Global.Settings.Redirector.DNSProxy); @@ -102,20 +98,11 @@ public partial class SettingForm : BindingForm #region TUN/TAP - BindTextBox(TUNTAPAddressTextBox, - s => IPAddress.TryParse(s, out _), - s => Global.Settings.TUNTAP.Address = s, - Global.Settings.TUNTAP.Address); + BindTextBox(TUNTAPAddressTextBox, s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Address = s, Global.Settings.TUNTAP.Address); - BindTextBox(TUNTAPNetmaskTextBox, - s => IPAddress.TryParse(s, out _), - s => Global.Settings.TUNTAP.Netmask = s, - Global.Settings.TUNTAP.Netmask); + BindTextBox(TUNTAPNetmaskTextBox, s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Netmask = s, Global.Settings.TUNTAP.Netmask); - BindTextBox(TUNTAPGatewayTextBox, - s => IPAddress.TryParse(s, out _), - s => Global.Settings.TUNTAP.Gateway = s, - Global.Settings.TUNTAP.Gateway); + BindTextBox(TUNTAPGatewayTextBox, s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Gateway = s, Global.Settings.TUNTAP.Gateway); BindCheckBox(UseCustomDNSCheckBox, b => { Global.Settings.TUNTAP.UseCustomDNS = b; }, Global.Settings.TUNTAP.UseCustomDNS); @@ -161,9 +148,7 @@ public partial class SettingForm : BindingForm i => Global.Settings.V2RayConfig.KcpConfig.writeBufferSize = i, Global.Settings.V2RayConfig.KcpConfig.writeBufferSize); - BindCheckBox(congestionCheckBox, - b => Global.Settings.V2RayConfig.KcpConfig.congestion = b, - Global.Settings.V2RayConfig.KcpConfig.congestion); + BindCheckBox(congestionCheckBox, b => Global.Settings.V2RayConfig.KcpConfig.congestion = b, Global.Settings.V2RayConfig.KcpConfig.congestion); #endregion @@ -208,6 +193,39 @@ public partial class SettingForm : BindingForm TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null); } + protected override void BindTextBox(TextBoxBase control, Func check, Action save, object value) + { + base.BindTextBox(control, check, save, value); + control.TextChanged += (_, _) => + { + if (Validate(control)) + { + errorProvider.SetError(control, null); + } + else + { + errorProvider.SetError(control, i18N.Translate("Invalid value")); + } + }; + } + + protected new void BindComboBox(ComboBox control, Func check, Action save, string value, object[]? values = null) + { + base.BindComboBox(control, check, save, value, values); + + control.TextChanged += (_, _) => + { + if (Validate(control)) + { + errorProvider.SetError(control, null); + } + else + { + errorProvider.SetError(control, i18N.Translate("Invalid value")); + } + }; + } + private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object? sender, EventArgs? e) { if (UseCustomDNSCheckBox.Checked) @@ -229,12 +247,27 @@ public partial class SettingForm : BindingForm #region Check - var checkNotPassControl = GetCheckFailedControls(); - foreach (Control control in checkNotPassControl) - Utils.Utils.ChangeControlForeColor(control, Color.Red); + var checkNotPassControl = GetInvalidateValueControls(); if (checkNotPassControl.Any()) + { + var failControl = checkNotPassControl.First(); + + // switch to fail control's tab page + var p = failControl.Parent; + while (p != null) + { + if (p is TabPage tabPage) + { + TabControl.SelectedTab = tabPage; + break; + } + + p = p.Parent; + } + return; + } #endregion diff --git a/Netch/Forms/SettingForm.resx b/Netch/Forms/SettingForm.resx index f298a7be..9a18e8d5 100644 --- a/Netch/Forms/SettingForm.resx +++ b/Netch/Forms/SettingForm.resx @@ -57,4 +57,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file