【问题标题】:WPF Automatic binding when an EF's entity values changeEF 的实体值更改时的 WPF 自动绑定
【发布时间】:2015-12-28 23:45:53
【问题描述】:

我是 Entity Framework 的新手,我正在努力学习它。

我试图修改官方文档中的一个练习:我想要一份父亲名单和一份儿子名单。每个儿子都必须从组合框菜单中选择一个父亲。

现在我可以做到这一点,但是,如果我添加一个父亲,我在父亲列表和组合框中都看不到它。如果我添加一个儿子,我不会在儿子的列表中看到这个儿子。

如果我关闭并重新打开程序,我会正确看到之前添加的父子。

如何自动升级组合框和列表视图中的数据?

我不喜欢调用函数来刷新,我想在数据库中更改某些内容时自动进行刷新。

我的项目由 3 个文件组成:

MainWindow.xaml

<Window x:Class="EF7Fam.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:EF7Fam"
        mc:Ignorable="d"
        Loaded="Page_Loaded"
        Title="MainWindow" Height="350" Width="550">

    <Grid>
        <StackPanel Orientation="Horizontal">
            <StackPanel Width="263" Margin="3 0 3 0">
                <TextBox Name="NewFT"></TextBox>
                <Button Click="Add_Click">Add</Button>
                <ListView Name="Fathers">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackPanel>
            <StackPanel Width="263" Margin="3 0 3 0">
                <TextBox Name="NewSN"></TextBox>
                <ComboBox Name="FT" DisplayMemberPath="Name" SelectedValuePath="FTId"/>
                <Button Click="Add_SN_Click">Add</Button>
                <ListView Name="Sons">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using Microsoft.Data.Entity;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace EF7Fam
{
    /// <summary>
    /// Logica di interazione per MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            using (var db = new Family())
            {
                db.Database.Migrate();
            }
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            using (var db = new Family())
            {
                Fathers.ItemsSource = db.Fathers.ToList();
                Sons.ItemsSource = db.Sons.ToList();
                FT.ItemsSource = db.Fathers.ToList();
            }
        }

        private void Add_Click(object sender, RoutedEventArgs e)
        {
            using (var db = new Family())
            {
                var ft = new Father { Name = NewFT.Text };
                db.Fathers.Add(ft);
                db.SaveChanges();
            }
        }

        private void Add_SN_Click(object sender, RoutedEventArgs e) 
        {
            using (var db = new Family())
            {
                var sn = new Son { Name = NewSN.Text, FTId = Int32.Parse(FT.SelectedValue.ToString()) };
                db.Sons.Add(sn);
                db.SaveChanges();
            }
        }
    }
}

模型.cs

using System;
using Microsoft.Data.Entity;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Runtime.CompilerServices;

namespace EF7Fam
{

    public class Family : DbContext
    {
        public DbSet<Father> Fathers { get; set; }
        public DbSet<Son> Sons { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename=Family.db");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Father>()
                .Property(b => b.Name)
                .IsRequired();
        }
    }

    public class Father : INotifyPropertyChanged
    {
        [Key]
        public int FTId { get; set; }
        public string Name { get; set; }

        public List<Son> Sons { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    public class Son : INotifyPropertyChanged
    {
        [Key]
        public int SNId { get; set; }
        public string Name { get; set; }

        public int FTId { get; set; }
        public Father Father { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我在这个网站和网络上查看了很多次,但没有找到解决方案。

如果有人可以帮助我让我知道我做错了什么,我将非常感激,

谢谢, 再见,

恩里科

【问题讨论】:

    标签: c# wpf entity-framework data-binding entity-framework-core


    【解决方案1】:

    要将您的控件绑定到DbSets,您需要这样做:

    private void Page_Loaded(object sender, RoutedEventArgs e)
    {
            using (var db = new Family())
            {
                db.Fathers.Load();
                Fathers.ItemsSource = db.Fathers.Local;
                db.Sons.Load();
                Sons.ItemsSource = db.Sons.Local;
                FT.ItemsSource = db.Fathers.Local;
            }
    }
    

    调用Load 方法,您将把现有对象加载到内存中,DbSet&lt;TEntity&gt;.Local 属性将为您提供一个ObservableCollection&lt;TEntity&gt;,其中包含当前跟踪的所有UnchangedModifiedAdded 对象给定DbSetDbContext。从ObservableCollection 添加或删除也将在DbSet 上执行相应的Add/Remove

    这样,如果您需要在视图中执行所有需要的操作后保存更改,您可以定义命令或覆盖视图中按钮的单击事件并调用上下文的 SaveChanges 方法:

      private void SaveChanges_Click(object sender, RoutedEventArgs e) 
      {
            using (var db = new Family())
            {
                db.SaveChanges();
            }
      }
    

    更新

    现在我看到你在使用 EF7,确实是我不知道这个版本的所有变化,但我可以告诉你有很多,也许这个属性还没有实现。我找到了一个question 询问这个问题,但尚未得到答复。

    在文档中挖掘更多我想我找到了一个solution,但事实是我不喜欢它,但它可以在Load 属性出现之前工作。根据 EF 文档,您可以这样做:

    private void Add_Click(object sender, RoutedEventArgs e)
    {
            using (var db = new Family())
            {
                var ft = new Father { Name = NewFT.Text };
                db.Fathers.Add(ft);
                db.SaveChanges();
                Fathers.ItemsSource = db.Fathers.ToList();
            }
     }
    

    如果我找到更好的解决方案,我会告诉你。

    【讨论】:

    • 非常感谢您的回答!!但我对这段代码有疑问:Visual Studio 说“DbSet 不包含定义,并且找不到接受 DbSet 类型的第一个参数的扩展方法 'Local'。”
    • 非常感谢,是的,这不是最好的解决方案,但它确实有效!我想我会尝试使用 EF6 的第一个解决方案!谢谢,再见,恩里科
    • 不客气 :) 我认为在 EF 7 的最终版本发布之前更改为 EF6 是一个明智的选择
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    相关资源
    最近更新 更多