简体   繁体   English

使用DataTemplate在WPF中通过LINQ to SQL进行双向绑定,以获取ListBox的ListItem的属性

[英]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. 我是C#和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. 我在C#中有一个WPF应用程序,其中包含一个包含ListBox的Window。 The ListBox defines a DataTemplate to display its items with a TextBox for each ListItem. ListBox定义了一个DataTemplate,用于为每个ListItem显示带有TextBox的项目。

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. 我在同一台机器上有一个Microsoft SQL Express服务器,它包含一个名为Test的数据库,其中包含一个名为Client的视图,它公开了另一个表的列id INT PRIMARY KEY IDENTITYname VARCHAR(50) NOT NULL ,以及一个名为Client_update(@id, @name)的存储过程Client_update(@id, @name) id ,@ name Client_update(@id, @name)通过同一个表上的简单UPDATE语句设置id@id的客户端的name列。

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. 我想要实现的是在窗口加载时填充ListBox,并通过将焦点切换到另一个组件来对数据(包含在文本框中)的更改持久保存到数据库(通过存储过程)的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 . 我使用Visual Studio向项目中添加了一个新的LINQ to SQL Classes元素,从而创建了一个名为DataClassesDataContext的DataContext类。 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) . 我使用Visual Studio中的OR Designer将视图映射到Class Client ,将存储过程映射到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). 然后我设置(再次使用OR Designer的GUI)表Client的主键为id ,其默认编辑方法为Client_update(id, name) (负责匹配存储过程和方法的参数)。

My XAML code (file MainWindow.xaml.cs ) is: 我的XAML代码(文件MainWindow.xaml.cs )是:

<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. 我已将TextBox Text属性设置为绑定到要在运行时在稍后显示的代码隐藏中设置的源的name属性,在TwoWay模式下。 The default Trigger for data updates on a TextBox should be OnLostFocus, so I'm not specifying it. TextBox上的数据更新的默认触发器应该是OnLostFocus,所以我没有指定它。

My code-behind (file MainWindow.cs ) is: 我的代码隐藏(文件MainWindow.cs )是:

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'." System.Data.Linq.dll中类型System.InvalidOperationException的未处理异常,其中包含“成员'id'的错误AutoSync规范”。 is thrown. 被扔了。

Searching online I have found that, by default, the OR Designer sets the AutoSync specification to Always on the primary key column. 在线搜索我发现,默认情况下,OR Designer会将AutoSync规范设置为主键列上的Always Setting it on OnUpdate raises the same problem, while setting it to OnInsert or Never , solves the issue. OnUpdate上设置它会引发同样的问题,同时将其设置为OnInsertNever ,可以解决问题。

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) (我在编辑应用程序上的数据后用Microsoft SQL Management Studio手动检查)

What are my questions 我有什么问题

What is the AutoSync parameter and why is it causing an Exception to be thrown? 什么是AutoSync参数以及为什么会引发异常? 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. PS我很抱歉,如果代码没有正确突出显示,我已阅读并尝试使用语法来应用Prettify,但由于某种原因,XAML没有选项,甚至使用XML和C#(cs)作为语言标签不起作用,我不明白为什么。

EDIT #1: The highlighting was working, it just wasn't shown in the preview. 编辑#1:突出显示正在工作,它只是未在预览中显示。

EDIT #2: Fixed some typos, the name of the view, stored procedure and classes are single-cased. 编辑#2:修正了一些拼写错误,视图的名称,存储过程和类是单一的。 (ie Client not Clients ) (即Client不是Clients

Try to define dataContext as global variable. 尝试将dataContext定义为全局变量。 Then whenever changes to objects from dataContext need to be submitted, call dataContext.SubmitChanges(); 那么只要改变从对象dataContext需要提交,调用dataContext.SubmitChanges(); . Synchronization to database should be on demand, by calling SubmitChanges() method (too expensive to be real-time) : 应该按需要通过调用SubmitChanges()方法同步到数据库(实时太昂贵):

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

UPDATE : (as respoind to comment) 更新:( 作为评论的respoind)

This is the way I know to persist changes to database using LINQ to SQL. 这是我所知道的使用LINQ to SQL持久更改数据库的方式。 DataContext works by tracking changes to entities retrieved from db, upon SubmitChanges invoked all changes sent back to db. DataContext通过跟踪对从db检索的实体的更改来工作,当SubmitChanges调用发送回db的所有更改时。

TwoWay mode binding doesn't refer to application-to-db, and db-to-application. TwoWay模式绑定不涉及application-to-db和db-to-application。 It is [bound UI control property]-to-[binding source] and the reverse. 它是[绑定UI控件属性] -to- [绑定源],反之亦然。 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. 当用户在TextBox中更改名称时,Client实体中的name属性将反映该更改,如果实体中的名称从代码更改,TextBox将显示更新的名称。 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. 但所有这些更改都在应用程序中,在调用SubmitChanges之前不会提交给db。

I'm not fully aware of AutoSync property, you can refer to this SO answer and see if that help you to understand it. 我不完全了解AutoSync属性,你可以参考这个SO答案 ,看看是否有助于你理解它。

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

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