2

My question might be dumb but I'm pretty new to WPF and MVVM.

So let's take the following example:

I have a contactbook app which displays all contacts using a ObservableCollection<Contact> in the MainWindow-View.

When I press the 'add contact' button a new view opens which has text fields for the user to enter a new contact and a save-button. However when I save the entry it gets saved to a new Collection since I'm not in the same data context (both use the same ViewModel as DataContext).

My question is how do I share the ObservableCollection<Contact> with both views so both views can edit it and use the same collection (i.e. save new contact in a view and the list in main-view also gets updated)?

A few code snippets:

private Contact contact;
private ObservableCollection<Contact> _contacts;

public ContactManager()
{
    Contacts = new ObservableCollection<Contact>();
    contact = new Contact();
}

public ObservableCollection<Contact> Contacts
{
    get { return _contacts; }
    set
    {
        _contacts = value;
        RaisePropertyChangedEvent("Contacts");
    }
}

public String Anrede
{
    get { return contact.Anrede; }
    set
    {
        contact.Anrede = value;
        RaisePropertyChangedEvent("Anrede");
    }
}

public String Vorname
{
    get { return contact.Vorname; }
    set
    {
        contact.Vorname = value;
        RaisePropertyChangedEvent("Vorname");
    }
}

public String Nachname
{
    get { return contact.Nachname; }
    set
    {
        contact.Nachname = value;
        RaisePropertyChangedEvent("Nachname");
    }
}

public String Adresse
{
    get { return contact.Adresse; }
    set
    {
        contact.Adresse = value;
        RaisePropertyChangedEvent("Adresse");
    }
}

public String Telefonnummer
{
    get { return contact.Telefonnummer; }
    set
    {
        contact.Telefonnummer = value;
        RaisePropertyChangedEvent("Telefonnummer");
    }
}


public ICommand CreateTestContactCommand
{
    get { return new DelegateCommand(CreateTestContact); }
}

public ICommand AddNewContactCommand
{
    get { return new DelegateCommand(AddNewContact); }
}

private void CreateTestContact()
{
    var testContact = new Contact
    {
        Anrede = "Herr",
        Vorname = "Max",
        Nachname = "Mustermann",
        Adresse = "Mustermannstraße 13",
        Telefonnummer = "123456789"
    };

    Contacts.Add(testContact);
}

private void AddNewContact()
{
    Contacts.Add(contact);
}

and the XAMLs:

Main Window:

<Window
    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:ContactBook_Test"
    xmlns:ViewModels="clr-namespace:ContactBook_Test.ViewModels" x:Class="ContactBook_Test.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="488" Width="533" ResizeMode="NoResize">
<Window.DataContext>
    <ViewModels:ContactManager/>
</Window.DataContext>
<Grid>
    <Grid.DataContext>
        <ViewModels:ContactManager/>
    </Grid.DataContext>
    <Label Content="Kontaktbuch" HorizontalContentAlignment="Center" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontSize="36" FontFamily="Calibri" Width="498"/>
    <DataGrid ItemsSource="{Binding Contacts}" AutoGenerateColumns="True"  CanUserAddRows="false" HorizontalAlignment="Left" Height="327" Margin="10,69,0,0" VerticalAlignment="Top" Width="498"/>
    <Button Command="{Binding CreateTestContactCommand}" Content="Testkontakt hinzufügen" HorizontalAlignment="Left" Margin="198,415,0,0" VerticalAlignment="Top" Width="128"/>
    <Button Content="Neuer Kontakt" HorizontalAlignment="Left" Margin="16,31,0,0" VerticalAlignment="Top" Width="87" Click="Button_Click"/>

</Grid>

AddContact:

<Window
    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:ViewModels="clr-namespace:ContactBook_Test.ViewModels" x:Class="ContactBook_Test.Views.AddContact"
    mc:Ignorable="d"
    Title="AddContact" Height="300" Width="300">
<Window.DataContext>
    <ViewModels:ContactManager/>
</Window.DataContext>
<Grid>
    <Grid.DataContext>
        <ViewModels:ContactManager/>
    </Grid.DataContext>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="84,46,0,0" TextWrapping="Wrap" Text="{Binding Anrede}" VerticalAlignment="Top" Width="200" />
    <TextBox HorizontalAlignment="Left" Height="23" Margin="84,85,0,0" TextWrapping="Wrap" Text="{Binding Vorname}" VerticalAlignment="Top" Width="200"/>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="84,123,0,0" TextWrapping="Wrap" Text="{Binding Nachname}" VerticalAlignment="Top" Width="200"/>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="84,163,0,0" TextWrapping="Wrap" Text="{Binding Adresse}" VerticalAlignment="Top" Width="200"/>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="84,201,0,0" TextWrapping="Wrap" Text="{Binding Telefonnummer}" VerticalAlignment="Top" Width="200"/>
    <Button Command ="{Binding AddNewContactCommand}" Content="Speichern" HorizontalAlignment="Left" Margin="107,240,0,0" VerticalAlignment="Top" Width="75"/>
    <Label Content="Anrede" HorizontalAlignment="Left" Margin="12,43,0,0" VerticalAlignment="Top"/>
    <Label Content="Name" HorizontalAlignment="Left" Margin="12,82,0,0" VerticalAlignment="Top"/>
    <Label Content="Nachname" HorizontalAlignment="Left" Margin="12,120,0,0" VerticalAlignment="Top"/>
    <Label Content="Adresse" HorizontalAlignment="Left" Margin="12,159,0,0" VerticalAlignment="Top"/>
    <Label Content="Telefon" HorizontalAlignment="Left" Margin="12,198,0,0" VerticalAlignment="Top"/>

</Grid>

mm8
  • 163,881
  • 10
  • 57
  • 88
hullunist
  • 1,117
  • 2
  • 11
  • 31
  • Maybe post some code to help us to help you. However if you have set the datacontext of both views to the same view model, just add an ICommand to the "Add-Contact" button and in that command add the new entry to the ObservableCollection – Daniele Sartori Jun 20 '17 at 10:16
  • @DanieleSartori updated the question with the code – hullunist Jun 20 '17 at 10:27
  • 1
    Usually when you create new `Window` you just use `var newWnd = new AddContact(); newWnd.DataContext = this.DataContext;` – XAMlMAX Jun 20 '17 at 10:29

2 Answers2

2

You should use the same instance of the view model for both views. Remove this from both views:

<Grid.DataContext>
    <ViewModels:ContactManager/>
</Grid.DataContext>

The Grid will inherit the DataContext from the parent window. As of now you are creating two instances of the view model in each view.

Also remove this from the AddContact view:

<Window.DataContext>
    <ViewModels:ContactManager/>
</Window.DataContext>

...and set its DataContext when you open the window:

AddContact addContact = new AddContact();
addContact.DataContext = this.DataContext;
addContact.Show():
mm8
  • 163,881
  • 10
  • 57
  • 88
1

There are some ways to resolve this:

Easy way

When you create the AddContact in the Main Window Button_Click, inject or assign the ViewModel. For example:

AddContact(this.DataContext);

or

new AddContact(){DataContext=this.DataContext);

Events way

In a MVVM architecture, you can share information between ViewModels with events, see Sharin variables between different ViewModels

Inject data on navigation

Usually the methods that you use to navigate to a page, allow you to send data, for example NavigationService.Navigate

I hope this can help you.

Gudarzi
  • 486
  • 3
  • 7
  • 22
ganchito55
  • 3,559
  • 4
  • 25
  • 46