只读记录

时间:2014-02-10 12:07:00

标签: c# winforms data-binding

我有一个Windows窗体应用程序,它具有数据绑定控件(详细信息视图),使用BindingSource,BindingNavigator和TableAdapter控件。我想将一条记录设为只读。我读了很多文章,没有运气就尝试了几件事。我在Navigator上处理了DeleteItem,但很快意识到你仍然可以更改它,Move会保存更改。

我已阅读有关IEditableObject和Filters的内容,但不知道如何使用上述控件实现此功能。

1 个答案:

答案 0 :(得分:0)

您的情况可能会有所不同,但由于我想要以只读方式创建的特殊记录是以编程方式添加的,而其他记录是由用户输入的,我只需将BindingSource的Filter属性更改为:

field1 <> 'Special' AND field2 <> 'Record'

然后不会显示特殊记录。

但是,我真的想要显示记录,只是不允许更改或删除。

BindingSource和BindingNavigator之间的关系在BindingNavigator Class文档的备注中有解释。

但是,我下载了BindingNavigator.cs source code in C# .NET并找到了这些BindingNavigator按钮事件处理程序: OnMoveFirst, OnMovePrevious, OnMoveNext, OnMoveLast, OnAddNew, OnDelete, 并且每个只是调用BindingSource中的相关函数。

以下是感兴趣的内容:

    private void OnDelete(object sender, EventArgs e) {
        if (Validate()) { 
            if (bindingSource != null) {
                bindingSource.RemoveCurrent(); 
                RefreshItemsInternal(); 
            }
        } 
    }

我们可以看到Validate()函数,但它的开发人员文档不是很好:

    // <include file="doc\BindingNavigator.uex" path='docs/doc[@for="BindingNavigator.Validate"]/*'>
    // <devdoc> 
    //     Triggers form validation. Used by the BindingNavigator's standard items when clicked. If a validation error occurs
    //     on the form, focus remains on the active control and the standard item does not perform its standard click action. 
    //     Custom items may also use this method to trigger form validation and check for success before performing an action. 
    // </devdoc>
    public bool Validate() { 
        bool validatedControlAllowsFocusChange;
        return this.ValidateActiveControl(out validatedControlAllowsFocusChange);
    }
有点嘿,因为它是公开的,我也可以称之为。他们都完成了:

    /// <devdoc>
    ///     Refresh the state of the items when the state of the data changes.
    /// </devdoc> 
    private void RefreshItemsInternal() {
        // Block all updates during initialization 
        if (initializing) { 
            return;
        } 

        // Call method that updates the items (overridable)
        OnRefreshItems();
    } 

    /// <include file="doc\BindingNavigator.uex" path='docs/doc[@for="BindingNavigator.OnRefreshItems"]/*'> 
    /// <devdoc> 
    ///     Called when the state of the tool strip items needs to be refreshed to reflect the current state of the data.
    ///     Calls <see cref="RefreshItemsCore"> to refresh the state of the standard items, then raises the RefreshItems event. 
    /// </see>
    protected virtual void OnRefreshItems() {
        // Refresh all the standard items
        RefreshItemsCore(); 

        // Raise the public event 
        if (onRefreshItems != null) { 
            onRefreshItems(this, EventArgs.Empty);
        } 
    }

    /// <include file="doc\BindingNavigator.uex" path='docs/doc[@for="BindingNavigator.RefreshItemsCore"]/*'>
    /// <devdoc> 
    ///     Refreshes the state of the standard items to reflect the current state of the data. 
    /// </devdoc>
    [EditorBrowsable(EditorBrowsableState.Advanced)] 
    protected virtual void RefreshItemsCore() {
        int count, position;
        bool allowNew, allowRemove;

        // Get state info from the binding source (if any)
        if (bindingSource == null) { 
            count = 0; 
            position = 0;
            allowNew = false; 
            allowRemove = false;
        }
        else {
            count = bindingSource.Count; 
            position = bindingSource.Position + 1;
            allowNew = (bindingSource as IBindingList).AllowNew; 
            allowRemove = (bindingSource as IBindingList).AllowRemove; 
        }

        // Enable or disable items (except when in design mode)
        if (!DesignMode) {
            if (MoveFirstItem != null)    moveFirstItem.Enabled    = (position > 1);
            if (MovePreviousItem != null) movePreviousItem.Enabled = (position > 1); 
            if (MoveNextItem != null)     moveNextItem.Enabled     = (position < count);
            if (MoveLastItem != null)     moveLastItem.Enabled     = (position < count); 
            if (AddNewItem != null)       addNewItem.Enabled       = (allowNew); 
            if (DeleteItem != null)       deleteItem.Enabled       = (allowRemove && count > 0);
            if (PositionItem != null)     positionItem.Enabled     = (position > 0 && count > 0); 
            if (CountItem != null)        countItem.Enabled        = (count > 0);
        }

        // Update current position indicator 
        if (positionItem != null) {
            positionItem.Text = position.ToString(CultureInfo.CurrentCulture); 
        } 

        // Update record count indicator 
        if (countItem != null) {
            countItem.Text = DesignMode ? CountItemFormat : String.Format(CultureInfo.CurrentCulture, CountItemFormat, count);
        }
    } 

所以我想出了以下内容以防止删除,这有效:

    // First change bindingNavigator1's DeleteItem property from bindingNavigatorDeleteItem to (none)
    // and then add a click event handler for the Delete button:
    private void bindingNavigatorDeleteItem_Click(object sender, EventArgs e)
    {
        // prevent special record deletion
        if ((textBox1.Text == "Special") && (textBox2.Text == "Record"))
        {
            MessageBox.Show("This record cannot be deleted.",
                "Delete operation aborted",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information,
                MessageBoxDefaultButton.Button1);
        }
        else
        {
            if (this.Validate() && (this.bindingSource1 != null))
            {
                this.bindingSource1.RemoveCurrent();
                this.bindingSource1.EndEdit();
                this.tableAdapterManager.UpdateAll(this.dataSet1);
            }
        }
    }

现在,防止变化呢?我为“保存”按钮添加了一个单击处理程序:

    private void bindingNavigator1SaveItem_Click(object sender, EventArgs e)
    {
        if ((textBox1.Text == "Special") && (textBox2.Text == "Record"))
        {
            MessageBox.Show("Cannot change this record.",
                "Save operation aborted",
                MessageBoxButtons.OK,
                MessageBoxIcon.Exclamation,
                MessageBoxDefaultButton.Button1);
            return;
        }

        try
        {
            this.Validate();
            this.bindingSource1.EndEdit();
            this.tableAdapterManager1.UpdateAll(this.DataSet1);
            this.tableAdapter1.Fill(DataSet1.Customers);
            this.bindingSource1.Position = this.DataSet1.Tables[0].Rows.Count;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

但是,此时,用户可以更改记录,然后单击其中一个移动按钮。现在看来TableAdapter?获得更新,但是如果没有单击“保存”按钮,更改永远不会写回数据库。也许我们可以在这里做得更好。

现在,还记得所有按钮调用的Validate()函数吗?查看其他事件处理程序,我们看到它们都调用Validate:

    private void OnMoveFirst(object sender, EventArgs e) { 
        if (Validate()) { 
            if (bindingSource != null) {
                bindingSource.MoveFirst(); 
                RefreshItemsInternal();
            }
        }

可能会有人认为BindingNavigator Validating事件可能会触发,我们可以显示一个MessageBox并设置e.Cancel = true,但由于某种原因,它不会触发任何按钮点击。

我最终采用的方法(看起来有点kludgy)是为特殊记录的关键字段处理TextChanged事件并禁用所有控件:

    private void textBox2_TextChanged(object sender, EventArgs e)
    {
        if (textBox2.Text == "Record")
        {
            foreach (Control ctrl in groupBox1.Controls)
            {
                if ((ctrl.GetType() == typeof(TextBox)) ||
                   (ctrl.GetType() == typeof(ComboBox)) ||
                   (ctrl.GetType() == typeof(CheckBox)))
                {
                    ctrl.Enabled = false;
                }
            }
        }
        else
        {
            foreach (Control ctrl in groupBox1.Controls)
            {
                if ((ctrl.GetType() == typeof(TextBox)) ||
                   (ctrl.GetType() == typeof(ComboBox)) ||
                   (ctrl.GetType() == typeof(CheckBox)))
                {
                    ctrl.Enabled = true;
                }
            }
        }
    }

如果唯一的控件是TextBoxes,您可能更喜欢使用“ctrl.ReadOnly = true”而不是“ctrl.Enabled = false”。

我仍然在寻找一种更简单,更优雅的方法来捕捉变化并阻止它们......