C# 数据网格与自定义对象绑定

C# data grid binding with custom objects

我正在尝试在 DataGrid 中显示帐户数据,但绑定似乎无法正常工作。

public ObservableCollection<AccountData> accountlist = new ObservableCollection<AccountData>();

public MainWindow()
        {
            InitializeComponent();
            AccountDataGrid.ItemsSource = accountlist;
        }

这是我的列表的样子以及我如何将它绑定到数据网格。

GetAccountsResponse resp = GetAccountsResponse.GetAccounts(m_authImpl);

            List<AccountData> accounts = resp.Accounts;
            for (int i = 0; i < accounts.Count; ++i)
            {
                AccountData account = accounts[i];
                accountlist.Add(account);
            }

我正在使用 API 获取帐户数据,然后将其放入我的可观察帐户列表中

<DataGrid x:Name="AccountDataGrid" HorizontalAlignment="Left" Height="249" Margin="10,278,0,0" VerticalAlignment="Top" Width="772" AutoGenerateColumns="False" IsReadOnly="True" >
            <DataGrid.Columns>
                <DataGridTextColumn Header="Type"  Binding="{Binding Path=m_type}"/>
                <DataGridTextColumn Header="Number"  Binding="{Binding Path=m_number}"/>
                <DataGridTextColumn Header="Status"  Binding="{Binding Path=m_status}"/>
                <DataGridTextColumn Header="isPrimary"  Binding="{Binding Path=m_isPrimary}"/>
                <DataGridTextColumn Header="isBilling"  Binding="{Binding Path=m_isBilling}"/>
                <DataGridTextColumn Header="clientAccountType"  Binding="{Binding Path=m_clientAccountType}"/>
            </DataGrid.Columns>
        </DataGrid>

这是我的数据网格的代码

public class AccountData
    {
        public UserAccountType m_type;
        public string m_number;
        public AccountStatus m_status;
        public bool m_isPrimary;
        public bool m_isBilling;
        public ClientAccountType m_clientAccountType;

        public AccountData();
    }

这就是我的对象的样子!

一些背景故事: accountlist 在调试器中具有所有正确的信息,但是当需要在数据网格中显示结果时,它显示为空白条目。

public enum UserAccountType
    {
        Undefined = 0,
        Cash = 1,
        Margin = 2,
        TFSA = 3,
        RRSP = 4,
        SRRSP = 5,
        LRRSP = 6,
        LIRA = 7,
        LIF = 8,
        RIF = 9,
        SRIF = 10,
        LRIF = 11,
        RRIF = 12,
        PRIF = 13,
        RESP = 14,
        FRESP = 15,
        FX = 16,
        FXD = 17,
        Count = 18
    }
public enum AccountStatus
    {
        Undefined = -1,
        UnAllocated = 0,
        Active = 1,
        SuspendedClosed = 2,
        SuspendedViewOnly = 3,
        LiquidateOnly = 4,
        Closed = 5,
        Count = 6
    }
public enum ClientAccountType
    {
        Undefined = 0,
        Individual = 1,
        Joint = 2,
        InformalTrust = 3,
        Corporation = 4,
        InvestmentClub = 5,
        FormalTrust = 6,
        Partnership = 7,
        SoleProprietorship = 8,
        Family = 9,
        JointAndInformalTrust = 10,
        Institution = 11,
        Count = 12
    }

WPF 绑定不支持字段,原因很明显(违反封装)

克服此问题的最简单方法是将 public 字段转换为属性。

public class AccountData
{
    public UserAccountType m_type { get; set; }
    public string m_number { get; set; }
    public AccountStatus m_status { get; set; }
    public bool m_isPrimary { get; set; }
    public bool m_isBilling { get; set; }
    public ClientAccountType m_clientAccountType { get; set; }

    public AccountData() { /* . . . */ }
}

让我们使用 MVVM 来完成。该方法的主要目标是:对数据所做的任何更改都会立即实时显示在 DataGrid 中。

你需要一个实现INotifyPropertyChanged接口

的助手class

NotifyPropertyChanged.cs

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

然后使新数据class 派生自NotifyPropertyChanged。为了与 AccountData class 兼容,我为带有 AccountData 参数的构造函数添加了一个重载。

UIAccountData.cs

