views:

152

answers:

3

Hi,

So I have a listbox that shows the subject of an email (I use the chilkat imap client) when I select the subject of an email I want to show the message body in a textbox but i cant figure out how to do it, obviusly i use the listbox selectindexchanaged event but how would i go about it

Code So Far

// Create an object, connect to the IMAP server, login,
        // and select a mailbox.
        Chilkat.Imap imap = new Chilkat.Imap();
        imap.UnlockComponent("UnlockCode");
        imap.Connect("Imap URL");
        imap.Login("email address", "password");
        imap.SelectMailbox("Inbox");

        // Get a message set containing all the message IDs
        // in the selected mailbox.
        Chilkat.MessageSet msgSet;
        msgSet = imap.Search("ALL", true);

        // Fetch all the mail into a bundle object.
        Chilkat.EmailBundle bundle = new Chilkat.EmailBundle();
        bundle = imap.FetchBundle(msgSet);

        // Loop over the bundle and display the From and Subject.
        Chilkat.Email email;
        int i;
        for (i = 0; i < bundle.MessageCount - 1; i++)
        {

            email = bundle.GetEmail(i);
            listBox1.Items.Add(email.From + ": " + email.Subject);
            textBox1.Text = email.Body ;

        }

        // Save the email to an XML file
        bundle.SaveXml("bundle.xml");



        // Disconnect from the IMAP server.
        // This example leaves the email on the IMAP server.
        imap.Disconnect();
    }
}

thanks in advance

+1  A: 

In your xaml set up the listboxes to bind to the properties you'd like, and set up event handlers for when the selection changes.

    <StackPanel>
        <ListBox Name="listbox1" SelectionChanged="listbox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=From}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <ListBox Name="listbox2" SelectionChanged="listbox_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Subject}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBox Name="textbox1"></TextBox>
    </StackPanel>

Then in your code behind. bind the listboxes to a list of the email objects.

        listbox1.ItemsSource = emails;
        listbox2.ItemsSource = emails;

finally you need to handle the event from the listboxes.

    private void listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListBox listbox = (ListBox)sender;
        foreach (Email email in listbox.SelectedItems)
        {
            textbox1.Text = email.Body;
        }
    }

Please note this code is not tested.

eoldre
sorry couldnt get it to work ne other ideas
Shane121
In which way did it not work? did it throw an exception? If so, what was the message?
eoldre
+2  A: 

Assuming that the email indexes stay the same (I think the safest way to make sure of that would be to cache the fetched bundle in the form), I'd change to using a ListView instead of the ListBox and then I'd add the indexes to the list, either as a separate column or in the Tag of the items.

After you'd set up the ListView to look as you need it to look (ListView.View = View.Details; and ListView.MultiSelect = false; are probably the main ones) instead of:

listBox1.Items.Add(email.From + ": " + email.Subject);

you could do something like (if you do it the Tag way, which is slightly easier but some people think is bad):

listView1.Items.Add(email.From + ": " + email.Subject).Tag = i;

And then when the user selects a subject in the list, as you say, you handle the ListView.SelectedIndexChanged event and then just do something like:

if(ListView.SelectedItems.Count > 0)
{
    textBox1.Text = bundle.GetEmail((int)ListView.SelectedItems[0].Tag).Body;
}

Or if you're sure you only ever want to get out the text from the emails, you could insert the texts into the tags instead of the indexes.

