简体   繁体   English

只读记录

[英]Read-only record

I have a Windows Forms application that has data-bound controls (detail view), using BindingSource, BindingNavigator and TableAdapter controls. 我有一个Windows窗体应用程序,该应用程序具有使用BindingSource,BindingNavigator和TableAdapter控件的数据绑定控件(详细视图)。 I would like to make a single record read-only. 我想将一条记录设为只读。 I have read numerous articles and tried several things without luck. 我读了许多文章,尝试了几件事却没有运气。 I handled the DeleteItem on the Navigator, but soon realized you could still change it and a Move would save the changes. 我在导航器上处理了DeleteItem,但很快意识到您仍然可以更改它,而Move可以保存更改。

I have read about IEditableObject and Filters, but not sure how to implement this with the above controls. 我已经阅读了有关IEditableObject和Filters的信息,但不确定如何使用上述控件来实现这一点。

Your situation may be different, but since the special record I want to make read-only is added programmatically and the other records are entered by the user, I could just change the BindingSource's Filter property to this: 您的情况可能有所不同,但是由于我要以只读方式添加的特殊记录是通过编程方式添加的,而其他记录是由用户输入的,因此我可以将BindingSource的Filter属性更改为:

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

and then the special record will not be displayed. 然后特殊记录将不会显示。

However, I really want to display the record, just not allow changes or deletion. 但是,我真的想显示记录,只是不允许更改或删除。

The relationship between the BindingSource and the BindingNavigator is explained in the Remarks in the BindingNavigator Class document. BindingNavigator类文档中的备注中说明了BindingSource和BindingNavigator之间的关系。

However, I downloaded the BindingNavigator.cs source code in C# .NET and found these BindingNavigator button event handlers: OnMoveFirst, OnMovePrevious, OnMoveNext, OnMoveLast, OnAddNew, OnDelete, and each does a little more than just calling the related function in the BindingSource. 但是,我在C#.NET中下载了BindingNavigator.cs源代码,并找到了以下BindingNavigator按钮事件处理程序:OnMoveFirst,OnMovePrevious,OnMoveNext,OnMoveLast,OnAddNew,OnDelete,并且每个功能都不仅仅是在BindingSource中调用相关函数。

Here is one of interest: 这是有趣的一项:

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

We can see the Validate() function, but it suffers from less than great developer documentation: 我们可以看到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);
    }

Bit hey, since it's public, I could call it too. 有点嘿,既然是公开的,我也可以称呼它。 They all finish up with: 他们都完成了:

    /// <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);
        }
    } 

So I came up with the following to prevent deletion, which works: 因此,我提出了以下防止删除的方法,该方法有效:

    // 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);
            }
        }
    }

Now, what about preventing changes? 现在,如何防止更改? I have added a click handler for the Save button: 我为“保存”按钮添加了一个点击处理程序:

    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);
        }
    }

However, at this point, the user can change a record and then click one of the move buttons. 但是,此时,用户可以更改记录,然后单击移动按钮之一。 Now it seems the TableAdapter? 现在看来是TableAdapter? gets updated, but the change never gets written back to the database without the Save button clicked. 进行更新,但是如果不单击“保存”按钮,更改永远不会写回到数据库。 Maybe we can do better here. 也许我们可以在这里做得更好。

Now, remember that Validate() function that all of the buttons called? 现在,还记得所有按钮都调用了Validate()函数吗? Looking at the other event handlers, we see they all call Validate: 查看其他事件处理程序,我们看到它们都调用Validate:

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

One might be led to think that the BindingNavigator Validating event might fire, and we could have displayed a MessageBox and set e.Cancel = true, but for some reason, it didn't fire on any button clicks. 可能会导致人们认为BindingNavigator Validating事件可能会触发,我们可以显示一个MessageBox并将其设置为e.Cancel = true,但是由于某些原因,它不会在单击任何按钮时触发。

The approach I finally took (a little kludgy looking) was to handle the TextChanged event for a key field of the special record and disable all the controls: 我最终采取的方法(有点笨拙)是处理特殊记录的关键字段的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;
                }
            }
        }
    }

If the only controls are TextBoxes, you may prefer to use "ctrl.ReadOnly = true" instead of "ctrl.Enabled = false". 如果唯一的控件是TextBoxes,则您可能更喜欢使用“ ctrl.ReadOnly = true”,而不是“ ctrl.Enabled = false”。

I'm still looking for possibly a simpler, more elegant way to catch changes and prevent them... 我仍在寻找一种可能更简单,更优雅的方式来捕获更改并阻止更改...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM