locked
WPF - ADO.NET : Master/Detail - Detail DataGrid erasure on consolidation RRS feed

  • Question

  • I face a strange behevior when committing a new order for a customer in a Master/Details relationship through two DataGrids.

    When I add a customer in the last empty row of the Master DataGrid, and then add for that customer an order in the Details Datagrid on the last empty row of the control, on the commit event through a save button, the application erases the content of the Details DataGrid despate the fact that the orders DataView and DataTable have taken into acount the newly added child row.

    I follow the execution path and find out that the problem is tied to the update cascading rule of the customers table's TableAdapter, when updating is taking place in order to assign the ID of the customer to the foreigner key of the child order row in the RowUpdated event.

    Before execution of the commit phase
    Nom : Capture Avant.PNG
Affichages : 26
Taille : 18,1 Ko

    During the commit phase
    Nom : Capture Pendant.PNG
Affichages : 19
Taille : 31,2 Ko

    After the commit
    Nom : Capture Apres.PNG
Affichages : 19
Taille : 18,1 Ko

    I really don't know how to solve this problem ending searching helpfull links on internet:

    https://stackoverflow.com/questions/...-from-datagrid
    https://connect.microsoft.com/Visual...-binding-issue

    Code of the RowUpdated méthod :

            private void dataadapterClients_RowUpdated(object sender, OdbcRowUpdatedEventArgs args)
            {
                if (args.RecordsAffected > 0)
                {
                    if (args.StatementType == StatementType.Insert)
                    {
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Attibue un numero d'ordre au client nouvellement cree");
    
                        OdbcCommand cmdIdentity = new OdbcCommand("SELECT @@IDENTITY", args.Command.Connection);
                        cmdIdentity.Transaction = Transaction;
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Avant numerotation automatique du client");
                        int id = (int)(cmdIdentity.ExecuteScalar());
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Client_ID : " + id);
                        args.Row.Table.Columns["Client_ID"].ReadOnly = false;
                        args.Row.Table.DataSet.Tables["Commandes"].Columns["Client_ID"].ReadOnly = false;
                        MessageBox.Show("Stop ??????????????????");
                        args.Row["Client_ID"] = id;
                        args.Row.Table.Columns["Client_ID"].ReadOnly = true;
                        args.Row.Table.DataSet.Tables["Commandes"].Columns["Client_ID"].ReadOnly = true;
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Apres numerotation automatique du client");
                    }
                    if (args.StatementType == StatementType.Insert || args.StatementType == StatementType.Update)
                    {
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Recuperation de l'horodatage de la ligne client. Type d'ordre : " + args.StatementType);
    
                        int intCriteria = 0;
                        if (args.StatementType == StatementType.Insert)
                            intCriteria = (int)args.Row["Client_ID"];
                        else
                            intCriteria = (int)args.Row["Client_ID", DataRowVersion.Original];
                        OdbcCommand cmdStamp = new OdbcCommand("SELECT Stamp FROM Clients WHERE Client_ID = " + intCriteria, args.Command.Connection);
                        cmdStamp.Transaction = Transaction;
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Avant ExecuteScalar");
                        DateTime dteStamp = (DateTime)(cmdStamp.ExecuteScalar()); // DateTime.Now
                        Console.WriteLine("[DBWpf, ClientsTableAdapter] dataadapterClients_RowUpdated, Horodatage : " + dteStamp);
                        args.Row.Table.Columns["Stamp"].ReadOnly = false;
                        args.Row["Stamp"] = dteStamp;
                        args.Row.Table.Columns["Stamp"].ReadOnly = true; // true
                    }
                }
                else
                {
                    // args.Row.RowError = "Conflit d'acces concurrentiel optimiste rencontre"; // Concurrency Violation Encountered
                    // args.Status = UpdateStatus.SkipCurrentRow;
                }
            }

    The problem raises on execution of the instruction that follows the MessageBox.Show() which initializes the ID of the newly added customer and propages the update (Update Rule Cascade) to the child row.


    MessageBox.Show("Stop ??????????????????");
    args.Row["Client_ID"] = id;

    The "TwoWay Binding" of WPF should update the UI, but in this case, the content of the Details DataGrid is erased.

    That is why I would like to give here a download link to my project in order to let you see precisely the problem by executing the application I wrote, and may be able to figure out a solution to my problem.


    https://drive.google.com/file/d/0B64...ew?usp=sharing

    Thanks for your help
    .







    • Edited by rbmoman Monday, July 3, 2017 7:24 PM
    Thursday, June 22, 2017 12:17 AM

