Using Entity Framork I have a many-to-many relationship. In WPF I have a form for creating items. The problem is that when I add sub-items the list is not updated automatically.
For the purposes of a sample I have the following that uses an in-memory SQLite database. The database has tables for posts and tags. The window just shows:
And a button for adding a tag. When an available tag is selected and the Add button is pushed, the tag is added to the third ListBox but not the second ListBox. Note that in the button click handler, I set the ItemsSource for the third ListBox to null then reset it. Since that works, I know that the tags are being added to the Tags collection but the second ListBox is just not being updated. How do I get it to be updated automatically?
The following is the DbContext and entities.
Code Snippet
internal class db : DbContext { public db(DbContextOptions options) : base(options) { } public virtual DbSet<Post> Posts { get; set; } public virtual DbSet<Tag> Tags { get; set; } } public class Post { public event PropertyChangedEventHandler? PropertyChanged; public long Id { get; set; } public string? Name { get; set; } public List<Tag>? Tags { get; set; } = new List<Tag>(); } public class Tag : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; public long Id { get; set; } private string? title = string.Empty; public List<Post> Posts { get; set; } = new List<Post>(); public Tag(string title) { this.title = title; } public string? Title { get { return title; } set { if (value != title) { title = value; NotifyPropertyChanged(); } } } private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
The following is the XAML.
<Window.Resources> <DataTemplate x:Key="TagsTemplate"> <TextBlock Text="{Binding Path=Title, Mode=TwoWay}"/> </DataTemplate> </Window.Resources> <Grid x:Name="theGrid"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="20,20,20,20"> <ListBox x:Name="AvailableTags" ItemTemplate="{StaticResource TagsTemplate}" IsSynchronizedWithCurrentItem="True"/> <Button x:Name="AddButton" Height="20" Margin="20,20,20,20" Click="AddButton_Click">Add</Button> <ListBox x:Name="SelectedTags" MinWidth="50" ItemTemplate="{StaticResource TagsTemplate}" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Tags, Mode=TwoWay}"/> <ListBox x:Name="SelectedTagsTest" MinWidth="50" ItemTemplate="{StaticResource TagsTemplate}" IsSynchronizedWithCurrentItem="True" Margin="20,0,0,0" ItemsSource="{Binding Path=Tags, Mode=TwoWay}"/> </StackPanel> </Grid>
The following is the code-behind.
public partial class MainWindow : Window { db? db = null; Post post = new Post(); public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { var conn = new SqliteConnection("DataSource=:memory:"); conn.Open(); var options = new DbContextOptionsBuilder<db>() .UseSqlite(conn) .Options; // db = new db(options); try { db.Database.EnsureCreated(); } catch (Exception ex) { MessageBox.Show($"Creation error: {ex.Message}"); return; } // theGrid.DataContext = post; db.Tags.Add(new Tag("Hobby")); db.Tags.Add(new Tag("Profession")); db.Tags.Add(new Tag("Housework")); db.Tags.Add(new Tag("Animals")); db.Tags.Add(new Tag("Shopping")); db.Tags.Add(new Tag("Computers")); // AvailableTags.ItemsSource = db.Tags.Local.ToObservableCollection(); } private void AddButton_Click(object sender, RoutedEventArgs e) { Tag? cat = AvailableTags.SelectedItem as Tag; if (cat == null) return; if (cat.Title == null) return; post.Tags.Add(new Tag(cat.Title)); SelectedTagsTest.ItemsSource = null; SelectedTagsTest.ItemsSource = post.Tags; } }