简体   繁体   中英

Two way binding in WPF trough LINQ to SQL for the property of a ListItem of a ListBox using a DataTemplate

I am quite new to C# and WPF. Despite the vast documentation, tutorials and previous questions that I have read, I can't seem to find my scenario covered.

What I have

I have a WPF Application in C# with a single Window containing a ListBox. The ListBox defines a DataTemplate to display its items with a TextBox for each ListItem.

I have a Microsoft SQL Express server on the same machine, which contains a database named Test with a view named Client which exposes the columns id INT PRIMARY KEY IDENTITY and name VARCHAR(50) NOT NULL of another table, and a stored procedure named Client_update(@id, @name) that sets the name column of the client whose id is @id via a simple UPDATE statement on the same table.

What I am trying to achieve

What I'm trying to achieve is to have the ListBox to be populated on window load, and to have changes to the data (which is contained in text boxes) persisted to the database (trough the stored procedure) by switching focus to another component of the GUI.

What I have done

I have added a new LINQ to SQL Classes element to my project using Visual Studio, which resulted in the creation of a DataContext class named DataClassesDataContext . I have used the OR Designer in Visual Studio to map the view to the Class Client and the stored procedure to the method Client_update(id, name) . Then I have set (using again, the GUI of the OR Designer) the primary key of the table Client to be id and its default edit method to be Client_update(id, name) (taking care of matching stored procedure and method's parameters).

My XAML code (file MainWindow.xaml.cs ) is:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="Window_Loaded">
    <ListBox x:Name="myListBox">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Path=name Mode=TwoWay}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

I have set the TextBox Text property to be bound to the name property of the source to be set on runtime in the code-behind shown later, on a TwoWay mode. The default Trigger for data updates on a TextBox should be OnLostFocus, so I'm not specifying it.

My code-behind (file MainWindow.cs ) is:

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Initialize the data context
            DataClassesDataContext dataContext = new DataClassesDataContext();

            //Define the query to retrieve the list of clients from the SQL Server
            IEnumerable<Client> clients = from c in dataContext.Client
                                          select c;

            /* Set the item source to be the result of the query defined earlier
             * (This also gets the query executed)
             */
            myListBox.ItemsSource = clients;
        }
    }
}

What I have obtained

At first, the application won't run. An unhandled exception of Type System.InvalidOperationException in System.Data.Linq.dll carrying the message "Incorrect AutoSync specification for member 'id'." is thrown.

Searching online I have found that, by default, the OR Designer sets the AutoSync specification to Always on the primary key column. Setting it on OnUpdate raises the same problem, while setting it to OnInsert or Never , solves the issue.

However, setting it on those two values lets my application function only partly, the data is displayed, but changes are not persisted onto the database. (I checked manually with Microsoft SQL Management Studio after editing the data on the application)

What are my questions

What is the AutoSync parameter and why is it causing an Exception to be thrown? Is this the correct way to define a two way binding in this scenario, or am I totally going in the wrong direction? If so, could you give me any tip pointing me to the right strategy?

PS I'm sorry if the code isn't highlighted properly, I have read and tried to use the syntax to apply the Prettify thing, but for some reason there is no option for XAML, plus even using XML and C# (cs) as language tags is not working, and I have no idea why.

EDIT #1: The highlighting was working, it just wasn't shown in the preview.

EDIT #2: Fixed some typos, the name of the view, stored procedure and classes are single-cased. (ie Client not Clients )

Try to define dataContext as global variable. Then whenever changes to objects from dataContext need to be submitted, call dataContext.SubmitChanges(); . Synchronization to database should be on demand, by calling SubmitChanges() method (too expensive to be real-time) :

DataClassesDataContext dataContext = new DataClassesDataContext();
......
private void ButtonSubmit_Click(object sender, RoutedEventArgs e)
{
    dataContext.SubmitChanges();
}

UPDATE : (as respoind to comment)

This is the way I know to persist changes to database using LINQ to SQL. DataContext works by tracking changes to entities retrieved from db, upon SubmitChanges invoked all changes sent back to db.

TwoWay mode binding doesn't refer to application-to-db, and db-to-application. It is [bound UI control property]-to-[binding source] and the reverse. When user changes name in TextBox, name property in Client entity will reflect that change, also if name in the entity changed from code, TextBox will display updated name. That's what two-way means in this context. But all of those changes are in application, not submitted to db until SubmitChanges get invoked.

I'm not fully aware of AutoSync property, you can refer to this SO answer and see if that help you to understand it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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