简体   繁体   中英

LINQ Anonymous Type, Custom Class, ObservableCollection and WPF - how does it hang together?

I have a really simple class which I am populating with a LINQ query - all works well;

public class CatSummary : INotifyPropertyChanged
{
    private string _catName;
    public string CatName
    {
        get { return _catName; }
        set { if (_catName != value) { _catName = value; NotifyPropertyChanged("CatName"); } }
    }

    private decimal _catAmount;
    public decimal CatAmount
    {
        get { return _catAmount; }
        set { if (_catAmount != value) { _catAmount = value; NotifyPropertyChanged("CatAmount"); } }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // Used to notify Silverlight that a property has changed.
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            //MessageBox.Show("NotifyPropertyChanged: " + propertyName);

        }
    }

}

LINQ bit;

        var myOC = new ObservableCollection<CatSummary>();


        var initialQuery = BoughtItemDB.BoughtItems
                         .GroupBy(item => item.ItemCategory)
                         .Select(x => new CatSummary
                          { 
                              CatName = x.Key, 
                              CatAmount = x.Sum(amt => amt.ItemAmount)
                          });

        foreach (var item in initialQuery) myOC.Add(item);

I am trying to bind my WPF control to my custom class in the XAML below;

<ListBox x:Name="boughtItemsListBox" ItemsSource="{Binding CatSummary}" Margin="5,27,-35,100" Width="450" Height="371" Grid.ColumnSpan="2" Grid.RowSpan="2">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid HorizontalAlignment="Stretch" Width="440">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Column="0" Text="{Binding CatName, StringFormat=g}" TextWrapping="Wrap"  FontSize="{StaticResource PhoneFontSizeSmall}" VerticalAlignment="Top"/>
                <TextBlock Grid.Column="1" Text="{Binding CatAmount, StringFormat=\{0:C\}}" Margin="1" FontSize="{StaticResource PhoneFontSizeSmall}" VerticalAlignment="Top"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

This give me the following error; BindingExpression path error: 'CatSummary' property not found on 'MyApp.SpendAnalysis+CatSummary'

According to what I have read I think I need to make the properties of my class into ObservableCollection properties but this seems to break my LINQ query. I've tried all sorts for this, nor can I find any tutorials to help me understand how this should work. Any advice or pointers gladly received.

With your XAML bindings, the DataContext of boughtItemsListBox should be an item with a property named CatSummary of type IEnumerable<CatSummary> (or ObservableCollection<CatSummary> , or other implementer of INotifyCollectionChanged , if you want to be able to change the items that make up the list in real time). I'm guessing this is where you're going wrong.

Eg if you actually have something more like boughtItemsListBox.DataContext = myOC; , then it's looking for myOC.CatSummary and not finding anything; what you need to do is either change the XAML to ItemsSource="{Binding}" or change your code to boughtItemsListBox.ItemsSource = myOC; (and remove the now-useless ItemsSource setting in the XAML).

Assuming you have a MyOC property

private ObservableCollection<CatSummary> _myOC = new ObservableCollection<CatSummary>();
public ObservableCollection<CatSummary> MyOC
{
    get { return _myOC ; }
    set { if (_myOC != value) { _myOC = value; NotifyPropertyChanged("MyOC"); } }
}

bind your ListBox to the ObservableCollection , then each ListBoxItem will have a DataContext of type CatSummary .

<ListBox x:Name="boughtItemsListBox" ItemsSource="{Binding MyOc}" ...

In your LINQ query code

var initialQuery = BoughtItemDB.BoughtItems
                    .GroupBy(item => item.ItemCategory)
                    .Select(x => new CatSummary
                     { 
                         CatName = x.Key, 
                         CatAmount = x.Sum(amt => amt.ItemAmount)
                     });

reassign MyOC

MyOC = new ObservableCollection<CatSummary>(initialQuery.ToList());

or use the existing MyOC collection.

MyOC.Clear();
foreach (var item in initialQuery) MyOC.Add(item);

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