All replies

  • Hi,

    I download your demo, but it does not work, I download demo on connect site you shared. it works. I assume that this is the same problem.

    As you say, the "TwoWay Binding" of WPF should update the UI, this happened between ViewModel and UI, Not ViewModel And DataBase.  you still need save the changed data in to data by code. I suggest you use Entity Framework.

    By the way, It's best to keep code to a minimum to what pertains to the question, there is way too much code shown that as I see it has nothing to do with your question.

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    • Edited by Bob Ding Monday, June 26, 2017 9:24 AM
    Friday, June 23, 2017 9:25 AM
  • I have three pieces of architectural advice.

    1)

    Use entity framework rather than datatables, dataadapters etc.

    2)

    Use MVVM. Almost all development teams who work with wpf use MVVM.

    3)
    Don't allow users to edit directly in a datagrid.

    If you do then there are a host of potential problems and you need a stack of code to cover all the edge cases. Validation is very difficult to impose, let alone display failure messages.

    The way I'd approach this.

    Use a datagrid in read only mode to allow the user to select the record.

    Edit in a panel overlaying the window content and force the user to save or quite on the record before editing another. That way you can easily disable the save button whilst the entity is invalid, you have room to show validation failures AND it's easy to make your UI far richer. 


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Friday, June 23, 2017 2:31 PM
  • Hello,

    Thank you for having taken time to run my project. I am suprised to hear that it works. Would it be possible for you to upload here a snapshot of the resulting window alter the commit? What version of Visual Studio are you using?

    My project uses ADO.NET to post changes to the database and UI DataGrids are binded to DataViews of the Dataset through CollectionViewSources. So this should make the Job.

    The code here is part of ADO.NET processing and the problem raises, as I explained, when the ID of the current customer is updated after getting it from the database for the newly inserted customer.

    You can see the change of the customer ID after the commit phase, through the current row of the customers DataGrid.

    But, minwhile the details DataGrid is erased, and that should not happen. The added order for the current customer should remain in the orders DataGrid with the foreign key ID column up to date.

    I can't get it right




    • Edited by rbmoman Monday, July 3, 2017 7:25 PM
    Friday, June 23, 2017 10:51 PM
  • hi,

    >> as I explained, when the ID of the current customer is updated after getting it from the database for the newly inserted customer.

    Are you sure the database update successful? and how do you save or update the database? any code for  save or update, any button to execute. DataGrid can not submit update to database automatically.

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, June 26, 2017 9:40 AM
  • Hi Bob,

    Here is the link to my project https://drive.google.com/file/d/0B64...ew?usp=sharing

    If you run it, you'll be able to submit changes to the database through the save toolbar button. The code is in the clientsBindingNavigatorSaveItem_Click() méthode of the MainWindow class:

    ...
    tableAdapterManager.UpdateAll(this.dataSetDBWpf);
    ...





    • Edited by rbmoman Monday, June 26, 2017 6:05 PM
    Monday, June 26, 2017 1:52 PM
  • Well, to answer your question: no, not at all.

    Haven't you run the project before? because your first answer lets it belive. You can see how I deal with all the stuff from the project code.

    But I can tell you, If you do not have any idea about the problem I'm dealing with, it is worth answering.

    Thank you very much

    • Edited by rbmoman Tuesday, June 27, 2017 6:37 PM
    Tuesday, June 27, 2017 6:28 AM
  • HI,

    >> But I can tell you, If you do not have any idea about the problem I'm dealing with, it is worth answering.

    Ok, But I would still give you some suggestion.

    1. Maybe you should bind dynamicResources.

    Because StaticResource will only be assigned once and any changes to resource dictionary ignored.

    A DynamicResource defers looking up the resource until it is needed at runtime. It will update the target if the source resource dictionary is changed.

    2.Try to reload the data from Dataset to CollectionViewSources after you save data.

    If data have been saved correctly, but did not show on the UI, maybe you are missing steps to re-obtain a new dataSet from Database. CollectionViewSources not update

    Views do not change the underlying source collections.  If the source collection do not implements the INotifyCollectionChanged interface, the changes would not raised CollectionChanged event are propagated to the views.

    BTW, Do not post the same question multiple times in MSDN forum.

    Best REgards,

    Bob




    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    • Edited by Bob Ding Wednesday, June 28, 2017 4:41 AM
    Wednesday, June 28, 2017 4:41 AM
  • "Maybe you should bind dynamicResources"

    The static binding is Visual Studio's generation through the DataSources designer and the UI designer

    "re-obtain a new dataSet from Database"

    Well, I don't have to, the data are already in the dataset.

    "If the source collection do not implements the INotifyCollectionChanged interface, the changes would not raised CollectionChanged event"

    I thought Datasets were able to be binded to WPF controls. Could you confirm this fact?

    "Do not post the same question multiple times in MSDN forum"

    Sorry for that, I was just wondering may be by changing forum from WPF to .NET Framework, I will get more relevant answers.

    Thank you any way

    Wednesday, June 28, 2017 5:11 AM
  • Hi,

    >>I thought Datasets were able to be binded to WPF controls. Could you confirm this fact?

    Dataset is not implement INotifyCollectionChanged  interface. You need refresh dataset by manual in code behind.

    And you need  ObservableCollection<T> Class.

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, June 28, 2017 8:30 AM
  • You can bind to ADO.NET objects, such as DataTable. The ADO.NET DataView implements the IBindingList interface, which provides change notifications that the binding engine listens for.

    https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/binding-sources-overview

    Hello Bob,

    Are you sure for the manual refresh?

    • Edited by rbmoman Friday, June 30, 2017 4:34 AM
    Friday, June 30, 2017 3:47 AM
  • Hi,

    As far as I know, only implement the INotifyPropertyChanged interface to provide Change Notifications. If you bind your DataGrid to a DataTable, and then if you change the data in DataGrid, this data will be updated to the DataTable(TwoWay binding mode), however, if you change the data in the DataTable, and this data will not be updated to the DataGrid, because the DataTable has not completed the INotifyPropertyChanged interface.

    Maybe below article would help you.

    WPF and SQLite Database

    https://www.codeproject.com/Articles/153407/WPF-and-SQLite-Database

    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    • Edited by Bob Ding Wednesday, July 5, 2017 9:09 AM
    Wednesday, July 5, 2017 9:07 AM
  • Hi Bob,

    Thank you for your reply.

    In order to get it clear, I made a test with this code at the end of the commit phase. After modifying the current customer name, I click on the save button which initiates the update to the datasource and executes the code test.

    string strCriteria = "Client_ID = " + (int)(((DataRowView)(clientCommandesViewSource.View.CurrentItem))["Client_ID"]);
    DataRow[] datarowCommandes = dataSetDBWpf.Commandes.Select(strCriteria, null, DataViewRowState.CurrentRows);
    foreach (DataRow pRow in datarowCommandes)
    {
        pRow["Libelle"] = "test" + pRow["Libelle"] = "test" + (int)(((DataRowView)(clientCommandesViewSource.View.CurrentItem))["Client_ID"]);
    }

    This code updates the column Label (Libelle in french) of all the orders for the customer.

    Before the update

    After the update

    You can see that updates to DataRows of DataTables are propaged to DataRowViews of DataViews of those DataTables, which are last binded to CollectionViewSources, and finally toDataGrid control targets.

    Cannot give better proof of feasibility. So, that means the TwoWay binding mode works perfectly with dataset objects as binding sources of binding targets.

    • Edited by rbmoman Thursday, July 6, 2017 3:34 AM
    Wednesday, July 5, 2017 3:19 PM
    • Edited by rbmoman Friday, July 14, 2017 7:12 PM
    Wednesday, July 12, 2017 8:20 PM
  • Hi Bob,

    For your information on the fix to this issue:

    In a master/detail scenario using ADO data, changing the primary key of a master item cleared the display of the corresponding details, with no way to display them thereafter. This is fixed by default for apps targeting .NET Framework 4.7.2, and for all apps that set the app-context switch Switch.System.Windows.Data.DoNotUseFollowParentWhenBindingToADODataRelation to false. [457740, PresentationFramework.dll, Bug]

    https://github.com/Microsoft/dotnet-framework-early-access/blob/master/release-notes/build-3052/dotnet-build-3052-changes.md



    • Edited by rbmoman Sunday, February 11, 2018 7:26 PM
    Sunday, February 11, 2018 7:24 PM