Windows 窗体数据绑定到单选按钮

问题描述 投票:0回答:1

我正在尝试将两个布尔属性绑定到两个单选按钮。当表单加载时,两个单选按钮均处于未选中状态。当我单击第一个单选按钮时,它被选中。但是,当我尝试单击第二个单选按钮时,第一个单选按钮保持选中状态,第二个单选按钮仍未选中。如果我再次单击第二个单选按钮,两个单选按钮都将被取消选中。如果我再次单击第二个单选按钮,它将被选中。基本上,需要点击 3 次才能选中第二个单选按钮。如果我首先检查了第二个单选按钮,则同样适用于第一个单选按钮。

我有一个视图模型类:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace WinFormsApp1
{
    internal class ViewModel : INotifyPropertyChanged
    {
        private bool _isCashPayment;
        private bool _isChequePayment;

        public bool IsCashPayment
        {
            get => _isCashPayment;
            set
            {
                if (_isCashPayment == value)
                {
                    return;
                }
                _isCashPayment = value;


                // Notify the UI that the property has changed.
                OnPropertyChanged();
            }
        }

        public bool IsChequePayment
        {
            get => _isChequePayment;
            set
            {
                if (_isChequePayment == value)
                {
                    return;
                }

                _isChequePayment = value;

                // Notify the UI that the property has changed.
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        // Notify the UI that one of the properties has changed.
        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    }
}

表格代码:


namespace WinFormsApp1
{
    public partial class Form7 : Form
    {
        private ViewModel _viewModel;

        public Form7()
        {
            InitializeComponent();
            _viewModel = new ViewModel();
        }

        private void Form7_Load(object sender, EventArgs e)
        {
            rdbCash.DataBindings.Add(nameof(rdbCash.Checked), _viewModel, nameof(_viewModel.IsCashPayment), false, DataSourceUpdateMode.OnPropertyChanged);
            rdbCheque.DataBindings.Add(nameof(rdbCheque.Checked), _viewModel, nameof(_viewModel.IsChequePayment), false, DataSourceUpdateMode.OnPropertyChanged);
        }
    }
}

表单设计器生成的代码:

namespace WinFormsApp1
{
    partial class Form7
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            rdbCash = new RadioButton();
            rdbCheque = new RadioButton();
            button1 = new Button();
            SuspendLayout();
            // 
            // rdbCash
            // 
            rdbCash.AutoSize = true;
            rdbCash.Location = new Point(20, 66);
            rdbCash.Name = "rdbCash";
            rdbCash.Size = new Size(51, 19);
            rdbCash.TabIndex = 0;
            rdbCash.Text = "Cash";
            rdbCash.UseVisualStyleBackColor = true;
            // 
            // rdbCheque
            // 
            rdbCheque.AutoSize = true;
            rdbCheque.Location = new Point(136, 66);
            rdbCheque.Name = "rdbCheque";
            rdbCheque.Size = new Size(66, 19);
            rdbCheque.TabIndex = 1;
            rdbCheque.Text = "Cheque";
            rdbCheque.UseVisualStyleBackColor = true;
            // 
            // button1
            // 
            button1.Location = new Point(15, 28);
            button1.Name = "button1";
            button1.Size = new Size(75, 23);
            button1.TabIndex = 2;
            button1.Text = "button1";
            button1.UseVisualStyleBackColor = true;
            // 
            // Form7
            // 
            AutoScaleDimensions = new SizeF(7F, 15F);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(800, 450);
            Controls.Add(button1);
            Controls.Add(rdbCheque);
            Controls.Add(rdbCash);
            Name = "Form7";
            Text = "Form7";
            Load += Form7_Load;
            ResumeLayout(false);
            PerformLayout();
        }

        #endregion

        private RadioButton rdbCash;
        private RadioButton rdbCheque;
        private Button button1;
    }
}

我正在尝试使用数据绑定而不是传统的单选按钮检查事件处理程序。我期望单选按钮在单击时会更改其状态,并更改绑定属性的值。我尝试将两个单选按钮放在一个组框中,结果是相同的。 我观察到的是,当选择第一个单选按钮并单击第二个单选按钮时,首先调用绑定到第一个单选按钮的属性设置器并传递 false 值,然后调用绑定到第二个单选按钮的属性设置器。但是,设置器获取的是假值,而不是传递真值,因此,单击单选按钮时不会选中该单选按钮。

c# winforms mvvm radio-button
1个回答
0
投票

在 Jimi 和这篇文章的帮助下:MVVM radiobuttons,我终于在 Windows 窗体中使用了单选按钮双向数据绑定。

在 form.cs 文件中,我们创建一个 BindingSource 并使用它来绑定单选按钮。

    private void Form7_Load(object sender, EventArgs e)
    {
        BindingSource rdBindingSource = new BindingSource(_viewModel, null);
        rdbCash.DataBindings.Add(nameof(rdbCash.Checked), rdBindingSource, nameof(_viewModel.IsCashPayment), false, DataSourceUpdateMode.OnPropertyChanged);
        rdbCheque.DataBindings.Add(nameof(rdbCheque.Checked), rdBindingSource, nameof(_viewModel.IsChequePayment), false, DataSourceUpdateMode.OnPropertyChanged);
    }

在 ViewModel 中,我们定义一个枚举对象来保存单选按钮值。单选按钮绑定属性的设置器不会直接触发 PropertyChanged() 事件。相反,他们更改支付源属性,并且支付源属性仅在更改时才会触发 PropertyChanged() 事件。这里的关键点是,当单击单选按钮时,会发生两个事件:首先,先前选择的单选按钮会将状态更改为 false(未选中),其次当前选择的单选按钮会将状态更改为 true(选中)。 PropertyChanged() 事件应仅在第二个事件中触发。如果它在第一个事件中被触发,它会弄乱单选按钮的状态。第二个事件实际上会将单选按钮状态传递为 false(未选中)。

internal enum PaymentSource
{
    None,
    Cash,
    Cheque
}

internal partial class ViewModel : INotifyPropertyChanged
{
    private PaymentSource _paymentSource;

    public bool IsCashPayment
    {
        get { return Source == PaymentSource.Cash; }
        set
        {
            if (value && Source != PaymentSource.Cash)
            {
                Source = PaymentSource.Cash;
            }
        }
    }

    public bool IsChequePayment
    {
        get { return Source == PaymentSource.Cheque; }
        set
        {
            if (value && Source != PaymentSource.Cheque)
            {
                Source = PaymentSource.Cheque;
            }
        }
    }

    public PaymentSource Source
    {
        get { return this._paymentSource; }
        set
        {
            if (this._paymentSource == value)
                return;

            this._paymentSource = value;
            OnPropertyChanged(nameof(IsCashPayment));
            OnPropertyChanged(nameof(IsChequePayment));
        }
    }
    public event PropertyChangedEventHandler? PropertyChanged;

    // Notify the UI that one of the properties has changed.
    private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
© www.soinside.com 2019 - 2024. All rights reserved.