WPF loading animation on a separate UI thread? (C#) - c#

Okay, I have a loading animation that runs while a large DataTable is populated to let the user know that the program has not frozen. I have the animation working fine, but it freezes while the DataTable is updatingv as well. Is there some way to have multiple UI threads, so that the animation will continue to run while the DataTable is loading information?
EDIT: Current code is below.
private void CreateFileTable()
{
file_data = new DataSet();
data_table = new DataTable();
file_data.Tables.Add(data_table);
DataColumn tempCol = new DataColumn("File Name", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Ext", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Size", typeof(string));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Created", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Modified", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Accessed", typeof(Label));
data_table.Columns.Add(tempCol);
tempCol = new DataColumn("Location", typeof(string));
data_table.Columns.Add(tempCol);
File_List.ItemsSource = file_data.Tables[0].DefaultView;
}
private void PopulateDirectories(string[] directories)
{
for (int i = 0; i < directories.Length; i++)
{
DirectoryInfo tempDirInfo = new DirectoryInfo(directories[i]);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempDirInfo.Name;
tempRow["Ext"] = "";
tempRow["Size"] = "";
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.CreationTime.ToLongDateString() + ", " + tempDirInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastWriteTime.ToLongDateString() + ", " + tempDirInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempDirInfo.LastAccessTime.ToLongDateString() + ", " + tempDirInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempDirInfo.FullName;
data_table.Rows.Add(tempRow);
}
}
}
private void PopulateFiles(string[] files)
{
for (int i = 0; i < files.Length; i++)
{
FileInfo tempFileInfo = new FileInfo(files[i]);
bool isSystem = ((File.GetAttributes(files[i]) & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
DataRow tempRow = data_table.NewRow();
tempRow["File Name"] = tempFileInfo.Name;
tempRow["Ext"] = tempFileInfo.Extension;
int fileSize = (int)tempFileInfo.Length;
if (fileSize > 1048576)
{
tempRow["Size"] = "" + fileSize / 1048576 + " MB";
}
else if (fileSize > 1024)
{
tempRow["Size"] = "" + fileSize / 1024 + " KB";
}
else
{
tempRow["Size"] = "" + fileSize + " B";
}
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.CreationTime.ToLongDateString() + ", " + tempFileInfo.CreationTime.ToLongTimeString();
tempRow["Created"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastWriteTime.ToLongDateString() + ", " + tempFileInfo.LastWriteTime.ToLongTimeString();
tempRow["Modified"] = tempLabel;
tempLabel = new Label();
tempLabel.Padding = new Thickness(2, 0, 2, 0);
tempLabel.Content = tempFileInfo.LastAccessTime.ToLongDateString() + ", " + tempFileInfo.LastAccessTime.ToLongTimeString();
tempRow["Accessed"] = tempLabel;
tempRow["Location"] = tempFileInfo.DirectoryName;
data_table.Rows.Add(tempRow);
}
}
}
private string GetSelectedPath(TreeViewItem selectedNode)
{
return selectedNode.Tag as string;
}
private void PopulateFileList()
{
PopulateDirectories(Directory.GetDirectories(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
PopulateFiles(Directory.GetFiles(GetSelectedPath((TreeViewItem)Dir_Tree.SelectedItem)));
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
PopulateFileList();
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
I have tried using the BackgroundWorker and calling the UpdateFileList() method inside it, but I'm not having any luck.
EDIT: Below is my code for the BackgroundWorker.
private BackgroundWorker bgWorker1;
private void InitializeBackgroundWorker()
{
bgWorker1 = new BackgroundWorker();
bgWorker1.DoWork += new DoWorkEventHandler(bgWorker1_DoWork);
bgWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker1_RunWorkerCompleted);
}
private void bgWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (Dispatcher.CheckAccess())
{
PopulateFileList();
}
else
{
Dispatcher.Invoke(new Action(() => PopulateFileList()));
}
}
private void bgWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TxtFoundCount.Text = "Files/Folders Found: " + File_List.Items.Count;
LoadingWheel.Visibility = System.Windows.Visibility.Hidden;
}
private void UpdateFileList()
{
LoadingWheel.Visibility = System.Windows.Visibility.Visible;
CreateFileTable();
bgWorker1.RunWorkerAsync();
}
I have a SelectionChanged event on a TreeView that calls InitializeBackgroundWorker() and UpdateFileList(). The list loads still, and I get no errors, but the loading animation never becomes visible.
Any help would be greatly appreciated.
Thanks.

There is only one UI thread. What you need to do is to load the data in the DataTable on a different thread.
If you want to show progress to the DataTable loading along the way (either directly, or through a ProgressBar or some other mechanism), the BackgroundWorker is a fairly straight-forward way to do that.
UPDATE: Very Simple Background Worker example
Here is a fairly simple example. It adds 100 random numbers to a collection, pausing the thread for a short time between each to simulate a long loading process. You can simply cut and paste this into a test project of your own to see it work.
The thing to notice is that the heavy lifting (the stuff that takes a while) is done in the DoWork, while all UI updates are done in ProgressChanged and RunWorkerCompleted. In fact, a separate list (numbers) is created in the DoWork handler because the global mNumbers collection is on the UI thread, and can't interact in the DoWork handler.
XAML
<Button x:Name="btnGenerateNumbers"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Generate Numbers" />
C# Code-Behind
BackgroundWorker bgWorker = new BackgroundWorker();
ObservableCollection<int> mNumbers = new ObservableCollection<int>();
public Window1()
{
InitializeComponent();
bgWorker.DoWork +=
new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged +=
new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
btnGenerateNumbers.Click += (s, e) => UpdateNumbers();
this.DataContext = this;
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Visibility = Visibility.Collapsed;
lstItems.Opacity = 1d;
btnGenerateNumbers.IsEnabled = true;
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
List<int> numbers = (List<int>)e.UserState;
foreach (int number in numbers)
{
mNumbers.Add(number);
}
progress.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Random rnd = new Random();
List<int> numbers = new List<int>(10);
for (int i = 1; i <= 100; i++)
{
// Add a random number
numbers.Add(rnd.Next());
// Sleep from 1/8 of a second to 1 second
Thread.Sleep(rnd.Next(125, 1000));
// Every 10 iterations, report progress
if ((i % 10) == 0)
{
bgWorker.ReportProgress(i, numbers.ToList<int>());
numbers.Clear();
}
}
}
public ObservableCollection<int> NumberItems
{
get { return mNumbers; }
}
private void UpdateNumbers()
{
btnGenerateNumbers.IsEnabled = false;
mNumbers.Clear();
progress.Value = 0;
progress.Visibility = Visibility.Visible;
lstItems.Opacity = 0.5;
bgWorker.RunWorkerAsync();
}

I wrote a little test program which shows the use of the Dispatcher class. It just requires a WPF-Window and a ListBox with Name "listBox". Should be easy to apply this solution to your problem.
public void Populate() {
// for comparison, freezing the ui thread
for (int i = 0; i < 1000000; i++) {
listBox.Items.Add(i);
}
}
private delegate void AddItemDelegate(int item);
public void PopulateAsync() {
// create a new thread which is iterating the elements
new System.Threading.Thread(new System.Threading.ThreadStart(delegate() {
// inside the new thread: iterate the elements
for (int i = 0; i < 1000000; i++) {
// use the dispatcher to "queue" the insertion of elements into the UI-Thread
// DispatcherPriority.Background ensures Animations have a higher Priority and the UI does not freeze
// possible enhancement: group the "jobs" to small units to enhance the performance
listBox.Dispatcher.Invoke(new AddItemDelegate(delegate(int item) {
listBox.Items.Add(item);
}), System.Windows.Threading.DispatcherPriority.Background, i);
}
})).Start();
}

Related

Access Object created in another method C#

In this code I'm creating Few DataGridViews. Number of those depends on file which within each launch of application will be different, so is number of DataGridViews.
How can I Access particular dataGridView grid[i] and modify it from which event Form1_UserAddedRow was called in that method?
Code:
public void Form1_Load(object sender, EventArgs e)
{
string[] lines = System.IO.File.ReadAllLines(#"..\..\Base.txt");
int diet_num = 0;
int grid_num = 0;
foreach (string x in lines) diet_num++;
grid_num = (diet_num / Constant.DATAGRID_DIETS_IN_GRID) + 1;
DataGridView[] grid = new DataGridView[grid_num];
for (int i = 0; i < grid_num; i++)
{
grid[i] = new DataGridView();
grid[i].Tag = i;
grid[i].Parent = this;
grid[i].Location = new Point(12, 12 + (8 + Constant.DATAGRID_ROW_HEIGHT * 2) * i);
grid[i].Visible = true;
grid[i].RowHeadersVisible = false;
grid[i].Height = Constant.DATAGRID_ROW_HEIGHT * 2;
grid[i].Width = Constant.DATAGRID_COLUMN_SIZE * Constant.DATAGRID_DIETS_IN_GRID + 3;
grid[i].UserAddedRow += Form1_UserAddedRow;
}
this.Width = Constant.DATAGRID_COLUMN_SIZE * Constant.DATAGRID_DIETS_IN_GRID + 40;
foreach (string x in lines)
{
DataGridViewColumn col = new DataGridViewTextBoxColumn();
col.Width = Constant.DATAGRID_COLUMN_SIZE;
col.HeaderText = x;
int colIndex = grid[0].Columns.Add(col);
}
}
private void Form1_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
//I want to access grid[i] and modify it here.
}
You should be able to cast the Sender object parameter in your event handler to the type of DataGridView to retrieve the grid which has been effected.
You are getting the DataGridViewRowEventArgs e as the argument to your event handler and thus you can access the Row property like
e.Row.Cells["somename"].Value = "some_value";
private void Form1_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
var grid = sender as DataGridView;
if (grid == null) return;
//... do something
}

How to convert Listview dates to local time zone?

I have 3 columns in the ListView. From,Subject,Date
I'm using the OpenPop library.
private int numberofallmessages = 0;
private int countMsg = 0;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
OpenPop.Pop3.Pop3Client PopClient = new OpenPop.Pop3.Pop3Client();
PopClient.Connect("mail", 110, false);
PopClient.Authenticate("me", "me",
OpenPop.Pop3.AuthenticationMethod.UsernameAndPassword);
List<string> uids = PopClient.GetMessageUids();
int messageCount = PopClient.GetMessageCount() -1;
numberofallmessages = messageCount;
allMessages = new List<OpenPop.Mime.Message>(messageCount);
for (int i = messageCount; i > 0; i--)//for (int i = messageCount - 1; i > -1; i--)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return;
}
string currentUidOnServer = uids[i];
if (!seenUids.Contains(currentUidOnServer))
{
if (i > 0)
allMessages.Add(PopClient.GetMessage(i));
SaveFullMessage(PopClient.GetMessage(i), i);
w = new StreamWriter(emailsIDSFile, true);
w.WriteLine(currentUidOnServer);
w.Close();
int nProgress = (messageCount - i + 1) * 100 / messageCount;
backgroundWorker1.ReportProgress(nProgress, PopClient.GetMessageCount().ToString() + "/" + i);
}
}
PopClient.Disconnect();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbt.Value = e.ProgressPercentage;
pbt.Text = e.ProgressPercentage.ToString() + "%";
pbt.Invalidate();
label8.Text = e.UserState.ToString();
label8.Visible = true;
lvnf.Items.Add(new ListViewItem(new string[]
{
allMessages[countMsg].Headers.From.ToString(), //From Column
allMessages[countMsg].Headers.Subject, //Subject Column
allMessages[countMsg].Headers.DateSent.ToString() //Date Column
}));
countMsg += 1;
}
The problem is in the progresschanged event i think. Where i add the items to each column.
When it's adding the emails to the ListView i see it like this:
The problem is on the date column the date is fine but the time in not my time. Not sure of what place the time is but in my place it's now 1:52 AM
How can i get/set the time of my place ?
I couldn't find in the line:
allMessages[countMsg].Headers.DateSent.ToString()
How to change it to my time.
Try this:
allMessages[countMsg].Headers.DateSent.ToLocalTime().ToString();
You want to utilize the DateTime.ToLocalTime() method. It does the heavy lifting for you.
Hope this helps
Edit: Removed incorrect version as the documentation for OpenPop.Net states that the MessageHeader.DateSent property is in fact a DateTime object.

Excel sheet renaming won't work

I want to rename the specified worksheet and put some processed data on it, but the following code (which looks almost the same as every example I could find on this and other websites) just won't to what I want:
try
{
xl.Worksheet = (ExcelRefer.Worksheet) xl.Workbook.Worksheets.get_Item("Sheet1");
xl.Worksheet.Name = "Smoothed result";
xl.Workbook.Save();
xl.Range = xl.Worksheet.UsedRange;
Debug.WriteLine("Saved");
}
catch(Exception exception)
{
MessageBox.Show(exception.Message);
}
The exception is never thrown, so the code doesn't contain any errors but the worksheet in the document I've opened still has the same name. Also, the
Debug.WriteLine(...)
method is called and the output is correctly displayed. It does even ask me if I want to overwrite the existing file when I tell the Workbook to save. Could anyone tell me what I'm doing wrong?
Edit:
xl is an object of a class that contains all necessary elements for working with excel.
using ExcelRefer = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Tools.Excel;
using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using ZedGraph;
class XLSXMulti
{
private static List<int[]> dataPositions = new List<int[]>();
private static PointPairList measureData = new PointPairList();
private static PointPairList[] dataComparison = new PointPairList[2];
private static bool smoothing = false;
private static bool measureDataFound = false;
private static bool fileFinished = false;
private static string[] excelFilePaths;
private static string[] temp;
private static int numberOfSamples;
private static BackgroundWorker[] bwg;
private static ProgressBar pbExcel;
public static void Init(string[] filePaths, ProgressBar pb, bool smooth)
{
excelFilePaths = filePaths;
pbExcel = pb;
smoothing = smooth;
measureData.Clear();
dataPositions.Clear();
temp = Interaction.InputBox("Number of consecutive values to be used " +
"for smoothing and the iteration step. These values" +
" will be used for all selected files.\n" +
"IMPORTANT: The two values have to be separated with a ','!",
"Select grade of smoothing",
"5,1", 500, 480).Split(',');
bwg = new BackgroundWorker[Environment.ProcessorCount];
for(int i = 0; i < bwg.Length; i ++)
{
bwg[i] = new BackgroundWorker();
}
foreach(BackgroundWorker bw in bwg)
{
bw.DoWork += bwg_doWork;
bw.ProgressChanged += bwg_ProgressChanged;
bw.RunWorkerCompleted += bwg_RunworkerCompleted;
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
}
List<string>[] filesForWorkers = new List<string>[bwg.Length];
for(int i = 0; i < filesForWorkers.Length; i ++)
{
filesForWorkers[i] = new List<string>();
}
int filesPerWorker = filePaths.Length / bwg.Length;
int workerindex = 0;
MessageBox.Show("filesPerWorker: " + filesPerWorker + "\n" +
"filePaths: " + filePaths.Length + "\n" +
"bwg: " + bwg.Length);
for(int i = 0; i < filePaths.Length; i ++)
{
filesForWorkers[workerindex].Add(filePaths[i]);
workerindex ++;
if(workerindex == bwg.Length)
{
workerindex = 0;
}
}
for(int i = 0; i < bwg.Length; i ++)
{
bwg[i].RunWorkerAsync(filesForWorkers[i]);
}
}
private static void bwg_doWork(object sender, DoWorkEventArgs e)
{
List<string> filelist = e.Argument as List<string>;
Excelattributes xl = new Excelattributes();
foreach(string s in filelist)
{
try
{
xl.App = new ExcelRefer.Application();
xl.Workbook = xl.App.Workbooks.Open(s, 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xl.Worksheet = (ExcelRefer.Worksheet)xl.Workbook.Worksheets.get_Item(1); // Zugriff auf eingegebenes Tabellenblatt
xl.Range = xl.Worksheet.UsedRange;
findMeasureData(xl);
if(xl.Workbook != null){xl.Workbook.Close(true, null, null);}
if(xl.App != null) {xl.App.Quit();}
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
finally
{
if(xl.Workbook != null){xl.Workbook.Close(true, null, null);}
if(xl.App != null) {xl.App.Quit();}
}
}
}
private static void bwg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// TODO: implement some sort of progress reporting
}
private static void bwg_RunworkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Statuslabel.label.Text = "Backgroundworker finished";
(sender as BackgroundWorker).Dispose();
}
private static void findMeasureData(Excelattributes xl)
{
// Erste 15 Zeilen durchgehen, um herauszufinden wo die Messwerte beginnen (9 + 6 Sicherheit)
// Nur bis inkl. Spalte AZ
try
{
for(int zeile = 1; zeile <= 15; zeile ++)
{
for(int spalte = 1; spalte <= 52; spalte ++)
{
// WICHTIG: (xl.Range...).Text verwenden, um Nullreferenceexception zu vermeiden [?]
Object zelleninhalt = (xl.Range.Cells[zeile, spalte] as ExcelRefer.Range).Text;
if(zelleninhalt.Equals("DATA START"))
{
dataPositions.Add(new int[2] {zeile +1, spalte +1});
measureDataFound = true;
}
else if(zelleninhalt.Equals("Number of Samples"))
{
numberOfSamples = Convert.ToInt32((xl.Range.Cells[zeile, spalte+1] as
ExcelRefer.Range).Text);
}
}
}
if(measureDataFound == true)
{
int[,] temp = new int[dataPositions.Count, 2];
for(int i = 0; i < dataPositions.Count; i ++)
{
temp[i, 0] = dataPositions.ElementAt(i).ToArray()[0];
temp[i, 1] = dataPositions.ElementAt(i).ToArray()[1];
}
readMeasureData(temp, xl);
}
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
}
private static void readMeasureData(int[,] temp, Excelattributes xl)
{
int[,] zellen = temp;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for(int i = zellen[0,0]; i < (zellen[0,0] + numberOfSamples); i ++)
{
try
{
double weg = Convert.ToDouble((xl.Range.Cells[i, zellen[0,1]] as ExcelRefer.Range).Value2);
double kraft = Convert.ToDouble((xl.Range.Cells[i, zellen[1,1]] as ExcelRefer.Range).Value2);
measureData.Add(new PointPair(weg, kraft));
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
Application.Exit();
}
}
stopwatch.Stop();
MessageBox.Show(stopwatch.ElapsedMilliseconds / 1000 + " Sekunden");
dataComparison[0] = measureData;
if(smoothing == true)
{
smoothMeasureData(xl);
}
}
private static void smoothMeasureData(Excelattributes xl)
{
if(temp != null)
{
try
{
int[] smoothParameters = new int[]{Convert.ToInt32(temp[0]), Convert.ToInt32(temp[1])};
if(smoothParameters[1] == 0) {smoothParameters[1] = 1;}
PointPairList smoothedData = new PointPairList();
MessageBox.Show("Glätte...\n" + smoothParameters[0] + " " + measureData.Count);
for(int i = (smoothParameters[0] -1); i < measureData.Count; i += smoothParameters[1])
{
double durchschnittX = 0;
double durchschnittY = 0;
for(int x = i; x > (i - (smoothParameters[0])); x --)
{
durchschnittX += measureData.ElementAt(x).X;
durchschnittY += measureData.ElementAt(x).Y;
}
durchschnittX /= (double) smoothParameters[0];
durchschnittY /= (double) smoothParameters[0];
smoothedData.Add(new PointPair(durchschnittX, durchschnittY));
}
dataComparison[0] = measureData;
dataComparison[1] = smoothedData;
writeToXLSX(smoothedData, xl);
}
catch(Exception exception)
{
MessageBox.Show(exception.Message, "Exception");
}
}
}
private static void writeToXLSX(PointPairList smoothedData, Excelattributes xl)
{
try
{
xl.Worksheet = (ExcelRefer.Worksheet) xl.Workbook.Worksheets.get_Item("Sheet1");
xl.Worksheet.Name = "Smoothed result";
xl.Workbook.Save();
xl.Workbook.Close(true, null, null);
xl.Range = xl.Worksheet.UsedRange;
Debug.WriteLine("Saved");
}
catch(Exception exception)
{
MessageBox.Show(exception.Message);
}
}
}
Have you tried closing Excel when you've finished?
I can't quite work out where you are gettng xl from but the following code works for me:
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Workbook book = xlApp.Workbooks.Open(#"C:\test.xlsx");
Worksheet xl = book.Worksheets.get_Item("sheet1");
xl.Name = "Smoothed result";
book.Save();
book.Close();
Edit
After seeing the code you are using to open the workbook I think the issue is there. You are opening the file using:
xl.Workbook = xl.App.Workbooks.Open(s, 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
The third parameter is a ReadOnly parameter which you have set to true. Try setting that to false.

Displaying Particular Item On listView1_MouseClick

In Quize method I am passing qestions which contains set of all my Questions to be displayed using DisplayQuestion(),Question is my Class, Problem is that I am getting only First Question displayed, how can I get them displayed when i click on listviewItem if suppose questionscontains 10 Questions,than in listviewItem I have displayed numbers(1 2 3 4 5 ....10),when i click on each number how do i display that particular Questiondisplyed on click and if not clicked how all Questions displayed one by one using timer
public partial class GroupExmStart : Form
{
string[] randomQsn = new string[totQsn + 1]; //totQsn is the total number of question for e.g.10
public GroupExmStart(string GroupName, string DurationID)
{
InitializeComponent();
this.GrpID=GroupName;
TopiID=db.GetTopicIDForGroup(GrpID);
string[] conf = db.GetConfiguration(Convert.ToInt16(DurationID)).Split('|');
Question qsn = new Question();
var questions = qsn.Foo(TopiID, conf);
int z = Quiz(questions);
totQsn = Convert.ToInt16(conf[0]);
for (int kk = 1; kk <= totQsn; kk++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = kk.ToString();
listView1.Items.Add(lvi);
}
randomQsn = new string[totQsn + 1];
timer1.Interval = 1000; //1000ms = 1sec
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}
int Quiz(List<Question> questions)
{
foreach (Question question in questions)
{
DisplayQuestion(question);
}
return 0;
}
private void DisplayQuestion(Question question)
{
string Q = question.Text;
label5.Text = Q;
string OP1 = question.Option1;
string OP2 = question.Option2;
string OP3 = question.Option3;
string OP4 = question.Option4;
radioButton12.Text = OP1;
radioButton11.Text = OP2;
radioButton10.Text = OP3;
radioButton9.Text = OP4;
}
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
if (randomQsn.GetLength(0) >= 0)
{
if (listView1.SelectedItems.Count > 0)
{
//here how should i get That particular Question so that i can display it
//something like this ? //Convert.ToInt16(listView1.SelectedItems[0].SubItems[0].Text)
DisplayQuestion(question);
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tik++;
if (tik == 60)
{
label1.Text = (Convert.ToInt16(label1.Text) - 1).ToString();
tik = 0;
}
}
}
Thanks for any help in advance
The following is what you are looking for. You must grab the text of the list view item and use that as the index of the question.
if (listView1.SelectedItems.Count > 0)
{
var q = Convert.ToInt16(listView1.SelectedItems[0].Text);
var selectedQuestion = questions[q - 1];
DisplayQuestion(selectedQuestion);
}
In order for this to work, modify your constructor to the following:
private List<Question> questions;
public partial class GroupExmStart : Form
{
string[] randomQsn = new string[totQsn + 1]; //totQsn is the total number of question for e.g.10
public GroupExmStart(string GroupName, string DurationID)
{
InitializeComponent();
this.GrpID=GroupName;
TopiID=db.GetTopicIDForGroup(GrpID);
string[] conf = db.GetConfiguration(Convert.ToInt16(DurationID)).Split('|');
Question qsn = new Question();
/// THIS IS MODIFIED //
questions = qsn.Foo(TopiID, conf);
int z = Quiz(questions);
totQsn = Convert.ToInt16(conf[0]);
for (int kk = 1; kk <= totQsn; kk++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = kk.ToString();
listView1.Items.Add(lvi);
}
randomQsn = new string[totQsn + 1];
timer1.Interval = 1000; //1000ms = 1sec
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}

Leave Event is called twice in case of deletion

I have the below code
public void panel_item_collections_Click(object sender, EventArgs e)
{
TextBox[] textbox_item_array = new TextBox[5];
item_textbox.Add(textbox_item_array);
textbox_item_array[0] = new TextBox();
textbox_item_array[0].Width = label_item_code.Width;
textbox_item_array[0].Height = 26;
textbox_item_array[0].Font = print_font_default;
textbox_item_array[0].Location = new Point(label_item_code.Location.X, 45 + (20 * row_count));
textbox_item_array[0].Name = string.Concat("item_code", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[0]);
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);
textbox_item_array[1] = new TextBox();
textbox_item_array[1].Width = label_item_descrition.Width;
textbox_item_array[1].Font = textbox_item_array[0].Font;
textbox_item_array[1].Location = new Point(label_item_descrition.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[1].Name = string.Concat("item_description", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[1]);
textbox_item_array[2] = new TextBox();
textbox_item_array[2].Width = label_item_price.Width;
textbox_item_array[2].Font = textbox_item_array[0].Font;
textbox_item_array[2].Location = new Point(label_item_price.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[2].Name = string.Concat("item_price", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[2]);
textbox_item_array[3] = new TextBox();
textbox_item_array[3].Width = label_item_quantity.Width;
textbox_item_array[3].Font = textbox_item_array[0].Font;
textbox_item_array[3].Location = new Point(label_item_quantity.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[3].Name = string.Concat("item_quantity", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[3]);
textbox_item_array[4] = new TextBox();
textbox_item_array[4].Width = label_item_total.Width;
textbox_item_array[4].Font = textbox_item_array[0].Font;
textbox_item_array[4].Location = new Point(label_item_total.Location.X, textbox_item_array[0].Location.Y);
textbox_item_array[4].Name = string.Concat("item_total", row_count.ToString());
panel_item_collections.Controls.Add(textbox_item_array[4]);
row_count++;
}
Now, here is the leave event handler:
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
Now, the problem is like this:
If the user leave the textbox blank, the row will be deleted. The strange problem is:
If press tab, it will execute the leave event handler twice. It means it will try to delete the same textbox twice and this will create problem. Can any one help me how to avoid this double calling?
Thanks
I want to add more: here is exactly what is happening:
Now, I will give exactly how it is executed:
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
After that, it calls the same function
void dynamic_text_item_code_Leave(object sender, EventArgs e)
{
//MessageBox.Show(((Control)sender).Name.Substring(((Control)sender).Name.Length - 1, 1));
int i;
string name_textbox = ((Control)sender).Name;
i = System.Convert.ToInt32(name_textbox.Substring(name_textbox.Length - 1, 1));
//MessageBox.Show(i.ToString());
//i--;
TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[i]);
double item_total;
Item item = new Item();
if (long.TryParse(textbox_item_array[0].Text, out item.item_code) == true)
{
if (item.get_item() == 0)
{
textbox_item_array[1].Text = item.item_details;
textbox_item_array[2].Text = item.sell_price.ToString();
textbox_item_array[3].Text = "1";
item_total = System.Convert.ToInt32(textbox_item_array[3].Text) * item.sell_price;
textbox_item_array[4].Text = item_total.ToString();
}
}
else
{
//TextBox[] textbox_item_array = new TextBox[5];
textbox_item_array = (TextBox[])(item_textbox[item_textbox.Count - 1]);
panel_item_collections.Controls.Remove(textbox_item_array[0]);
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
Then it continues here
panel_item_collections.Controls.Remove(textbox_item_array[1]);
panel_item_collections.Controls.Remove(textbox_item_array[2]);
panel_item_collections.Controls.Remove(textbox_item_array[3]);
panel_item_collections.Controls.Remove(textbox_item_array[4]);
item_textbox.RemoveAt((item_textbox.Count - 1));
row_count--;
}
}
So, why it executes once it get to the remove
Are you sure, you don't use this line twice in another part of code?
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);
If not I suppose you hooks dynamic_text_item_code_Leave to one another control.
EDIT
You can also to protect by adding this code:
if (textbox_item_array[0].Leave != null)
textbox_item_array[0].Leave += new EventHandler(dynamic_text_item_code_Leave);

Categories

Resources