user3732456
user3732456

Reputation: 33

C# WPF DataBinding "Can't find Property"

I'm still trying to get my head around databinding in WPF and so I'm having a bit of a problem working out how to solve this issue for the past 10 hours:

I have an object named birdList which consist of a list of Bird Objects(bird1 and bird2). I have made all properties within the objects public. When I try to data bind the Common_Name property to a TextBlock I get nothing. Looking at the output in Visual Studio I see the following Errors/Warnings:

System.Windows.Data Warning: 60 : BindingExpression (hash=47755470): Default mode resolved to OneWay
System.Windows.Data Warning: 78 : BindingExpression (hash=47755470): Activate with root item 'birdList'
System.Windows.Data Warning: 108 : BindingExpression (hash=47755470):   At level 0 - for String.Bird found accessor <null>
System.Windows.Data Warning: 108 : BindingExpression (hash=47755470):   At level 0 - for EnumerableCollectionView.Bird found accessor <null>
System.Windows.Data Warning: 108 : BindingExpression (hash=47755470):   At level 0 - for Char.Bird found accessor <null>

System.Windows.Data Error: 40 : BindingExpression path error: 'Bird' property not found on 'object' ''String' (HashCode=-874795121)'.

System.Windows.Data Warning: 103 : BindingExpression (hash=47755470): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 88 : BindingExpression (hash=47755470): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=47755470): TransferValue - using final value ''

C# Code:

    public Birds birdList { get; set; }

    Bird bird1 { get; set; }
    Bird bird2 { get; set; }

public MainWindow()
    {

        InitializeComponent();

        birdList = new Birds();

        bird1 = new Bird();
        bird2 = new Bird();

        bird1.Common_Name = "Mallard";
        bird2.Common_Name = "Red-winged Blackbird";

        birdList.Bird.Add(bird1);
        birdList.Bird.Add(bird2);

        birdList.Test = "Testing!";

        this.DataContext = this;

        this.Photos = (PhotoCollection)(Application.Current.FindResource("Photos") as ObjectDataProvider).Data;
        this.Photos.Path = "E:\\Network Drive\\SarDonnie's Documents - Networked\\Birds\\Ducks, Geese, and Swans (Anatidae)\\Mallard\\Clear Lake Bay Park 1-18-16";

    }

public class Birds
{
    public string Test { get; set; }

    public List<Bird> _Birds = new List<Bird>();
    public List<Bird> Bird
    {
        get
        {
            return this._Birds;
        }
    }
}

public class Bird
{

    public string Common_Name { get; set; }

} 

XMAL:

        <Grid  Grid.Column="2">
            <ContentControl Content="birdList">
                <ContentControl.ContentTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Height="50" Text="{Binding Bird.Common_Name,diag:PresentationTraceSources.TraceLevel=High}">
                            </TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </ContentControl.ContentTemplate>
            </ContentControl>
        </Grid>

I have also tried this to test accessing the Test property inside the birdList Object:

<Grid  Grid.Column="2">
            <ContentControl Content="birdList">
                <ContentControl.ContentTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Height="50" Text="{Binding Test>
                            </TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </ContentControl.ContentTemplate>
            </ContentControl>
        </Grid>

My question is why can't I seem to find the birdList.Bird.Common_Name property or the birdList.Test property?

Thanks!

Also feel free to point me to some good sites that house information on WPF. I hate banging my head on this keyboard and getting no where!

Upvotes: 1

Views: 2793

Answers (2)

Doncot
Doncot

Reputation: 78

Your code has two problems:

  1. You need to tell the path of the "list" to xaml (via "ItemsSource" property).
  2. Use controls that accept collections such as ListBox.

Here's my code:

  • MainWindow.xaml.cs
public partial class MainWindow : Window
{
    public List<Bird> Birds { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        Birds = new List<Bird>();

        var bird1 = new Bird();
        bird1.CommonName = "Mallard";
        Birds.Add(bird1);

        var bird2 = new Bird();
        bird2.CommonName = "Red-winged Blackbird";
        Birds.Add(bird2);

        this.DataContext = this;
    }

    public class Bird
    {
        public string CommonName { get; set; }
    }
}
  • MainWindow.xaml
<Grid>
    <ListBox ItemsSource="{Binding Birds}" DisplayMemberPath="CommonName" />
</Grid>

EDIT

If you need to update from the code on runtime, follow Jai's advice. I only showed the minimal solution for the question.

EDIT2

Always remember to use properties in binding, or else it won't work.

public class Birds
{
    public string Test { get; set; }

    //Binding source must be a property!!
    public List<Bird> _Birds { get; set; } = new List<Bird>();
    public List<Bird> Bird
    {
        get
        {
            return this._Birds;
        }
    }
}
<Grid>
    <DataGrid ItemsSource="{Binding birdList._Birds}" />
</Grid>

Upvotes: 4

Jai
Jai

Reputation: 8363

Okay, there are quite a bit of problem out there.

Firstly, you have a list of birds, but you only have one TextBlock to display it. If you have manually made multiple TextBlock (which you didn't show us), then there is no point to make a list of birds, because it means that your view is aware exactly how many birds there are (in your viewmodel???). Therefore, Doncot is right to say that you should use a ListBox, or any other controls that allow you to display a list of items.

Secondly, if you want to use a ListBox (or something similar), you would need to have to consider your list of birds. Binding is always related to INotifyPropetyChanged, INotifyCollectionChanged and DependencyProperty. Creating a class just to hold your list does not seem necessary, unless you have other special requirements that we are not aware of.

Doncot is quite near there, but I want to touch a little more. Using a List<Bird> works fine, except that your ListBox would not know it needs to update itself when the list is modified later. This is because that List<Bird> does not notify the Binding engine when a change occurs.

If you implement this together with INotifyPropertyChanged, your binding would be able to trigger when the whole List<Bird> is being re-instantiated (i.e. Birds = new List<Bird>();). This is normally not what you want, either.

If you change the whole List<Bird> into ObservableCollection<Bird>, your binding will trigger when you do Birds.Add(new Bird());. This is because ObservableCollection<> is an implementation of INotifyCollectionChanged interface, which you may also choose to implement it on your own. Up to this point, your binding is pretty solid, except that it does not know when a Bird within that list is being modified.

Consider this:

Birds.ElementAt(0).Common_Name = "New Bird Name!";

The collection itself is not being modified, because it is still holding to the reference of the same Bird instance. Therefore, the Binding engine will not be aware of this change. To solve this, your Bird class needs to implement INotifyPropertyChanged as well, which is similar to what vishakh369 answered.

Upvotes: 1

Related Questions