ho1
no sorry cant get it to work
Shane121
could you explain it a little easier im new to c# so i dont get what your saying thanks for the help though
Shane121
@Shane: Have you changed from a `ListBox` to a `ListView` in your form? And named the `ListView` listView1? And then changesd the `View` and `MultiSelect` properties as I describe in my answer above? After that you'll have to add a `Column` to the `ListView` (you can do that by going to the `Columns` property on it . After that hopefully the rest of the code makes sense.
ho1
thanks for all your help ut i still get this error Error Non-invocable member 'System.Windows.Forms.ListView.SelectedItems' cannot be used like a method.
Shane121
@Shane: My mistake, too used to writing VB.Net at the moment so I used `(` and `)` when it should have been `[` and `]`. See my updated answer above.
ho1
@ho1: Thanks for all you help i really appreaciate it but I still cant get it now i get this error "Cannot implicitly convert type 'Chilkat.Email' to 'string'"
Shane121
@shame: Try again, was missing a .Body
ho1
@ho1: your code worked great but now the only problem is i have to put the above code in the slected index changed event otherwise i get the error "bundle" does not exsist, this wouldnt be a prob but it creates dublicates of the emails in the inbox every time is there any way to stop remove this error
Shane121
@shane: Not sure what you mean with inbox? Do you mean that it creates duplicates in the actual email inbox?
ho1
@ho1: I was able to solve the dublicate part myself, but i have to put all the code in the slected index changed event otherwise i get the error above is there a way to fix that
Shane121
Anyone got able to solve that prob
Shane121
+1  A: 

Your issue is not with the email but with how you are displaying items in your form. You are trying to do things in a winforms way, which is fine for winforms (kind of) but really is pointless and code-heavy in WPF. You should do some reading about MVVM (plenty of questions here on the subject).

Here's a demo showing what you want to do using only a few lines of code that takes advantage of the binding infrastructure of WPF. You can create a new WPF app and copypaste the few lines (change my namespace and class names to match the app you create!) and see it in action.

There is one window. I'm simulating emails here; you'd get your emails and dump them in the collection:

public partial class MainWindow : Window
{
    public ObservableCollection<FakeEmail> Emails { get; private set; }

    public MainWindow()
    {
        Emails = new ObservableCollection<FakeEmail>();
        // simulates emails being received; you would popoulate with valid emails IRL
        Emails.Add(new FakeEmail 
            { From = "herp", Subject = "derp", Message = "herp derp" });
        Emails.Add(new FakeEmail 
            { From = "foo", Subject = "bar", Message = "foo bar" });
        Emails.Add(new FakeEmail 
            { From = "Binding", Subject = "Rocks", Message = "Binding rocks" });
        InitializeComponent();
    }
}
/// <summary>
/// I don't have your libraries
/// </summary>
public sealed class FakeEmail
{
    public string From { get; set; }
    public string Subject { get; set; }
    public string Message { get; set; }
}

I've added an ObservableCollection of type FakeEmail to the window. OCs work well with binds, as the collection notifies binds when elements are added or removed.

Next, the window. Please note, I'm not showing the <Window definition here, but I have named the window emailClient!

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <ListBox
        x:Name="emailList"
        ItemsSource="{Binding Emails, ElementName=emailClient}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock
                        Text="{Binding From}" />
                    <TextBlock
                        Text="{Binding Subject}"
                        TextWrapping="NoWrap"
                        TextTrimming="CharacterEllipsis" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <TextBlock
        Grid.Column="1"
        Text="{Binding SelectedValue.Message, ElementName=emailList, FallbackValue='Select an email pls'}" />
</Grid>

Some of the finer notes: The ListBox's ItemsSource is bound to the ObservableCollection I defined on the window. The ListBox will listen for items coming and going in that collection, and use the DataTemplate to display UI for each item in the colleciton.

For each FakeEmail the ItemTemplate finds, it creates a new instance of the DataTemplate and contents, and sets the DataContext of the template to the FakeEmail instance. That means, within the DataTemplate I can simply bind against the properties of a FakeEmail instance and everything gets wired up at runtime.

The ListBox has a property called SelectedValue, which I can use to show the email message. When you select an item in the ListBox, SelectedValue is the instance from ItemsSource that is the DataContext of the DataTemplate; that which is currently displayed in that item in the UI. So, in order to show the currently selected email's message, I just need to bind against the ItemSource's SelectedValue's Message property, since SelectedValue will be the currently selected email.

And that's it. No listening, no "\r\n" BS. A couple binds and an Observable collection.

Will