我正在开发一个处理多用户和多公司概念的 Winforms 应用程序。如果用户进入公司并打开某些内容(例如帐户),它会在 datagridview 中显示该数据,但是当用户在应用程序内切换公司时,它也会切换 SQLite 数据库文件。
但问题是:datagridview 没有显示第二家公司的数据,它仍然显示第一家公司的数据。
这是我的代码:
private void ViewSearch(string search)
{
try
{
string query = string.Empty;
if (string.IsNullOrEmpty(search) && !string.IsNullOrEmpty(search))
{
query = "Select * from tblAccount";
}
else
{
query = "Select * from tblAccount where AccountName Like '%" + search + "%'";
ds = AccessToDatabaseComp.retrieve(query);
dgvAccountView.AutoGenerateColumns = false;
bs.DataSource = ds.Tables[0];
if (ds != null)
{
if (ds.Tables[0].Rows.Count > 0)
{
dgvAccountView.DataSource = ds.Tables[0];
dgvAccountView.Columns[0].HeaderText = "Account ID";
dgvAccountView.Columns[0].Width = 50;
dgvAccountView.Columns[0].DataPropertyName = "AccountID";
dgvAccountView.Columns[0].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleLeft;
dgvAccountView.Columns[1].HeaderText = "Account Name";
dgvAccountView.Columns[1].Width = 500;
dgvAccountView.Columns[1].DataPropertyName = "AccountName";
dgvAccountView.Columns[1].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleLeft;
dgvAccountView.Columns[2].HeaderText = "Credit";
dgvAccountView.Columns[2].Width = 150;
dgvAccountView.Columns[2].DataPropertyName = "Credit";
dgvAccountView.Columns[2].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
dgvAccountView.Columns[3].HeaderText = "Debit";
dgvAccountView.Columns[3].Width = 150;
dgvAccountView.Columns[3].DataPropertyName = "Debit";
dgvAccountView.Columns[3].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
}
else
{
dgvAccountView.DataSource = null;
}
}
else
{
dgvAccountView.DataSource = null;
}
}
}
catch
{
}
}
数据访问类:
public class AccessToDatabaseComp
{
private static string dbPathcomp = Application.StartupPath + "\\" + frmMain.Instance.lblDBFile.Text + ";";
private static string conStringComp = "Data Source=" + dbPathcomp + "version=3";
//SQLiteConnection sqliteconComp = new SQLiteConnection(conStringComp);
private static SQLiteConnection sqlite;
public static SQLiteConnection sqliteconComp()
{
if (sqlite == null)
{
sqlite = new SQLiteConnection(conStringComp);
}
if (sqlite.State != System.Data.ConnectionState.Open)
{
sqlite.Open();
}
else
{
sqlite.Close();
}
return sqlite;
}
public static bool insert(string query)
{
try
{
int rows = 0;
SQLiteDataAdapter da = new SQLiteDataAdapter();
da.InsertCommand = new SQLiteCommand(query, sqliteconComp());
rows = da.InsertCommand.ExecuteNonQuery();
if (rows > 0)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
public static bool update(string query)
{
try
{
int rows = 0;
SQLiteDataAdapter da = new SQLiteDataAdapter();
da.UpdateCommand = new SQLiteCommand(query, sqliteconComp());
rows = da.UpdateCommand.ExecuteNonQuery();
if (rows > 0)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
public static bool delete(string query)
{
try
{
int rows = 0;
SQLiteDataAdapter da = new SQLiteDataAdapter();
da.DeleteCommand = new SQLiteCommand(query, sqliteconComp());
rows = da.DeleteCommand.ExecuteNonQuery();
if (rows > 0)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
public static DataSet retrieve(string query)
{
try
{
SQLiteDataAdapter da1 = new SQLiteDataAdapter();
da1.SelectCommand = new SQLiteCommand(query, sqliteconComp());
DataSet ds1 = new DataSet();
da1.Fill(ds1);
return ds1;
}
catch
{
return null;
}
}
}
请提出一些解决方案来摆脱这个问题。
首先,你确定要写这个吗:
if (string.IsNullOrEmpty(search) && !string.IsNullOrEmpty(search))
{
query = "Select * from tblAccount";
}
有没有一个时刻
search
既空又不空?
无论如何,您已经正确地将数据与数据的显示方式分开。
从您的代码中,我了解到您始终希望显示相同的列,这些列应始终以相同的格式显示相同的属性。
那么为什么要重新初始化方法中的所有列
ViewSearch
?
我的建议是在构造函数中仅初始化列一次,并且仅更改数据源。
在 DataGridView 中,您想要显示一系列相似对象的各种属性,这些对象都至少有一个
AccountId
、一个 AccountName
、一个 Credit
和一个 Debit
。
为了以后更改后的安全,我的建议是引入一个接口
IAccount
,您想要在 DataGridView 中显示为行的每个对象至少应该实现这个接口。由于您假设该对象已经具有 AccountId、AccountName 等属性,因此这不会限制您的类的使用。
public interface IAccount // TODO: invent a proper name
{
int AccountId {get;}
string AccountName {get;}
decimal Debet {get;}
decimal Credit {get;}
}
您的表单需要一种方法来获取必须显示的数据:
IEnumerable<IAccount> FetchAccountsToDisplay(string search)
{
... // do something with AccessToDatabaseComp
// either return the fetched Accounts,
// or return Enumerable.Empty<IAccount>();
}
如何将数据获取到
IEnumerable<IAccount>
不是这个问题的一部分。
使用 Visual Studio Designer 添加列,并定义数据的显示方式。在构造函数中定义哪一列应显示哪个属性:
public MyForm()
{
InitializeComponent();
// define which column should display which property:
this.columnAccountId.DataPropertyName = nameof(IAccount.AccountId);
this.columnAccountName.DataPropertyName = nameof(IAccount.AccountName);
this.columnDebet.DataPropertyName = nameof(IAccount.Debet);
this.columnCredit.DataPropertyName = nameof(IAccount.Credit);
}
您也可以使用设计器来完成此操作。使用
nameof
的优点是,如果您稍后更改属性名称而忘记更改名称,编译器会抱怨。
public ICollection<IAccount> DisplayedAccounts
{
get => (ICollection<IAccount>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
显示数据:
void OnDisplayAccounts(string search)
{
ICollection<IAccount> accountsToDisplay = this.FetchAccountsToDisplay(search)
.ToList();
this.DisplayedAccounts = accountsToDisplay;
}
然后很快!您的帐户已显示。即使没有任何帐户 (=
Enumerable.Empty<IAccount>()
),也会显示一个漂亮的空 DataGridView。
上面的方法很简单,只需要几行代码。但是,操作员无法更改 DataGridView 中显示的数据。如果您需要,只需更改几行代码即可。
public interface IAccount
{
int AccountId {get; set;}
string AccountName {get; set;}
decimal Debet {get; set;}
decimal Credit {get; set;}
}
public BindingList<IAccount> DisplayedAccounts
{
get => (BindingList<IAccount>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
void OnDisplayAccounts(字符串搜索) { IList accountToDisplay = this.FetchAccountsToDisplay(搜索) .ToList(); this.DisplayedAccounts = new BindingList(accountsToDisplay); }
现在操作员可以编辑帐户。之后,他单击“确定”按钮以表明他已完成编辑。
void ButtonOk_Clicked(object sender, ...)
{
// fetch the edited data, as well as the original data:
IEnumerable<IAccount> originalAccounts = ...
IEnumerable<IAccount> editedAccounts = this.DisplayedAccounts.Cast<IAccount>();
this.ProcessEditedAccounts(originalAccounts, editedAccounts);
}
执行一些智能 LINQ 来找出哪些帐户
我认为完整的外部联接在这里可能很方便,但这不是问题的一部分
最后两个有用的方法:获取当前行和所有选定的行
IAccount CurrentAccount => (IAccount)((DataGridViewRow)this.dataGridView1.CurrentRow)?.DataBoundItem;
IEnumerable<IAccount> selectedAccounts = this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<IAccount>();