Arno4Jackie
Arno4Jackie

Reputation: 331

Binding observable collection to listview wpf

I am making a email client for work and I can't get the observable collection to bind to my listview in the correct way. Please, advise as I can't find my exact problem online

C#

public partial class Mail : UserControl
{
    ObservableCollection<GetMails> BindMe = new ObservableCollection<GetMails>();
    public Mail()
    {
        InitializeComponent();
    }

    private void Grid_Loaded_1(object sender, RoutedEventArgs e)
    {
        BindMe = FetchAllHeaders(server, port, false, username, password);
        lstMail.ItemsSource = BindMe.ToList();
    }

    private ObservableCollection<GetMails> FetchAllHeaders(string hostname, int port, bool useSsl, string username, string password)
    {
        // The client disconnects from the server when being disposed
        using (Pop3Client client = new Pop3Client())
        {
            GetMails gm = new GetMails();

            // Connect to the server
            client.Connect(hostname, port, useSsl);

            // Authenticate ourselves towards the server
            client.Authenticate(username, password);

            // Get the number of messages in the inbox
            int messageCount = client.GetMessageCount();

            // We want to download all messages
            ObservableCollection<GetMails> getHeaders = new ObservableCollection<GetMails>();

            // Messages are numbered in the interval: [1, messageCount]
            // Ergo: message numbers are 1-based.
            // Most servers give the latest message the highest number
            for (int i = messageCount; i > 0; i--)
            {
                MessageHeader headers = client.GetMessageHeaders(i);
                RfcMailAddress from = headers.From;
                Message message = client.GetMessage(i);
                gm.Header = client.GetMessageHeaders(i).Subject;
                gm.From = from.MailAddress.ToString();
                int count = 0;
                foreach (MessagePart attachment in message.FindAllAttachments())
                {
                    count++; 
                }
                gm.NumberAttach = count;
                getHeaders.Add(gm);
            }


            // Now return the fetched messages
            return getHeaders;
        }
    }
}

XAML

<UserControl x:Class="VeriMail.Mail"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid Loaded="Grid_Loaded_1" Margin="0,0,-132,0">
    <ListView x:Name="lstMail" Height="399" VerticalAlignment="Top" Margin="0,0,-67,-99" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.CanContentScroll="True">
        <ListView.View>
            <GridView>
                <GridViewColumn Width="140" Header="Subject" />
                <GridViewColumn Width="140" Header="From" />
                <GridViewColumn Width="140" Header="Number of Attachments"/>
            </GridView>
        </ListView.View>
    </ListView>

</Grid>

The output I get when I do it like this is just VeriMail.GetMail all over

If I change the gridview to this:

<GridViewColumn Width="140" Header="Subject" DisplayMemberBinding="{Binding Header}"/>
<GridViewColumn Width="140" Header="From" DisplayMemberBinding="{Binding From}"/>
<GridViewColumn Width="140" Header="Number of Attachments" DisplayMemberBinding="{Binding NumberAttach}"/>

Then the output changes to the information I want but it copies one email the last one inserted into the observable collection.

Upvotes: 0

Views: 1429

Answers (3)

Gildor
Gildor

Reputation: 2574

UPDATE: The problem is when you call ToList the collection is copied and won't reflect changes in future. Remove that method call and it should work.

However it's better to bind ItemsSource in XAML than in code behind:

<ListView x:Name="lstMail" ItemsSource="{Binding BindMe, RelativeSource={RelativeSource AncestorType=UserControl}}" Height="399" VerticalAlignment="Top" Margin="0,0,-67,-99" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.CanContentScroll="True">
    <ListView.View>
        <GridView>
            <GridViewColumn Width="140" Header="Subject" />
            <GridViewColumn Width="140" Header="From" />
            <GridViewColumn Width="140" Header="Number of Attachments"/>
        </GridView>
    </ListView.View>
</ListView>

And also not change BindMe collection reference itself in FetchAllHeaders(), but instead modify it's contents. Otherwise the binding won't work because the reference is again changed.

Upvotes: 1

JimBobBennett
JimBobBennett

Reputation: 2159

As mentioned in another answer, its a usual practice to bind in the XAML (e.g. using the MVVM pattern). If you had done this, you may have found the bug...

lstMail.ItemsSource = BindMe.ToList();

When you call ToList() you create a brand new List<GetMails> containing the contents of the observable collection. When you add a new item, the list doesn't change - it's a copy and knows nothing about the original collection.

Try

lstMail.ItemsSource = BindMe;

and this should fix your problem.

Upvotes: 1

Bryan Stump
Bryan Stump

Reputation: 1439

lstMail.ItemsSource = BindMe.ToList();

should be

lstMail.ItemsSource = BindMe;

Binding to a list is different than binding to an ObservableCollection. Observable collection is the object to use for binding because it implements ICollectionChanged. List does not.

Upvotes: 0

Related Questions