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
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.
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.
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.
Related
I try to display the List I create on List Box and i don't know how
I have a class in which I add a new book to List
C# code
public class Manager
{
static List<Book> lstBook = new List<Book>();
public void AddBookM(int isbn, string author, string des, string name, float price, ushort quantity, DateTime dateTime, string edition)
{
Book book = new Book(isbn, author, des, name, price, quantity,dateTime, edition, new List<string> { "" });
lstBook.Add(book);
}
}
now in XAML i want to see the books in the List in the ListBox and in the AutoSuggestBox i want to search for books in the list and that this will open up possibilities for completion
XAML code
<AutoSuggestBox Name="SBSearchBtn" HorizontalAlignment="Center" Width="500" FontSize="20" BorderBrush="Black" Header="Search" PlaceholderText="Write here!" Margin="0,90,0,0" VerticalAlignment="Top" TextChanged="SBSearchBtn_TextChanged" QuerySubmitted="SBSearchBtn_QuerySubmitted" SuggestionChosen="SBSearchBtn_SuggestionChosen"/>
<ListBox Name="SearchList" Width="500" Margin="140,194,860,400" Background="WhiteSmoke" FontSize="25"/>
For displaying the items in the ListBox you need to do the following :
Firstly, you need to define a item template for your ListBox so that you can specify how your list items will look like. For simplicity I have only bound name and author properties from Book class.
XAML
<ListBox Name="SearchList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding author}"></TextBlock>
<TextBlock Text="{Binding name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I would recommend using a ObservableCollection instead of just using a List, since you want to update the list items based on what user searches.
private ObservableCollection<Book> lstBook = new ObservableCollection<Book>();
Next, in the Page's Loaded event (or any event that you might want based on your requirement) set the ItemSource of the ListBox to the ObservableCollection you created (listBook).
private void Page_Loaded(object sender, RoutedEventArgs e)
{
//Setting item source of the list box
SearchList.ItemsSource = lstBook;
//Adding an entry to lstBook
AddBookM(1, "auth", "des", "name", 2.55f, 10, DateTime.Now, "edition");
}
You should now see an entry added to the list view . The advantage of using ObservableCollection in this scenario is that whenever you make changes(add/remove) any item in the ObservableCollection<Book> lstBook your UI will automatically be updated.
Thus when you call AddBookM() again, you will see that the new entry is also added to the ListBox.
Hope this helps .
You need to write it manually:
lstBook.Items.Add(book); // of course this is working only a ToString command to the Book class
You didn't say what ListBox you were talking about so I just assumed you were talking about the ListBox in the Windows Forms library.
Hoped I helped you!
I want to be able to say:
Get the first textblock, then the first checkbox, both with the number 1 in their name.
Then if the checkbox is checked, then the textblock can be populated.
See code:
for (int i = 1; i < 10; i++)
{
TextBlock a = (this.FindName(string.Format("tb_{0}", i)) as TextBlock);
CheckBox b = (this.FindName(string.Format("ck_{0}", i)) as CheckBox);
if (b.IsChecked.HasValue)
{
if (a != null) a.Text = data.ArrayOfSensors[i].ToString();
}
else
{
if (a != null) a.Text = data.ArrayOfSensors[0].ToString();
}
}
So when the checkbox is enabled, the textblock will be populated with the index from the array.
Many thanks!
EDIT: A slightly better explanation:
The textblocks are named: tb_1, tb_2 etc
The Checkboxes are named: cb_1, cb_2 etc
The array is:
[0] 0
[1] 100
[2] 150
The number is what they all have in common. So I can use a for loop with i as a common variable for each. I also have about 50 textboxes and Comboboxes and don't want to write each one out individually.
EDIT: My ComboBoxes and Textblocks are created on Xaml code like this:
<CheckBox x:Name="Cb_1" Width="15" Height="15" Margin="349,53,127,164" IsChecked="True" />
<TextBlock x:Name="tb_1" Text="80" Height="20" Width="20" Margin="266,35,205,177" />
Its hard to answer without seeing what your XAML looks like, however it sounds like you may be trying to use WPF like it is WinFirms.
To build an interface like this in WPF, you should start by creating a custom class to hold your data, and then use an ItemsControl to render your collection of data.
For example, your class might look something like this
public class SensorData() : INotifyPropertyChanged
{
// should implement INotifyPropertyChanged of course
public string Text { get; set; }
public bool IsChecked { get; set; }
}
And an ObservableCollection<SensorData> might be rendered using an <ItemsControl> with a ItemsPanelTemplate containing both a CheckBox and a TextBox
<ItemsControl ItemsSource="{Binding MyCollectionOfSensorData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="{Binding IsChecked}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This will loop through the collection of SensorData objects, and render a CheckBox and TextBox for each one. If you want to do any manipulation of the data from the code-behind, you only need to modify the properties of the SensorData objects.
For example, you could have a loop that goes
for (int i = 0; i < MyCollectionOfSensorData.Length; i++)
{
SensorData item = MyCollectionOfSensorData[i];
if (item.IsChecked)
item.Text = data.ArrayOfSensors[i].ToString();
else
item.Text = "0";
}
And there would be no interaction with the UI objects at all.
i want to change textblock text in page initialize event
here is my xaml
<ListBox Margin="3,60,1,10" BorderThickness="2" Grid.Row="1" Name="lstAnnouncement" Tap="lstAnnouncement_Tap" Width="476" d:LayoutOverrides="VerticalMargin">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="thispanel" Grid.Row="1" Orientation="Horizontal" Height="120" Width="478" >
<StackPanel.Background>
<ImageBrush ImageSource="Images/Text-ALU.png" Stretch="Fill" />
</StackPanel.Background>
<Grid HorizontalAlignment="Left" Width="30" Margin="0,0,0,2" Background="#FF0195D5" Height="118">
<TextBlock x:Name="txtDate" TextWrapping="Wrap">
</TextBlock>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i want to change txtDate.Text using c# in code-behind but txtdate is not accessible in code behind so how to achieve it ?
The reason you're not able to access the txtDate object is because it's contained within the DataTemplate you're using for the ListBox. This isn't an error - the DataTemplate is being applied to every single item added to your ListBox.
Given that the ListBox creates, among other controls, a Grid containing a TextBlock with the name "txtDate", for every single item added to it, what would it mean to access the txtDate object? How would your program decide which of a (functionally) infinite number of txtDates associated with an identical number of ListBoxItems you meant when you referenced txtDate?
If you wanted to be able to easily change the content of txtDate, you'd want to bind the ItemsSource of your ListBox to a property in a ViewModel. The easiest way to do this would be to have that property be an IEnumerable containing a custom model type. This way, you could update the text property of that model and call NotifyPropertyChanged on the that property, and the UI would update to reflect the new data.
Here's an example:
public class YourViewModel
{
public List<YourModel> Models { get; set; }
}
public class YourModel : INotifyPropertyChanged
{
private string yourText;
public string YourText
{
get { return yourText; }
set
{
yourText = value;
NotifyPropertyChanged("YourText");
}
}
// add INotifyPropertyChanged implementation here
}
And then you'd want to bind the ItemsSource of the ListBox to YourViewModel's Models property, and the text of your TextBox to the YourModel's YourText property. Any time you change the YourModel.YourText property, it'll automatically update on the UI. I think it's probably subject to debate whether having your model implement INotifyPropertyChanged is proper MVVM, but I find it a lot easier in these cases than forcing the ViewModel to update every single model each time a change is made on one of them.
If you're not familiar with the MVVM pattern used with WPF, this might be a good start: MVVM example.
this function will help you... This will help u find the control inside of a listbox runtime..
public FrameworkElement SearchVisualTree(DependencyObject targetElement, string elementName)
{
FrameworkElement res = null;
var count = VisualTreeHelper.GetChildrenCount(targetElement);
if (count == 0)
return res;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targetElement, i);
if ((child as FrameworkElement).Name == elementName)
{
res = child as FrameworkElement;
return res;
}
else
{
res = SearchVisualTree(child, elementName);
if (res != null)
return res;
}
}
return res;
}
Here first parameter is parent and the second parameter is the name of the element which in your case is "txtDate".. hope it works!!
I'm looking for the best way to populate a check boxes from the following code. I have looked into Binding but not really sure where to go.
Here is the edited code that is working
private void dpDateSelection_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
DateTime? date = dpDateSelection.SelectedDate;
logDate = date != null ? date.Value.ToString("yyyy-MM-dd") : null;
dpDateSelection.ToolTip = logDate;
LoadLogs(logDate);
}
private void LoadLogs(string ldate)
{
string[] logs = Directory.GetFiles(logPath + ldate, "*.ininlog");
InitializeComponent();
logList = new ObservableCollection<String>();
logList.Clear();
foreach (string logName in logs)
{
string s = logName.Substring(logName.IndexOf(ldate) + ldate.Length + 1);
int extPos = s.LastIndexOf(".");
s = s.Substring(0, extPos);
logList.Add(s);
}
this.DataContext = this;
}
<ListBox x:Name="Logs" ItemsSource="{Binding logList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" ToolTip="{Binding}" Tag="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will want to start by using an ItemsControl instead of a StackPanel, since ItemsControls are automatically set up to display collections of things:
<ItemsControl ItemsSource="{Binding Logs}"/>
Note the use of ItemsSource. With the accompanying binding string, it basically says "Look for a property on the DataContext called "Logs" and put everything in it into this control".
Next you said you wanted this displayed as checkboxes, so we use an item template:
<ItemsControl ItemsSource="{Binding Logs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content={Binding .}/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This says "Use a checkbox for each Item in the ItemsSource". The DataTemplate can be a Grid or other collection control as well, so this is a really powerful feature in WPF. The "Binding ." just binds to the object itself (a string in this case, so we don't need a special path).
Finally, you need to set up a property to bind to in your view model:
ObservableCollection<String> Logs {get; set;}
You want an ObservableCollection so that when anything is added to or removed from the list, it automatically updates the UI. If you are going to be completely replacing the list (assignment), then you need to implement INotifyPropertyChanged and invoke the PropertyChanged event in that properties setter.
In your posted loop, you would add each log file to this property.
Also, make sure that somewhere you set the DataContext property of the XAML file (View) to your view model object. If everything is in code behind, use DataContext = this. Note that doing this is considered bad practice, and you should use a separate class (ViewModel).
You didn't mention what you wanted the CheckBoxes to do, so I haven't included anything related to that in my answer. You will likely want to abstract your logs into an object with a "Selected" property you can then bind the IsChecked property of the CheckBoxes to.
Obviously this is a lot to take in, please let me know if I can clarify anything or help further!
Update
You put the property in your ViewModel (DataContext). Whatever class that is, you write:
ObservableCollection<String> Logs {get; set;}
private void LoadLogs()
{
string[] logs = Directory.GetFiles(logPath + logDate, "*.ininlog");
foreach(string logName in logs)
{
string s = logName.Substring(logName.IndexOf(logDate) + logDate.Length + 1);
int extPos = s.LastIndexOf(".");
s = s.Substring(0, extPos);
//MessageBox.Show(s);
Logs.Add(s); //Add the parsed file name to the list
}
}
Simple question but I am totally confused. I am developing a wp7 app using C#. I want a listbox with input number of image item which source should be same i.e. the list box should contain 'n' Image control with source set to a single image where 'n' is number of Listbox Item enter by user. e.g. If the user input '10', then the listbox should have ten items. I want the listbox ItemsPanelTemplate as Wrap-panel. Can somebody suggest me how to get this?
Define a ListBox in your XAML something like this
<ListBox x:Name="ListBoxImages">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Imagesource}" Width="300"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and then set its Source in the code behind like this
int noOfImages = 10; //Take the input from user
List<ImageClass> imageList = new List<ImageClass>();
for(int i=0; i<noOfImages; i++)
imageList.Add(new ImageClass() { Imagesource = "/user.jpg" });
ListBoxImages.ItemsSource = imageList; //Set the source of the listbox here
where ImageClass is,
public class ImageClass
{
public String Imagesource { get; set; }
}
The above is a sample for your understanding. Please customize wisely to suit your needs