public class UIAccountData : NotifyPropertyChanged
{
    private UserAccountType m_type;
    private string m_number;
    private AccountStatus m_status;
    private bool m_isPrimary;
    private bool m_isBilling;
    private ClientAccountType m_clientAccountType;

    public UserAccountType Type
    {
        get => m_type;
        set
        {
            m_type = value;
            OnPropertyChanged();
        }
    }
    public string Number
    {
        get => m_number;
        set
        {
            m_number = value;
            OnPropertyChanged();
        }
    }
    public AccountStatus Status
    {
        get => m_status;
        set
        {
            m_status = value;
            OnPropertyChanged();
        }
    }
    public bool IsPrimary
    {
        get => m_isPrimary;
        set
        {
            m_isPrimary = value;
            OnPropertyChanged();
        }
    }
    public bool IsBilling
    {
        get => m_isBilling;
        set
        {
            m_isBilling = value;
            OnPropertyChanged();
        }
    }
    public ClientAccountType ClientAccountType
    {
        get => m_clientAccountType;
        set
        {
            m_clientAccountType = value;
            OnPropertyChanged();
        }
    }

    public UIAccountData() { }

    public UIAccountData(AccountData account)
    {
        m_type = account.m_type;
        m_number = account.m_number;
        m_status = account.m_status;
        m_isPrimary = account.m_isPrimary;
        m_isBilling = account.m_isBilling;
        m_clientAccountType = account.m_clientAccountType;
    }

    public AccountData ToAccountData => new AccountData
    {
        m_type = m_type,
        m_number = m_number,
        m_status = m_status,
        m_isPrimary = m_isPrimary,
        m_isBilling = m_isBilling,
        m_clientAccountType = m_clientAccountType,
    };
}

然后创建包含集合并用于绑定的主要 class。

MainViewModel.cs

public class MainViewModel : NotifyPropertyChanged
{
    public ObservableCollection<UIAccountData> _accountlist;

    public ObservableCollection<UIAccountData> Accountlist
    {
        get => _accountlist;
        set
        {
            _accountlist = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        // demo values
        Accountlist = new ObservableCollection<UIAccountData>
        {
            new UIAccountData { Type = UserAccountType.Cash, Number = "123", Status = AccountStatus.Active, IsPrimary = true, IsBilling = true, ClientAccountType = ClientAccountType.Individual },
            new UIAccountData { Type = UserAccountType.Margin, Number = "aaa", Status = AccountStatus.Active, IsPrimary = false, IsBilling = true, ClientAccountType = ClientAccountType.InformalTrust },
            new UIAccountData { Type = UserAccountType.LRRSP, Number = "bbb", Status = AccountStatus.SuspendedClosed, IsPrimary = true, IsBilling = false, ClientAccountType = ClientAccountType.Partnership },
            new UIAccountData { Type = UserAccountType.SRIF, Number = "456", Status = AccountStatus.SuspendedViewOnly, IsPrimary = false, IsBilling = false, ClientAccountType = ClientAccountType.Institution },
        };
        AccountData account = new AccountData() { m_type = UserAccountType.FX, m_number = "fff", m_status = AccountStatus.SuspendedViewOnly, m_isPrimary = true, m_isBilling = false, m_clientAccountType = ClientAccountType.Institution };
        Accountlist.Add(new UIAccountData(account));
    }
}

完整标记

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        Title="MainWindow" Height="600" Width="1000" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <local:MainViewModel/><!-- MainViewModel attached here -->
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding Accountlist}" HorizontalAlignment="Left" AutoGenerateColumns="False" IsReadOnly="True" Width="Auto">
            <DataGrid.Columns>
                <DataGridTextColumn Width="*" Header="Type" Binding="{Binding Type}"/>
                <DataGridTextColumn Width="*" Header="Number" Binding="{Binding Number}"/>
                <DataGridTextColumn Width="*" Header="Status" Binding="{Binding Status}"/>
                <DataGridCheckBoxColumn Width="*" Header="IsPrimary" Binding="{Binding IsPrimary}"/>
                <DataGridCheckBoxColumn Width="*" Header="IsBilling" Binding="{Binding IsBilling}"/>
                <DataGridTextColumn Width="*" Header="ClientAccountType" Binding="{Binding ClientAccountType}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

最后

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}