简体   繁体   中英

Bind multiple views to the same data context (WPF-MVVM)

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 (ie 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>

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():

There are some ways to resolve this:

Easy way

When you create the AddContact in the Main Window Button_Click, inyect 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

Inyect data on navigation

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

I hope this can help you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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