【问题标题】:DataGrid and Observable Collection in WPFWPF 中的 DataGrid 和 Observable 集合
【发布时间】:2012-12-21 00:54:54
【问题描述】:

我的 WPF 应用程序中有一个如下所示的数据网格。

<Window x:Class="MyApp.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
    <DataGrid x:Name="dgTest"  ItemsSource="{Binding TestSource}" AutoGenerateColumns="False" >
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="125" >
                       <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                                      <TextBox Text="{Binding Column1}"></TextBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Width="500" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Column2}"></TextBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
         <Button Click="SaveButton_Click">Save</Button>
    </Grid>
</Window>

我使用以下代码绑定它。现在我的要求是当用户在数据网格内的这些文本框中输入一些文本并单击保存按钮时,它应该更新数据库。我怎样才能做到这一点?

namespace MyApp
{
    public partial class TestWindow: Window
    {
        private ObservableCollection<Test> _testSource
        public ObservableCollection<Test> TestSource
        {
            get
            {
                return _testSource;
            }
            set
            {
                _testSource = value;
                OnPropertyChanged("TestSource");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }

        public TestWindow()
        {
            InitializeComponent();
            TestSource= new ObservableCollection<Test>();
             string strConnString = Application.Current.Properties["connectionStr"].ToString();
             SqlConnection con = new SqlConnection(strConnString);
            SqlCommand cmd = new SqlCommand("SELECT Column1,Column2 FROM MyTable", con);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataTable dtTest = new DataTable();
            da.Fill(dtTest);
            foreach (DataRow row in dtTest)
            {
                Test cd = new Test();
                cd.Column1 = row["Column1"].ToString();
                cd.Column2 = row["Column2"].ToString();
                TestSource.Add(cd);
            }
            this.DataContext = this;
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
             // here I need to get the updated ObservableCollection, but now it is showing old data
             foreach Test t in TestSource)
            {
                string a = t.Column1;
                string b = t.Column2;
            }
        }
    }

    public class Test: 
    {
        public string Column1{ get; set; }
        public string Column2{ get; set; }
    }
}

谢谢

【问题讨论】:

  • 尝试将您的 ItemsSource-Binding 中的 Mode 设置为 TwoWay
  • 您好,开发者,您是否使用 MVVM 模式将 DB 值绑定到数据网格?回复晚了非常抱歉。我也在研究这个问题。谢谢

标签: wpf


【解决方案1】:

我想补充几点。
1)您不需要在您的 TestSource 属性上有一个设置器。您设置了一次此值,并且在设置 DataContext 之前,所以这是没有意义的。
2) 您没有在 Test 类上实现 INotifyPropertyChanged
3)您在 UI 线程上加载数据。这可能会导致应用程序在加载数据时冻结(变得无响应)。

尝试将 C# 代码更新为以下内容:

namespace MyApp
{
    public partial class TestWindow: Window
    {
        private ObservableCollection<Test> _testSource = new ObservableCollection<Test>();


        public TestWindow()
        {
            InitializeComponent();

            //NOTE: this blocks the UI thread. Slow DB/Network will freeze the App while we wait. 
            // This should be done on a background thread.
            string strConnString = Application.Current.Properties["connectionStr"].ToString();
            SqlConnection con = new SqlConnection(strConnString);
            SqlCommand cmd = new SqlCommand("SELECT Column1,Column2 FROM MyTable", con);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataTable dtTest = new DataTable();
            da.Fill(dtTest);
            foreach (DataRow row in dtTest)
            {
                Test cd = new Test();
                cd.Column1 = row["Column1"].ToString();
                cd.Column2 = row["Column2"].ToString();
                TestSource.Add(cd);
            }
            this.DataContext = this;
        }

        public ObservableCollection<Test> TestSource { get { return _testSource; } }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            var rowIdx = 0;
            foreach(var t in TestSource)
            {
                string a = t.Column1;
                string b = t.Column2;

                Console.WriteLine("Row {0}, col1='{1}', col2='{2}'", rowIdx++, a, b);
            }
        }
    }

    public sealed class Test : INotifyPropertyChanged
    {
        private string _column1;
        private string _column2;

        public string Column1
        { 
            get{return _column1;}
            set
            {
                if(_column1!=value)
                {
                    _column1 = value;
                    OnPropertyChanged("Column1");
                }
            }           
        }
        public string Column2
        { 
            get{return _column2;}
            set
            {
                if(_column2!=value)
                {
                    _column2 = value;
                    OnPropertyChanged("Column2");
                }
            }           
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

您可能还想将绑定更新为双向,但我认为这是默认设置。

<TextBox Text="{Binding Column1, Mode=TwoWay}" />

【讨论】:

  • 我用 '' 尝试了你的方法,它成功了。谢谢 Lee
【解决方案2】:

DataGridTemplateColumn(或custom DataGrid.RowStyle)中创建您自己的用户界面时,DataGrid 会将您所有绑定上的UpdateSourceTrigger(即应更新基础数据的时间)更改为Explicit,如果您没有自己指定。

此处简要描述了该“功能”:5 Random Gotchas with the WPF DataGrid,尽管作者说无论您是否设置 UpdateSourceTrigger 都会发生这种情况,但您自己设置确实有效(至少在 .Net 4.0 中)。

使用LostFocus 模仿默认的TextBox 行为(以及CheckBox 上的PropertyChanged 等):

...
<TextBox Text="{Binding Column1, UpdateSourceTrigger=LostFocus}"></TextBox>
...
<TextBox Text="{Binding Column2, UpdateSourceTrigger=LostFocus}"></TextBox>
...

【讨论】:

  • 巨大的加一个我,我一直有这个确切的问题,我花了两天时间才找到这个!!!!!!
猜你喜欢
  • 2015-01-11
  • 2017-04-18
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
  • 2023-03-07
  • 2017-07-23
  • 1970-01-01
  • 2015-11-18
相关资源
最近更新 更多