簡體   English   中英

我可以使用隱藏的DataGridView列來管理SQL Server VarBinary字段嗎?

[英]Can I use a hidden DataGridView column to manage a SQL Server VarBinary field?

我有一個表,我想在一個字段中存儲一個字節數組。 字節數組大約是20個字節(160位)的密鑰數據。

我正在使用幾個DataGridView來管理該表和該應用程序的其他表。 我目前有兩個例程,可以讓我提供一個SQL選擇字符串,而DataGridView允許用戶編輯數據。

private void InitializeUsersDataGrid()
{
  string sql = "SELECT UserId, Enabled, AccessLevel, Name, KeyValue FROM Users";

  DataGridViewIntialize(dgvUsers, sql);
  dgvUsers.Columns[fdKeyValue].Visible = false;
}

private void DataGridViewIntialize(DataGridView dataGridView, string sql)
{
  dataGridViewInUse = dataGridView; // This is the current DataGridView
  OleDbConnection oleDbConnection = new OleDbConnection(txtConnectionString.Text);
  dataAdapter = new OleDbDataAdapter(sql, oleDbConnection);
  OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter);  //Creates SQL commands for IUD

  dataTable = new DataTable();
  dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture;

  dataAdapter.Fill(dataTable);
  bindingSource1.DataSource = dataTable;

  dataGridView.DataSource = bindingSource1;

  // Resize the DataGridView columns to fit the newly loaded content.
  dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
  AddDeleteRefreshContextMenu(dataGridView);
}

我有一個保存按鈕,提供驗證,然后調用TableSave

private void TableSave()
{
  // Update the database with the user's changes.
  try {
    dataAdapter.Update((DataTable)bindingSource1.DataSource);
  }
  catch (Exception ex) {
    MessageBox.Show(ex.Message);
  }
}

當我將varbinary字段添加到表中時,它在DataGridView為損壞的位圖圖像。 這與我無關,因為無論如何我都不會顯示它。

當我嘗試為調用分配一個新的字節數組時,它將生成一個DataGridView默認錯誤對話框。

/// <summary>
/// Change the password hash for the selected login
/// </summary>
/// <param name="e"></param>
void ChangePassword( DataGridViewCellEventArgs e)
{
  // Request a new password with confirmation
  fPassword form = new fPassword();
  form.Confirm = true;
  DialogResult dr = form.ShowDialog();
  if (dr == DialogResult.OK) {
    // Combine the UserID with the password to generate a new key
    byte[] salt;
    byte[] key;
    string p = dgvUsers.Rows[e.RowIndex].Cells["UserId"].Value + form.Value;
    Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
    if (dgvUsers.Rows[e.RowIndex].Cells[fdKeyValue].ValueType == key.GetType()) {
      // We do get this far Error occurs on the assignment in the next line
      dgvUsers.Rows[e.RowIndex].Cells[fdKeyValue].Value = key;
    } 
  }
}

DataGridView默認錯誤對話框

DataGridView中發生以下異常:System.ArgumentException:參數無效。
在System.Windows.Forms.Formatter.FormatObjectInternal(對象值,在System.Drawing.Image.FromStream(流流,布爾值useEmbeddedColorManagement,布爾值validateImageData)在System.Windows.Forms.Formatter。在System.Windows.Forms.Formatter.FormatObject(對象值,類型targetType,TypeConverter sourceConverter,TypeConverter targetConverter,字符串formatString,IFormatProvider formatInfo,對象formattedNullValue, System.Windows.Forms.DataGridViewCell.GetFormattedValue(Object value,Int32 rowIndex,DataGridViewCellStyle&cellStyle,TypeConverter valueTypeConverter,TypeConverter formattedValueTypeConverter,DataGridViewDataErrorContexts上下文的對象dataSourceNullValue)

要替換此默認對話框,請處理DataError事件。

看來這與CellFormatting但我不知道如何將其關閉。

添加以下代碼后,將出現一個更簡單的對話框,顯示以下錯誤消息:

System.FormatException:單元格的格式化值具有錯誤的類型。

private void dgvUsers_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
  DataGridViewColumn c = dgvUsers.Columns[fnKeyValue];
  if (c != null) {
    if (e.ColumnIndex == c.Index) {
      if (e.Value != null) {
        e.FormattingApplied = true;
      }
      else
        e.FormattingApplied = false;
    }
  }
}

更新#1-使用Ivan Stoev建議的方法的其他代碼,並顯示了我用來請求密碼更改的按鈕單元。 它還增加了保存鹽值。

此時的問題是,當我按下“更改密碼”按鈕時,可以打開密碼對話框,獲取密碼。 生成鹽和鍵,將其保存到該行鹽和鍵值的datagrid單元中。

如果我執行TableSave() (請參閱前面的代碼),則除非我在網格中的另一行上單擊鼠標,否則這些字段不在SQL Server表中。 該操作似乎表明該行是臟的,然后保存將起作用。

在更改密碼之前或之后,我還可以更改用戶可見的字段之一,該行將保存。

private void InitializeUsersDataGrid()
{
  string sql = "SELECT UserId, Enabled, AccessLevel, Name, Salt, KeyValue FROM Users";

  AddAccessLevelColumn(dgvUsers);  //Add column if needed
  AddPasswordChangeColumn(dgvUsers);
  DataGridViewIntialize1(dgvUsers, sql);
  dgvUsers.Columns[UsersFieldName.fdAccessLevel].Visible = false;  //Hide the raw column from the database

  MoveAccessLevelColumn(dgvUsers, AccessLevelComboBoxColumn, AccessLevelColumnPosition);
  MoveAccessLevelColumn(dgvUsers, PasswordButtonColumn, PasswordColumnPosition);
}

private void DataGridViewIntialize1(DataGridView dataGridView, string sql)
{
  dataGridViewInUse = dataGridView; // This is the current DataGridView
  OleDbConnection oleDbConnection = new OleDbConnection(txtConnectionString.Text);
  dataAdapter = new OleDbDataAdapter(sql, oleDbConnection);
  OleDbCommandBuilder commandBuilder = new OleDbCommandBuilder(dataAdapter);  //Creates SQL commands for IUD

  dataTable = new DataTable();
  dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture;

  dataAdapter.Fill(dataTable);
  dataTable.Columns[Data.UsersFieldName.fdSalt].ColumnMapping = MappingType.Hidden;
  dataTable.Columns[Data.UsersFieldName.fdKey].ColumnMapping = MappingType.Hidden;
  bindingSource1.DataSource = dataTable;

  dataGridView.DataSource = bindingSource1;

  // Resize the DataGridView columns to fit the newly loaded content.
  dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
  AddDeleteRefreshContextMenu(dataGridView);
}

private void AddPasswordChangeColumn(DataGridView dataGridView)
{
  // This column will remain unless manually removed, so it only needs to be added
  // the first time the DVG is initialized.

  if (dataGridView.Columns[PasswordButtonColumn] == null) {
    // creating new ComboBoxCell Column
    DataGridViewButtonColumn btnColumn = new DataGridViewButtonColumn();
    btnColumn.Text = "Change Password";
    btnColumn.HeaderText = "Change Password";
    btnColumn.UseColumnTextForButtonValue = true;
    btnColumn.Name = PasswordButtonColumn;
    btnColumn.FlatStyle = FlatStyle.Popup;
    dataGridView.Columns.Insert(0, btnColumn);
    // Add a CellClick handler to handle clicks in the button column.
    dataGridView.CellClick += dataGridView_CellClick;
  }
}

private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
  // Ignore clicks that are not on button cells.
  if (e.RowIndex >= 0 && e.RowIndex < dgvUsers.RowCount - 1 && e.ColumnIndex == dgvUsers.Columns[PasswordButtonColumn].Index) {
    ChangePassword(e);
  }
}

