4

I'm building a basic WPF application using MVVM. I'm trying to get data about different power plants to display on a Datagrid. The program builds fine and displays the grid lines, but no data. I've seen lots of of similar questions and tried several things, but nothing seems to work. I'm wondering if the problem is just an easy thing I don't know to look for.

View:

public class Plant : INotifyPropertyChanged
{
    #region Properties

    private int plantID;
    public int PlantID
    {
        get { return plantID; }
        set
        {
            plantID = value;
            RaisePropertyChanged("PlantID");
        }
    }

    //...etc for all properties//
    #endregion

    void RaisePropertyChanged(string prop)
    {
        if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

ViewModel:

public class MainViewModel : ViewModelBase, IPlantViewModel
{
    private ObservableCollection<Plant> _plants = new ObservableCollection<Plant>();
    private ICommand _loadCommand;

    public MainViewModel()
    {
        _plants = GetPlants();
        _plants.Add(new Plant (){PlantID=1, Name="Orange Plant", Address="1111 Orange Road", PlantPhone="(314)456 7489",PlantFax="(593)202 6948", Contact="John Smith"});
        _plants.Add(new Plant() { PlantID = 2, Name = "Blue Plant", Address = "2222 Blue Lane", PlantPhone = "6368967483", PlantFax = "783279948", Contact = "Jane Doe" });
    }

    public ObservableCollection<Plant> Plants
    {
        get { return _plants; }
    }

    public ObservableCollection<Plant> GetPlants()
    {
        if (_plants == null || _plants.Count == 0)
            _loadCommand.Execute(_plants);

        return _plants;
    }
}

View:

    <UserControl 
    x:Class="Directory.MVVM.View.DirectoryView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:local="clr-namespace:Directory.ViewModel"
    Height="481" Width="708">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="21*"/>
        <ColumnDefinition Width="79*"/>
    </Grid.ColumnDefinitions>
    <DataGrid
        ItemsSource="{Binding Source=Plants}"
        Name="PlantGrid"
        HorizontalAlignment="Left" 
        Margin="82,54,0,0" 
        VerticalAlignment="Top" 
        Height="147" Width="515"
        AutoGenerateColumns="False" Grid.ColumnSpan="2">
        <DataGrid.DataContext>
            <local:MainViewModel />
        </DataGrid.DataContext>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Plant ID" Width="89" Binding="{Binding Path=PlantID,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
            <DataGridTextColumn Header="Name" Width="140" Binding="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay }" />
            <DataGridTextColumn Header="Address" Width="90"    Binding="{Binding Path=Address, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
            <DataGridTextColumn Header="Phone" Width="90" Binding="{Binding Path=PlantPhone, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
            <DataGridTextColumn Header="Fax" Width="60" Binding="{Binding Path=PlantFax,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
            <DataGridCheckBoxColumn Header="Contact" Width="58" Binding="{Binding Path=Contact, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
        </DataGrid.Columns>
    </DataGrid>

Code Behind for the view(Not sure if I should have more in here):

public partial class DirectoryView : UserControl

public DirectoryView()
{
    InitializeComponent();

}

Also, I'm using this as a reference:

http://wpfgrid.blogspot.com/2014/02/simple-observablecollection-wpf-mvvm.html

Yael
  • 1,566
  • 3
  • 18
  • 25
Urn_ee
  • 69
  • 1
  • 10

3 Answers3

2

Try changing your itemsource binding in your datagrid you have source not path. From

ItemsSource="{Binding Source=Plants}"

To

ItemsSource="{Binding Path=Plants}"

or

ItemsSource="{Binding Plants}"

Changing your binding will fix the issue where your data is not showing in the grid at least for the two plants with id's 1 and 2 which you are adding in your constructor of you mainviewmodel. You also have a call to GetPlants in your constructor which loads additional plants by calling execute on a command. We cannot see how you are adding the plants to the observablecollection. You may want to take a look at how you are adding these plants to the observablecollection in getplants. If commenting out

_plants = GetPlants();

in

MainViewModel

Causes you to see two plants you also have an issue in how you are adding items in the command execute method.

kmcnamee
  • 5,097
  • 2
  • 25
  • 36
  • Both of those options just removed my grid lines. Also there are no errors in the solution, but something pops up that says "TargetInvocationException was unhandled" ? – Urn_ee Mar 17 '15 at 21:49
  • @Urn_ee try removing/debugging the GetPlants method call in the MainViewModel constructor and if that doesn't work the properties in your Plant class other than the PlantId property, you don't have the code for them in the question so i don't know what they do. After fixing the binding error I see a grid with two plants id 1 and 2. – kmcnamee Mar 18 '15 at 00:15
  • Removing the GetPlants method worked with the binding fix. Thanks! – Urn_ee Mar 18 '15 at 14:18
1

if you set the DataContext like you did, your datagrid gets a new instance of your MainViewmodel every time the UserControl is loaded new.

    <DataGrid.DataContext>
        <local:MainViewModel />
    </DataGrid.DataContext>

so you have to change your itemssource first to

ItemsSource="{Binding Path=Plants}"

now all you have to check is how you "fill" your collection. if you do NOT use Add, Clear and Remove for alter your collection then you have to raise OnPropertyChanged() so WPF notice that there are changes.

public ObservableCollection<Plant> Plants
{
    get 
    {
        return _plants;
    }

    set 
    {
         _plants = value;
         OnPropertyChanged();
    }

}
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • Think you mean set not - get / get? Also a public set is not needed. see http://stackoverflow.com/questions/17183812/observablecollection-setter-isnt-firing-when-item-is-added – kmcnamee Mar 18 '15 at 13:57
  • yes i mean set :) and yes you just need OnPropertyChanged() when you do not use Add/Remove Clear to alter the collection and set the property to a new instance. – blindmeis Mar 18 '15 at 14:01
  • ya agreed for a new instance of the observablecollection the set would be used if the code to load plants in the command is doing a new this would fix that issue. – kmcnamee Mar 18 '15 at 14:07
0

Here's how I do it:

Window Constructor instantiates the view model (here I have it as a property but the instantiation can also be inside the constructor) and sets the DataContext to the ViewModel:

public partial class WinFilesTransmitted : Window
{
    static FTViewModel w = new FTViewModel();
    public WinFilesTransmitted()
    {
        InitializeComponent();
        DataContext = w;
    }
}

The DataGrid xaml then does this:

<DataGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
                        ItemsSource="{Binding DataContext.oFTrn, RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding UID}" Header="xID" Width="40"/>
        <DataGridTextColumn Binding="{Binding TransID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="TransID" Width="40"/>
        <DataGridTextColumn Binding="{Binding DocID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="DocID" Width="40"/>
        <DataGridTextColumn Binding="{Binding TransmittalName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="325">

So the ItemsSource of the datagrid is set to my observablecollection, and the columns are bound to the properties directly.

Paul Gibson
  • 622
  • 1
  • 9
  • 23