首頁 c-sharp-WPF的Binding與Modal與Xaml研究
文章
Cancel

c-sharp-WPF的Binding與Modal與Xaml研究

前言

這邊紀錄我在維護ASP.NET Core時在Razor中常碰到的語法使用方式 (可能有些不局限於ASP.NET Core)

Modal 綁定UI 簡單寫法

Step1.建立Modal並繼承 INotifyPropertyChanged

然後實作OnPropertyChanged 要注意屬性資料的寫法 一定要像範例中那樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public class BaseInfo : INotifyPropertyChanged
 {
     public event PropertyChangedEventHandler PropertyChanged;
     protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }
     private string _CodeName;

     public string CodeName
     {
         get => _CodeName;
         set
         {
             if (_CodeName != value)
             {
                 _CodeName = value;
                 OnPropertyChanged(nameof(CodeName));
             }
         }
     }
 }

Step2.Xaml設定資料綁定

a.設定綁定Modal

在 xmlns:local底下 加入Modal的namespace

1
xmlns:modal="clr-namespace:test.Domain.Model

以及設定namespace底下的Class

1
2
3
    <Window.DataContext>
        <modal:BaseInfo />
    </Window.DataContext>

b.綁定Property 以textBox為例 Biding大概這樣寫

1
   <TextBox Text="{Binding CodeName, Mode=TwoWay}"/>

Step3.透過讀取綁定的Modal資料來撰寫商業邏輯的方式

a.建立RelayCommand並繼承 ICommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

b.方才的Modal物件要擁有 ICommand的屬性資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 public class BaseInfo : INotifyPropertyChanged
 {
     public ICommand SubmitCommand { get; }
     public BaseInfo()
     {
         // 初始化命令并绑定业务逻辑
         SubmitCommand = new RelayCommand(ExecuteSubmit, CanExecuteSubmit);
     }
     public event PropertyChangedEventHandler PropertyChanged;
     protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }
     private string _CodeName;

     public string CodeName
     {
         get => _CodeName;
         set
         {
             if (_CodeName != value)
             {
                 _CodeName = value;
                 OnPropertyChanged(nameof(CodeName));
             }
         }
     }
     
      private void ExecuteSubmit(object parameter)
     {
         // 这里是按钮点击后执行的业务逻辑
         MessageBox.Show($"You entered: ");
     }

     private bool CanExecuteSubmit(object parameter)
     {
         // 可以在这里添加按钮是否可用的逻辑,比如判断 Text 是否为空
         return true;
     }
 }

c.xaml設定ICommand的Binding

1
 <Button x:Name="btn_SHOW_JSON" Command="{Binding SubmitCommand}"/>

d.補充說明 SubmitCommand 傳入參數的方式 可以使用CommandParameter

1
2
3
<Button Content="Submit" 
        Command="{Binding SubmitCommand}" 
        CommandParameter="Hello, World!" />

上述為輸入參數為”Hello, World!” 下面為讀取參數”Hello, World!”的方式

1
2
3
4
5
private void ExecuteSubmit(object parameter)
{
    string message = parameter as string;
    MessageBox.Show(message); // This will show "Hello, World!"
}

也可以傳遞Xaml物件

1
2
3
4
<TextBox Name="InputTextBox" Width="200" Margin="10"/>
<Button Content="Submit" 
        Command="{Binding SubmitCommand}" 
        CommandParameter="{Binding Text, ElementName=InputTextBox}" />

Step4.控制按鈕狀態

寫一個Class 去繼承IValueConverter

1
2
3
4
5
6
7
8
9
10
11
12
13
 public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            bool isVisible = (bool)value;
            return isVisible ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

xaml的部分會像這樣

1
2
3
4
5
6
7
8
9
10
    <Window.Resources>
        <!-- Register the converter -->
        <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
    </Window.Resources>


  <TextBlock Text="This is visible or hidden"
                   Visibility="{Binding IsTextVisible, Converter={StaticResource BoolToVisibilityConverter}}"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"/>

Step5.單純寫text驗證的方式

範例xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Grid>
    <StackPanel>
        <TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" 
                 Margin="10"/>
        <TextBlock Foreground="Red" Text="{Binding (Validation.Errors)[0].ErrorContent}"   Margin="10"/>

        <TextBox Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" 
                 Margin="10"/>
        <TextBlock Foreground="Red" Text="{Binding (Validation.Errors)[1].ErrorContent}"  Margin="10"/>

        <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" 
                 Margin="10"/>
        <TextBlock Foreground="Red" Text="{Binding (Validation.Errors)[2].ErrorContent}"  Margin="10"/>
    </StackPanel>
</Grid>

範例C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    public partial class MainWindow : Window
    {
        public Person Person { get; set; }
        public MainWindow()
        {
            InitializeComponent();

            // Initialize the Person object and set DataContext
            Person = new Person();
            DataContext = Person;
        }
    }
    public class Person : IDataErrorInfo
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }

        // IDataErrorInfo Implementation
        public string this[string columnName]
        {
            get
            {
                switch (columnName)
                {
                    case nameof(FirstName):
                        if (string.IsNullOrWhiteSpace(FirstName))
                            return "First name is required.";
                        break;

                    case nameof(LastName):
                        if (string.IsNullOrWhiteSpace(LastName))
                            return "Last name is required.";
                        break;

                    case nameof(Age):
                        if (Age < 0 || Age > 120)
                            return "Age must be between 0 and 120.";
                        break;
                }
                return null;
            }
        }

        public string Error => null; // No global error
    }
本文由作者按照 CC BY 4.0 進行授權