/// <summary>
/// Change the password hash for the selected login
/// </summary>
/// <param name="e"></param>
private void ChangePassword(DataGridViewCellEventArgs e)
{
  // Request a new password with confirmation
  fPassword form = new fPassword();
  form.Confirm = true;
  DialogResult dr = form.ShowDialog();
  if (dr == DialogResult.OK) {
    // Combine the UserID with the password to generate a new key
    byte[] salt;
    byte[] key;
    string p = dgvUsers.Rows[e.RowIndex].Cells[Data.UsersFieldName.fdUserId].Value + form.Value;
    Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
    var gridRow = dgvUsers.Rows[e.RowIndex];
    var dataRow = (DataRowView)gridRow.DataBoundItem;
    var value = dataRow[Data.UsersFieldName.fdKey];
    dataRow[Data.UsersFieldName.fdSalt] = salt;  //This assignment now works
    dataRow[Data.UsersFieldName.fdKey] = key;
  }
}

更新#2將'NotifyCurrectCell'臟添加到'ChangePassword'方法中,通知網格需要保存單元格而不更改UI中的行。

private void ChangePassword(DataGridViewCellEventArgs e)
{
  // Request a new password with confirmation
  fPassword form = new fPassword();
  form.Confirm = true;
  DialogResult dr = form.ShowDialog();
  if (dr == DialogResult.OK) {
    // Combine the UserID with the password to generate a new key
    byte[] salt;
    byte[] key;
    string p = dgvUsers.Rows[e.RowIndex].Cells[Data.UsersFieldName.fdUserId].Value + form.Value;
    Data.PBK2DF2Hash.GenerateSaltAndKey(p, out salt, out key);
    var gridRow = dgvUsers.Rows[e.RowIndex];
    var dataRow = (DataRowView)gridRow.DataBoundItem;
    var value = dataRow[Data.UsersFieldName.fdKey];
    dataRow[Data.UsersFieldName.fdSalt] = salt;  //This assignment now works
    dataRow[Data.UsersFieldName.fdKey] = key;
    dgvUsers.NotifyCurrentCellDirty(true); //Tells the UI that this row has changed and needs updating
  }
}

您遇到的問題是因為DataGridView在默認情況下為byte[]數據類型創建了DataGridViewImageColumn

這里有一些選擇。

  • 答:將DataGridView.AutoGenerateColumns屬性設置為false並手動創建網格列。

  • B.如果您確實不需要網格中的該列,則不要嘗試隱藏網格列,而根本不要創建網格列。 但是如何實現呢? 對於類屬性,可以使用Browsable(false) ,但這是DataTable 好吧,盡管沒有記錄, MappingType.Hidden出於相同的目的,可以將DataColumn.ColumnMapping屬性與MappingType.Hidden一起使用。

您的情況下,請刪除此行

dgvUsers.Columns[fdKeyValue].Visible = false;

並在DataGridViewIntialize方法內部使用

// ...
dataAdapter.Fill(dataTable);
dataTable.Columns[fdKeyValue].ColumnMapping = MappingType.Hidden;
bindingSource1.DataSource = dataTable;
// ...

您仍然可以使用這樣的基礎數據源訪問列數據

var gridRow = dgvUsers.Rows[...];
var dataRow = (DataRowView)gridRow.DataBoundItem;
var value = dataRow[fdKeyValue];

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM