ListBox does not update entries automaticly when using binding - c#

Im having some trouble updating a listbox when new data is added to it. This is my code
The my listbox has
<ListBox Name="EmailList" ItemsSource="{Binding ListBoxData, Mode=TwoWay}"
And here is my program:
public partial class MainWindow : Window
{
string hostname = Properties.Settings.Default.pop_host;
int port = Properties.Settings.Default.pop_port;
bool useSsl = Properties.Settings.Default.pop_usessl;
string username = "recent:" + Properties.Settings.Default.username;
string password = Properties.Settings.Default.password;
// When this button is pressed the program starts a backgroundworker
// that begins the download of mails
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker getNewMail = new BackgroundWorker();
getNewMail.DoWork += newEmail;
getNewMail.RunWorkerAsync();
getNewMail.RunWorkerCompleted += updateList;
}
// The actual function that downloads the mails (Using OpenPOP)
private void newEmail(object sender, DoWorkEventArgs e)
{
List<Message> allEmail = FetchAllMessages(hostname, port, useSsl, username, password);
ListBoxData = new List<EmailEntry> { };
foreach (Message singleEmail in allEmail)
{
var mailData = new ListBoxDataClass { theMessage = singleEmail, truncate = 40 };
readyUpListBoxData(mailData);
ListBoxData.Add(new EmailEntry { from = mailData.displayName, subject = mailData.partOfBody, messageID = singleEmail.Headers.MessageId.ToString() });
}
}
public class ListBoxDataClass
{
public Message theMessage { get; set; }
public int truncate { get; set; }
public string partOfBody { get; set; }
public string displayName { get; set; }
}
// A function that does different things with the downloaded data
public void readyUpListBoxData(ListBoxDataClass data)
{
MessagePart theEmailTxt = data.theMessage.FindFirstPlainTextVersion();
string noLineBreaks = theEmailTxt.GetBodyAsText().ToString().Replace(System.Environment.NewLine, " ");
data.partOfBody = noLineBreaks.Length <= data.truncate ? noLineBreaks : noLineBreaks.Substring(0, data.truncate) + " ..";
data.displayName = data.theMessage.Headers.From.DisplayName.ToString();
if (data.displayName == "")
{
data.displayName = data.theMessage.Headers.From.Address.ToString();
}
data.displayName += " <" + data.theMessage.Headers.From.Address.ToString() + ">";
}
}
I believe im supposed to use something called observable collection or something like that? I just cannot see how i can use that here in my program. I hope that some of you guys can assist me in the usage of Observable collection or point me to something else i can use, that does what i need.
I was thinking about using something like timer to achieve it, but im not sure that it would be good practice ?

Create public property for your ListBoxData as follows:
public partial class MainWindow : Window
{
public ObservableCollection<EmailEntry > ListBoxData{get;set;}
public MainWindow()
{
ListBoxData = new ObservableCollection<EmailEntry >();
InitializeComponents();
}
private void newEmail(object sender, DoWorkEventArgs e)
{
List<Message> allEmail = FetchAllMessages(hostname, port, useSsl, username, password);
foreach (Message singleEmail in allEmail)
{
var mailData = new ListBoxDataClass { theMessage = singleEmail, truncate = 40 };
readyUpListBoxData(mailData);
ListBoxData.Add(new EmailEntry { from = mailData.displayName, subject = mailData.partOfBody, messageID = singleEmail.Headers.MessageId.ToString() });
}
}

You can either implement INotifyPropertyChanged and then raise ListBoxData property when you're done adding or just change your ListBoxData to ObservableCollection because it implements INPC by default.

Related

WPF Set attributes back after a event is finished

so what i would like to is setting back some attributes after an Custom Event is finished.
Scenario i have a save BackupDrives Class that does collection of data and then offers a Event to be called after its done.
Changing object properties can be done by button click, what i would like is to set them back to the same value after the event is finished.
Button click does the thing :
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.SaveDrives += OnSavingDrives;
backup.DrivesSave();
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
Now the triggered event method is not able to change the properties back.
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(....);
File.WriteAllLines(e.Something, e.List2ToSave);
File.WriteAllLines(e.Something_lse, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
How do i do this. Since this does not work.
And on other thig here to - when i run the GUI i need to click 2 times one the same button to start it all. Done Code Clean + Rebuild. Still the same.
---EDIT---
As for code here you go.
This is a Class for collecting method and event.
public class DrivesEventArgs : EventArgs
{
string MYDOC = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
const string backup_Drives = "....";
const string backup_Letters = "...";
public List<string> List1ToSave = new List<string>();
public List<string> List2ToSave = new List<string>();
public string SAVE_DRIVE_Letter
{
get
{
string name = Path.Combine(MYDOC, backup_Letters);
return name;
}
}
public string SAVE_DRIVE_Path
{
get
{
string name = Path.Combine(MYDOC, backup_Drives);
return name;
}
}
}
public class DrivesBackup
{
private const string path = "Network";
private List<string> drives_to_save = new List<string>();
private List<string> letters_for_drives = new List<string>();
private RegistryKey reg1, reg2;
public event EventHandler<DrivesEventArgs> SaveDrives;
public void DrivesSave()
{
var data = new DrivesEventArgs();
try
{
if (drives_to_save.Count == 0)
{
reg1 = Registry.CurrentUser.OpenSubKey(path);
string[] mounted_drives = reg1.GetSubKeyNames();
foreach (var drive in mounted_drives)
{ //get all UNC Paths from mounted_drives
string[] getUNC = { path, drive };
string fullUNC = Path.Combine(getUNC);
reg2 = Registry.CurrentUser.OpenSubKey(fullUNC);
string UNCPath = reg2.GetValue("RemotePath").ToString(); //getting UNC PATH
Console.WriteLine(UNCPath);
data.List1ToSave.Add(drive.ToString());
data.List2ToSave.Add(UNCPath);
OnSaveDrives(data);
}
}
}
catch (Exception er)
{
throw er;
}
}
protected virtual void OnSaveDrives(DrivesEventArgs eD)
{
SaveDrives?.Invoke(this, eD);
}
Now here is the MAINWINDOW WPF
public partial class MainWindow : Window
{
DrivesBackup backup = new DrivesBackup();
public MainWindow()
{
InitializeComponent();
}
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.DrivesSave();
backup.SaveDrives += OnSavingDrives;
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), #"SEG-Backup"));
File.WriteAllLines(e.SAVE_DRIVE_Path, e.List2ToSave);
File.WriteAllLines(e.SAVE_DRIVE_Letter, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
}
Now i do hope this would make some things more clear.

How can I get the usercontrol to pass the values from the form it is initiated in to another one?

I am creating this simple video game ordering application. I have create an userControl named productControl. Code:
public partial class productControl : UserControl
{
private string pName;
private float pPrice;
private string pDesc;
private string pImgUrl;
public productControl() => InitializeComponent();
public string getName
{
get => pName;
set
{
pName = value;
productName.Text = pName;
}
}
public float getPrice
{
get => pPrice;
set
{
pPrice = value;
productPrice.Text = pPrice.ToString("c");
}
}
public string getDescription
{
get => pDesc;
set
{
pDesc = value;
descriptionText.Text = pDesc;
}
}
public string getImage
{
get => pImgUrl;
set
{
pImgUrl = value;
prodImage.Load(pImgUrl);
prodImage.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
public int getProdId { get; set; }
private void buyButton_Click(object sender, EventArgs e)
{
MessageBox.Show(pPrice.ToString("c"));
orderConfirmation o = new orderConfirmation();
o.Show();
}
}
And a Windows Forms named accountMain. Which retrieves data from SQL and calls the productControl to fill it up.
private string productRetriever = "SELECT prodId, prodName, prodPrice, prodImg, prodDesc FROM [dbo].[products]";
private void accountMain_Load(object sender, EventArgs e)
{
createFunctions();
}
private void createFunctions()
{
connection.Open();
SqlCommand retProduct = new SqlCommand(productRetriever, connection);
panel1.Controls.Clear()
using (var reader = retProduct.ExecuteReader())
{
int count = 0;
while(reader.Read())
{
p[count] = new productControl();
p[count].Name = count.ToString();
p[count].getProdId = (int)reader[0];
p[count].getName = (string)reader[1];
p[count].getPrice = (float)reader[2];
p[count].getImage = (string)reader[3];
p[count].getDescription = (string)reader[4];
panel1.Controls.Add(p[count]);
count++;
}
}
connection.Close();
}
Above I am using a while loop to populate the panel1 with productControl inside of the accountMain Form. How can I get userControl to hide the accountForm and show another form (passing the values into the new form like title and price) when I click the "Buy" button which is inside the userControl.
Also I am new to implementing SQL into C# is it the correct way to populate the panel1 in accountMain using executeReader() or is there a better approach of retrieving data from SQL and showing it in userControl?

How can I pass data between windows in my C# WPF application?

I have a login form in the MainWindow of my WPF application. If the user logs in successfully, I want to open the HomeWindow. My problem is that I need to pass the adminID variable from the MainWindow to the HomeWindow. How can I do this?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
int errors = 0;
if (txtUsername.Text == "")
{
lblUsernameStatus.Content = "This field is required.";
errors = errors + 1;
}
if (txtPassword.Text == "")
{
lblPasswordStatus.Content = "This field is required.";
errors = errors + 1;
}
if (errors == 0)
{
Administrator TryLogin = new Administrator();
if (TryLogin.VerifyUser(txtUsername.Text, txtPassword.Text))
{
HomeWindow home = new HomeWindow();
int adminID = TryLogin.userID;
home.Show();
this.Close();
}
else
{
lblLoginStatus.Content = TryLogin.status;
}
}
}
PS: I haven't written anything in the HomeWindow.xaml.cs file.
Define an initializer in HomeWindow to accept the data you wish to send:
private int AdminID;
public HomeWindow()
{
InitializeComponent();
}
public HomeWindow(int adminID) : base()
{
AdminID = adminID;
}
Then you can just:
HomeWindow home = new HomeWindow(TryLogin.userID);
home.Show();
this.Close();
Declaring static variable would be the simplest and easiest way because once login is authorized, the value doesn't change until logged off(application is exited)
I also used a way of passing value through Window constructor but static variables are much easier to utilize many fixed data of the logged-in users like customized setting data for each users. I also have a WPF app and pass 11 values and utilize easiily everywhere inside application.
Declare as static variable in MainWindow like,
public static int adminID;
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
adminID= TryLogin.userID;
}
And usage in HomeWindow is like,
MainWindow.adminID
Hope this helps..
There are different ways to achieve this.
Here is an example using singleton
public class User
{
private static readonly User _instance;
private static object syncRoot = new Object();
public string Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string SomeOtherProperty { get; set; }
private User()
{
// Initialize defaults
}
public void Reset()
{
// Clear existing values
}
public static User Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new User();
return _instance;
}
}
}
}
}
On your login form:
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
int errors = 0;
if (txtUsername.Text == "")
{
lblUsernameStatus.Content = "This field is required.";
errors = errors + 1;
}
if (txtPassword.Text == "")
{
lblPasswordStatus.Content = "This field is required.";
errors = errors + 1;
}
if (errors == 0)
{
Administrator TryLogin = new Administrator();
if (TryLogin.VerifyUser(txtUsername.Text, txtPassword.Text))
{
User.Instance.Reset(); // Make sure old data is removed
User.Instance.Username = txtUsername.Text;
User.Instance.Password = txtPassword.Text;
HomeWindow home = new HomeWindow();
int adminID = TryLogin.userID;
home.Show();
this.Close();
}
else
{
lblLoginStatus.Content = TryLogin.status;
}
}
}
On your home form:
You can retrieve the User credentials using User.Instance just make sure you reset it on logoff.

C# meetings system

I'm creating a form to hold information from "meetings". The user will fill out info regarding title, location, startTime, endTime, notes, and a date. What I am currently working on is the "save changes" button which will:
clear all the TextBoxes.
store the input in an array.
display only the title in the ListBox.
when the title is clicked on in the ListBox, the info stored in that array element re-populates in the appropriate TextBoxes should the user wish to make changes.
I have completed #1, #2 and #3 I would appreciate any help for #4. I've pasted the coding below for your viewing.
public partial class CalendarForm : Form
{
int currentIndex;
int arraySize = 0;
Meeting[] meetingArray = new Meeting[100];
public CalendarForm()
{
InitializeComponent();
}
private void saveChangesButton_Click(object sender, EventArgs e)
{
meetingArray[arraySize] = new Meeting();
meetingArray[arraySize].title = textBoxTitle.Text;
meetingArray[arraySize].location = textBoxLocation.Text;
meetingArray[arraySize].startTime = textBoxStartTime.Text;
meetingArray[arraySize].endTime = textBoxEndTime.Text;
meetingArray[arraySize].notes = notesTextBox.Text;
currentIndex = arraySize;
arraySize++;
meetingListBox.Enabled = true;
textBoxTitle.Text = "";
textBoxLocation.Text = "";
textBoxStartTime.Text = "";
textBoxEndTime.Text = "";
notesTextBox.Text = "";
*edit* added these two lines which now add the title to the listBox
meetingListBox.Items.Add(meetingArray[currentIndex].title);
Controls.Add(meetingListBox);
}
}
public class Meeting
{
public string title;
public string location;
public string startTime;
public string endTime;
public string notes;
};
This is how I would refactor the class:
public partial class CalendarForm : Form
{
private List<Meeting> Meetings { get; set; }
public CalendarForm()
{
InitializeComponent();
Meetings = new List<Meeting>();
}
private void saveChangesButton_Click(object sender, EventArgs e)
{
try
{
Meeting meeting = CreateMeeting();
Meetings.Add(meeting);
meetingListBox.Add(meeting);
}
catch
{
//Add proper error handling here
}
}
private Meeting CreateMeeting()
{
return new Meeting()
{
Title = textBoxTitle.Text,
Location = textBoxLocation.Text
StartTime = DateTime.Parse(textBoxStartTime.Text),
EndTime = DateTime.Parse(textBoxEndTime.Text),
Notes = notesTextBox.Text,
};
}
}
//As Matt Burland answered already:
private void meetingListBox_SelectedIndexChanged(object sender, EventArgs e)
{
Meeting meeting = meetingListBox.SelectedItem as Meeting;
if (meeting != null)
{
textBoxTitle.Text = meeting.Title;
//...etc for all your other text boxes.
}
}
public class Meeting
{
public string Title { get; set; }
public string Location { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Notes { get; set; }
public override string ToString()
{
return Title;
}
}
I've made a number of changes, more notably the switch from an Array to a List<>. Lists are more flexible and provide better functionality. Unless you really really need to use arrays, I would stay away from them just to better safeguard against logic errors index out of bounds type issues.
Also, I personally believe that dates should be stored in the DateTime struct format, but that is again a matter of preference. Note that it would be prudent to sanitize/validate the inputs (especially the dates) before assigning it into the Meeting object.
The Meeting object now has properties instead of public fields. Properties are preferred in case you ever want to change how something is Get/Set.
Hope this helps.
I really recommend you look up data binding and learn how to do this properly, but if you want a quick and dirty solution (although, in the end, you'll find it's a lot more work), I would do something like this:
private void saveChangesButton_Click(object sender, EventArgs e)
{
Meeting m = new Meeting();
m.title = textBoxTitle.Text;
m.location = textBoxLocation.Text;
m.startTime = textBoxStartTime.Text;
m.endTime = textBoxEndTime.Text;
m.notes = notesTextBox.Text;
meetingArray[arraySize] = m;
currentIndex = arraySize;
arraySize++;
meetingListBox.Enabled = true;
textBoxTitle.Text = "";
textBoxLocation.Text = "";
textBoxStartTime.Text = "";
textBoxEndTime.Text = "";
notesTextBox.Text = "";
meetingListBox.Items.Add(m);
//Controls.Add(meetingListBox); // You don't need to keep adding the control every time!
}
Now in your Meeting class, I'd override ToString() to just return the title. The ListBox will just use the ToString() method of whatever you add to it by default.
To help with #4, you want to bind the SelectedIndexChanged event and then use the SelectedItem property, cast it back to a Meeting object (because it'll return an Object) and then use it to repopulate your various text boxes.
Something like:
private void meetingListBox_SelectedIndexChanged(object sender, System.EventArgs e)
{
Meeting m = meetingListBox.SelectedItem as Meeting;
if (m != null)
{
textBoxTitle.Text = m.title;
//...etc for all your other text boxes.
}
}

Trouble with C# and Combobox not loading list

I'm working on building a List using Combobox that when a Client is selected from the combo list, it loads a specific URL. Problem is the List is blank.
Below is the code, but I don't see what I'm missing but it could be simple oversight since this is the first time I'm building a Combobox like this.
public partial class Form1 : Form
{
List<MyClient> clients;
public Form1()
{
InitializeComponent();
clients = new List<MyClient>();
clients.Add(new MyClient { ClientName = "Client 1", UrlAddress = #"http://www.google.com" });
BindBigClientsList();
}
private void BindBigClientsList()
{
BigClientsList.DataSource = clients;
BigClientsList.DisplayMember = "ClientName";
BigClientsList.ValueMember = "UrlAddress";
}
private void BigClientsList_SelectedIndexChanged(object sender, EventArgs e)
{
MyClient c = BigClientsList.SelectedItem as MyClient;
if (c != null)
{
string url = c.ClientName;
Process.Start(url);
}
}
}
class MyClient
{
public string ClientName { get; set; }
public string UrlAddress { get; set; }
}
You're missing the last line: DataBind
private void BindBigClientsList()
{
BigClientsList.DataSource = ClientSize;
BigClientsList.DisplayMember = "ClientName";
BigClientsList.ValueMember = "UrlAddress";
BigClientsList.DataBind;
}
Your constructor should look like this
public Form1()
{
InitializeComponent();
List<MyClient> clients = new List<MyClient>();
clients.Add(new MyClient { ClientName = "Client 1", UrlAddress = #"http://www.google.com" });
foreach(MyClient client in clients)
{
BigClients.Items.Add(client);
}
}
This adds two things:
Add the object to the ComboBox. Without that, how is the item supposed to appear in the ComboBox?
Put the code after the InitializeComponent() statement. Otherwise, when you try to add the MyClient object to the ComboBox, it will throw a NullReferenceException.

Categories

Resources