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.
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'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.
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;
}
}
}
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 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.