usurpateur
usurpateur

Reputation: 3

Element added to ObservableCollection in ViewModel not displayed

Quite a newbie in the world of WPF. I've been hurting my eyes for several days now, looking for a solution, but didn't manage to make it right.

What I'm trying to achieve :

Seems quite simple, uh ? I manage to do it all in the MainWindow.xaml.cs (only using ObservableCollection, no 'PropertyChanged' yet), but I can't make it work in the ViewModel.

Now my code :  

Client.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataBindingTestInViewModel
{
    class Client
    {
        protected string _firstName;
        protected string _name;

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }
}

MainWindow.xaml

<Window x:Class="DataBindingTestInViewModel.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:DataBindingTestInViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <ItemsControl Grid.Row="0" ItemsSource="{Binding Clients}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Label Grid.Column="0" Content="{Binding FirstName}" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <Button Grid.Row="1" Click="Button_Click">Add a client</Button>
    </Grid>
</Window>

MainWindow.xaml.cs (create an instance of ViewModel + Button_Click event)

using System.Windows;

namespace DataBindingTestInViewModel
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ViewModel viewmodel = new ViewModel();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            viewmodel.AddClient();
        }
    }
}

ViewModel.cs

using System.Collections.ObjectModel;
using System.Windows;

namespace DataBindingTestInViewModel
{
    class ViewModel
    {
        private ObservableCollection<Client> _clients = new ObservableCollection<Client>();

        public ViewModel()
        {
            _clients.Add(new Client() { FirstName = "John", Name = "Doe" });
            _clients.Add(new Client() { FirstName = "Jane", Name = "Doe" });
            //MessageBox.Show("Constructeur du View Model appelé.");
        }
        public ObservableCollection<Client> Clients
        {
            get { return _clients; }
        }

        public void AddClient()
        {
            _clients.Add( new Client() { FirstName = "Donald", Name = "Trump" } );
            MessageBox.Show("First element : " + _clients[0].FirstName + "\n" +
                            "Third element : " + _clients[2].FirstName );

        }
    }
}

Results :

What I think happens :

The MainWindow data context is binded to the ViewModel's Client instance. I create and add element in a new instance called in MainWindow - and that's not the one used in the datacontext.

Questions :

I guess that'll be a piece of cake for y'all. Thank you !!

Upvotes: 0

Views: 373

Answers (1)

ASh
ASh

Reputation: 35656

you are correct about working with 2 instances (one created in xaml and another in code-behind). there 2 ways to fix the situation.

work with the instance created in xaml. to get reference to that instance cast DataContext to vm type:

public partial class MainWindow : Window
{
    ViewModel viewmodel;

    public MainWindow()
    {
        InitializeComponent();
        viewModel = (ViewModel)DataContext;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        viewmodel.AddClient();
    }
}

work with the instance created in code. set DataContext from code too:

public partial class MainWindow : Window
{
    ViewModel viewmodel = new ViewModel();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = viewmodel;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        viewmodel.AddClient();
    }
}

and remove xaml part:

<Window.DataContext>
    <local:ViewModel />
</Window.DataContext>

Upvotes: 1

Related Questions