I have too many files on ftp server which I want to display in data gridview without saving or downloading and sort by date. These files are .qrt but I can open them in excel or notepad.
I did that but from sql db but never worked with ftp.
These files have names with dates.
I'm using asp.net web forms in C#.
How can I do this?
This as noted is quite easy. But, you don't mention if you have a bunch of folders that you want to display, or just files from one FTP folder?
As noted, if you have a bunch of folders, then VERY easy to use a tree view to display this hierarchy type setup. However, for one folder? Sure, a GridView is a great choice.
You can say use this:
our markup is thus this:
<asp:GridView ID="GridView1" runat="server"
CssClass="table" Width="20%"></asp:GridView>
Ok, and our code is this:
using System.Net;
using System.IO;
So, for page load, we have this:
const string UserId = "User Name here";
const string Password = "Password here";
const string fRoot = "ftp://ftp.mycoolwebsite.com/test";
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
DataTable MyFiles = new DataTable();
MyFiles.Columns.Add("File", typeof(string));
List<string> fList = GetFtpFiles();
foreach (string sFile in fList)
{
DataRow OneRow = MyFiles.NewRow();
OneRow["File"] = sFile;
MyFiles.Rows.Add(OneRow);
}
GridView1.DataSource = MyFiles;
GridView1.DataBind();
}
List<string> GetFtpFiles()
{
FtpWebRequest ftpR = (FtpWebRequest)WebRequest.Create(fRoot);
ftpR.Credentials = new NetworkCredential(UserId, Password);
ftpR.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = ftpR.GetResponse() as FtpWebResponse;
StreamReader sread = new StreamReader(response.GetResponseStream());
List<string> myDir = new List<string>();
string oneLine = sread.ReadLine();
while (!string.IsNullOrEmpty(oneLine))
{
myDir.Add(oneLine);
oneLine = sread.ReadLine();
}
sread.Close();
return myDir;
}
And out output is now this:
Now, if we want the file size? that's a problem, since in theory ONCE we have the file list, we could cook up code to "request" each file size - that's a pain, but WORSE is a lot of requests. The other way, is we can change this:
ftpR.Method = WebRequestMethods.Ftp.ListDirectory;
to:
ftpR.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
The above will return file information VERY much like an old style DOS file listing.
When we run above with above, we now get this:
So, if we want to sort say by file name, we have to parse out the file name, and date. This is not too hard. As noted, the other way is to "request" the file size an date for each file - but that is extra code, but WORSE is a LOT of hits to the ftp server.
And the style of the file listings are not consistent from web servers (might be windows, or linux), but we COULD parse out the above into columns - and then we can sort by date, or file name.
Parsing? Gee, this works:
void LoadGrid()
{
DataTable MyFiles = new DataTable();
MyFiles.Columns.Add("Date", typeof(DateTime));
MyFiles.Columns.Add("Size", typeof(int));
MyFiles.Columns.Add("File", typeof(string));
List<string> fList = GetFtpFiles();
foreach (string s in fList)
{
DataRow OneRow = MyFiles.NewRow();
string sRow = s;
while (sRow.Contains(" "))
sRow = sRow.Replace(" ", " ");
sRow = sRow.Replace(" ", ",");
string[] scols = sRow.Split(',');
OneRow["Size"] = scols[4];
OneRow["File"] = scols[8];
OneRow["Date"] = scols[7] + "-" + scols[6] + "-" + scols[5];
MyFiles.Rows.Add(OneRow);
}
GridView1.DataSource = MyFiles;
GridView1.DataBind();
}
And now we get this:
We need to formt that ate in the grid - but at least now, with a table, you can sort that table by date, or filename before you bind it, say like this:
MyFiles.DefaultView.Sort = "File";
GridView1.DataSource = MyFiles;
GridView1.DataBind();
And you can/could sort by date also - since we defined date column as datetime.
Related
So I finally was able to create a XML and change it as I want but now I needed to add the contents of a DataGridView to it. I thought that's quite easy as I saw the options to place it into a DataSet and use XmlWrite, but that was a mistake of me. Note that I'm still trying to learn C# so probably I make a silly mistake here. It is still not working maybe someone is willing to point me out what I am doing wrong?
I actually have two issues with this:
It ForEach loop doesn't get the existing column names
It doesn't add the table and its contents to the XML file
private void CreateClientFile()
{
string filename;
filename = Company + "_" + SiteName + ".xml";
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("CompanyProfile");
doc.AppendChild(root);
//Save document on Harddisk
doc.Save(#"C:\Users\NLRAGIL\Documents\10 - VibroManager\" + filename);
//Need to save first and than load again????
//Load document into program
doc.Load(#"C:\Users\NLRAGIL\Documents\10 - VibroManager\" + filename);
XmlNode main = doc.SelectSingleNode("CompanyProfile");
//Create Company name element
XmlElement companyname = doc.CreateElement("CompanyName");
companyname.InnerText = CompanyName;
main.AppendChild(companyname);
//Create sitename element
XmlElement sitename = doc.CreateElement("Sitename");
sitename.InnerText = SiteName;
main.AppendChild(sitename);
//Create IMO element
XmlElement imo = doc.CreateElement("IMO");
imo.InnerText = IMO;
main.AppendChild(imo);
DataTable dt = new DataTable();
for (int i = 0; i < dataGridView1.Columns.Count; i++)
{
dt.Columns.Add("column" + i.ToString());
}
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataRow dr = dt.NewRow();
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
dr["column" + j.ToString()] = row.Cells[j].Value ;
}
dt.Rows.Add(dr);
}
//Create DataSet and add the datatable
DataSet ds = new DataSet();
ds.Tables.Add(dt);
//Give the file name for where to write to.
ds.WriteXml(#"C:\Users\NLRAGIL\Documents\10 - VibroManager\" + filename);
//Show example for debugging
doc.Save(#"C:\Users\NLRAGIL\Documents\10 - VibroManager\" + filename);
System.Console.WriteLine(doc.InnerXml);
}
EXTRA CLARIFICATION:
The form I have looks as below:
The Textbox in the groupbox "Client Information" I'm able to save in a XML file. By altering the value of the numeric control I can express how much machine the particular client has. And the DataGridView gets more or less rows. But the information from the DataGridView I'm unable to append to the created XML file.
So the information from "Machine Name", "Serial No" etc I can't add to the XML file.
This is what I wanted to do, so later on in the program I can add certain measurements of each machine to it and store also in the same file.
But whatever I do my XML file looks like this:
I hope I explained it better now sorry for the confusion
Your question is Add the contents of a DataGridView to an existing XML file and you say your first issue is that your ForNext loop is not giving you the column names and your second issue is that the code fails to serialize the record to an XML file on disk. These two goals can be simplified by using Data Binding. This decouples your data from the view, making it easier to process. I would like to give you some insight if you wanted to try it out using the CompanyProfile in your code.
First, a CompanyProfile class declares the intended public properties:
public class CompanyProfile
{
public string CompanyName { get; set; }
public string SiteName { get; set; }
public string IMO { get; set; } = "Some Value";
}
Next, in your MainForm class a BindingList<CompanyProfile> is declared and attached to the DataGridView like this:
BindingList<CompanyProfile> DataSource = new BindingList<CompanyProfile>();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!DesignMode)
{
// Attach the data source to the view. Now changes to source records refresh in the view.
dataGridView1.DataSource = this.DataSource;
// Adding one or more records will generate the columns.
DataSource.Add(new CompanyProfile { CompanyName = "Linear Technology", SiteName = "Colorado Design Center"});
DataSource.Add(new CompanyProfile { CompanyName = "Analog Devices", SiteName = "1-1-2"});
// Use string indexer to get a column
dataGridView1.Columns[nameof(CompanyProfile.CompanyName)].AutoSizeMode = dataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns[nameof(CompanyProfile.SiteName)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
DataGridView1.AllowUserToAddRows = false;
}
}
The resulting DataGridView now looks like this:
This method makes a single file from a CompanyProfile record using XmlSerializer (but this is just one approach - and you could also serialize the entire list at one time if you choose).
private void CreateClientFile(CompanyProfile companyProfile, string fileName)
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(CompanyProfile));
using (var writer = new StreamWriter(fileName))
{
x.Serialize(writer, companyProfile);
}
// Open the file to view the result
Process.Start("notepad.exe", fileName);
}
Now, iterate a ForNext loop on the DataSource not the DataGridView. You no longer need to worry about columns because you have the bound properties instead.
private void btnSerialize_Click(object sender, EventArgs e)
{
var appData = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"datagridview_to_xml");
Directory.CreateDirectory(appData);
// Iterate the datasource list, not the DataGridView.
foreach (CompanyProfile companyProfile in DataSource)
{
CreateClientFile(
companyProfile,
fileName: Path.Combine(appData,
$"{companyProfile.CompanyName}_{companyProfile.SiteName}.xml")
);
}
}
Clicking the [Serialize] button reveals the two files.
So i had posted a question on getting my sample project working and it now works.. Sample Project
And thats great because as in that example i have a production project that requires copying files, so that will work great for that one.
But this question is about displaying a progress bar to a process that im not clear on how to implement.
Im reading a excel file using closedxml, i read that file into a datatable in order to perform some filtering and other things in order to populate some listboxes on my form, how can my sample code in my other post be implemented against the creation of 5 or 6 data tables?
I can provide some of the datatable creation methods, but the over all code is close to 600 lines right now and not finished yet.. so below is a stripped down sample of the current code im working with..
private void sample()
{
string plink = #"C:\Test\Sizes.xlsx";
string[] DistinctDept = { "Dept Code", "Dept Description" };
DataTable ListDept = GetDistinctRecords(LoadExceltoDatatable(plink), DistinctDept);
ListDept.Columns.Add(new DataColumn("DeptCombo", typeof(string), "'('+[Dept Code] +') ' + [Dept Description]"));
if (string.IsNullOrEmpty(ListDept.Rows[0]["Dept Code"].ToString()))
{
ListDept.Rows[0].Delete();
ListDept.AcceptChanges();
}
lbDept.DataSource = ListDept;
lbDept.DisplayMember = "DeptCombo";
lbDept.ClearSelected();
}
public static DataTable GetDistinctRecords(DataTable dt, string[] Columns)
{
DataTable dtUniqRecords = new DataTable();
dtUniqRecords = dt.DefaultView.ToTable(true, Columns);
return dtUniqRecords;
}
public static DataTable LoadExceltoDatatable(string sizeoptcalc)
{
using (var wb = new XLWorkbook(sizeoptcalc, XLEventTracking.Disabled))
{
var ws = wb.Worksheet(1);
var foundMonth = ws.Search("Month", System.Globalization.CompareOptions.OrdinalIgnoreCase);
var monthRow = foundMonth.Last().Address; // A11
var lastcell = ws.LastCellUsed().Address; // BC3950
DataTable dataTable = ws.Range(monthRow, lastcell).RangeUsed().AsTable().AsNativeDataTable();
return dataTable;
}
}
Can this be changed to report the progress? I mean in some cases the excel files are large and do take some time to fill in my listboxes.
Here are more of the datatable creations that i would like to account for the overall progress of them
in my case i wanted to display items from local SQLite database which i created as shown below:
public string CreateDB() //create database
{
var output = "";
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "IsoModule.db3");
output = "Database Created";
return output;
}
public string CreateTable() //create table
{
try
{
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "IsoModule.db3");
var db = new SQLiteConnection(dbPath);
db.CreateTable<UserInfo>();
db.CreateTable<TableInfo>();
string result = "Table(s) created";
return result;
}
catch (Exception ex)
{
return ("Error" + ex.Message);
}
}
and this is my code where i wish to retrieve data
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "IsoModule.db3");
var tablelistout = new SQLiteConnection(path);
var alltables = tablelistout.Table<TableInfo>();
foreach (var listing in alltables)
{
var from = new string[]
{
listing.tname + " - " + listing.status
};
ListView listtable = (ListView)FindViewById(Resource.Id.listtable);
listtable.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, from);
}
the code runs with NO ERROR but it only display last item in the table. it is confusing me, so i would like to ask how can i retrieve all the data from specific table?
or if someone has asked the same question please share me the link. many appreciate.
var alltables = tablelistout.Table<TableInfo>();
var data = new List<string>();
foreach (var listing in alltables)
{
data.Add(listing.tname + " - " + listing.status);
}
ListView listtable = (ListView)FindViewById(Resource.Id.listtable);
listtable.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, data.ToArray());
All I did was move 2 things out of the loop. First, I moved out the initialization of the array. Second, I moved out the listView + assignation of the adapter.
Your issue is that in the loop, you were always overriding everything you had done in the previous iteration (leaving you with the last item like you said).
Also, You should take note that it will be important for you to create a custom adapter if you plan on having a decent amount of data. ArrayAdapter is a native Android class which is then wrapped by a C# Xamarin object, meaning you will have both a C# and Java object per row. It adds overhead as both garbage collectors will have work to do and can cause performance issues. Xamarin devs tend to generally avoid it with the exception of quick prototyping.
On another note, I would use the FindViewById<T>(Int32) instead of the FindViewById(Int32) and casting it. FindViewById<ListView>(Resource.Id.listtable) in your case. Just taking advantage of the power of generics.
What i am trying to do is, i am getting file name/path using asp.net uploader control and then saving its path it grid view. e.g
String path = String.Empty;
path = FileUploader.FileName;
and then saving this path in grid view column.
savefiletoGrid(path);
After uploading all required files i am saving these file on server. like this
while( // condition )
{
string tempfilename = ""; // file name/path from gridview
string path2 = Server.MapPath("Dir\\" + tempfilename);
FileUploader.SaveAs(path2);
}
But, problem is that file is being saved on server with correct name but with size 0 byte.
Please let me know how to solve this issue ?
Actually i want something like client upload in asp.net, i 'll upload more than one file and show them in gridview ( or in something else ) so that user can see files to be selected and can delete from listed files.
File 'll be saved to server only when user click some other button say 'Update'. could you please help me , how to accomplish this ?
You have to catch the event generated in GridView in its RowCommand event also set a CommandName property for upload button.
Following is the detailed code through which you can accomplish this:
<asp:GridView ID="GridView1" runat="server" OnRowCommand="GridView1_RowCommand">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Upload" CommandName="Upload"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And in you code behind:
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Upload")
{
FileUpload FileUp = (FileUpload)e.Item.FindControl("FileUpload1");
string UploadedFileName = FileUp.FileName;
string Path = Server.MapPath("Documents");
FileUpload.SaveAs(Path + "\\" + UploadedFileName);
}
}
Hope it helps.
To accomplish this functionality, you'll have to let the user upload the files. You must save them temporarily to display them.
Then, upon the user clicking an 'Update' button, you will transfer the temporary files to your permanent storage.
Do you keep the FileUploaders in a GridView?
Problem has been solved by creating a DataTable with one column for control (with column data Type FileUpload) for example :
private DataTable CreateDtDocs(string name, string path, FileUpload FileUploader)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("SR_NO");
dt1.Columns.Add("Name");
dt1.Columns.Add("Path");
Type col_type = fubrowse.GetType();
DataColumn dt_col = new DataColumn("Control", col_type);
dt1.Columns.Add(dt_col);
DataRow dr = dt1.NewRow();
dr["SR_NO"] = "1";
dr["NAME"] = name;
dr["Path"] = path;
dr["Control"] = FileUploader;
dt1.Rows.Add(dr);
return dt1;
}
And then Populate table like below :
private DataTable AddDtDocs(string name, string path, FileUpload FileUploader)
{
DataTable dt1 = (DataTable)Session["AttachFilesdt"];
int count = dt1.Rows.Count;
DataRow dr = dt1.NewRow();
dr["SR_NO"] = count + 1;
dr["NAME"] = name;
dr["Path"] = path;
dr["Control"] = FileUploader;
dt1.Rows.Add(dr);
return dt1;
}
And then i am adding path name and control in Dictionary and passing them to a different function to save them on server.
Dictionary<string, FileUpload> DocsPathAndControl = new Dictionary<string, FileUpload>();
if (Session["AttachFilesdt"] != null)
{
tempdt = (DataTable)Session["AttachFilesdt"];
for (int i = 0; i < tempdt.Rows.Count; i++)
{
DocsPathAndControl.Add(tempdt.Rows[i]["Path"].ToString(), (FileUpload)tempdt.Rows[i]["Control"]);
}
Session["AttachFilesdt"] = null;
}
Function to save Files
private void AddDocuments(int jurisdictionID, Dictionary<string,FileUpload> docPathsAndControl)
{
foreach (var item in docPathsAndControl)
{
string tempfilename = jurisdictionID + "_" + item.Key.ToString();
string path = Server.MapPath("Dir\\" + tempfilename);
FileUpload FileUploaderControl = (FileUpload)item.Value;
FileUploaderControl.PostedFile.SaveAs(path);
}
}
Hope, it 'll help.
I'm having some evils trying to get my GridView control to behave. I have the below code, which successfully displays all the files in the directory. However I require two changes, both of which I am struggling with:
a) Currently the URL you get when clicking on the URL field is
http://localhost/LBSExplorer/SharedUser.csv (ie my home directory with the filename).
What I require is that the 'Display Text' be the filename only, and the URL be my desired text followed by the filename eg:
http://mystuff/page.aspx?FileID=SharedUser.csv
b) I want only to see the files that start with a certain prefix eg "Pay". I can do that with something like:
string[] filelist = Directory.GetFiles((#"C:\MF\Data\","Pay*.*");
but this doesn't like to bind to my Gridview!
I'd appreciate your help!
Mark
const string DocumentFolderPhysicalPath = (#"C:\MF\Data\");
const string DocumentFolderUrl = (#"C:\MF\Data\"); //"http://localhost/virtualfoldernameyouexposed/"; ; // now it is hardcoded but you could retreive it automatically
HyperLinkField hyperLinkField = new HyperLinkField();
hyperLinkField.DataTextField = "Name";
hyperLinkField.DataNavigateUrlFields = new string[] { "Name" };
//Would like this to work!
//HyperLinkField hyperLinkField2 = new HyperLinkField();
//hyperLinkField2.DataTextField = "Destination";
//hyperLinkField2.DataNavigateUrlFields = new string[] { (#"C:\MF\Data\") + "Name" };
GridView1.DataSource = GetDocuments(DocumentFolderPhysicalPath);
GridView1.Columns.Add(hyperLinkField);
GridView1.DataBind();
private System.IO.FileInfo[] GetDocuments(string physicalPath)
{
System.IO.DirectoryInfo directory =
new System.IO.DirectoryInfo(physicalPath);
if (directory.Exists)
{
return directory.GetFiles();
}
else
{
throw new System.IO.DirectoryNotFoundException(physicalPath);
}
}
What you are looking for is the DataNavigateUrlFormatString property.
hyperLinkField.DataNavigateUrlFormatString = "http://mystuff/page.aspx?FileID={0}";
The {0} here is replaced with your DataNavigateUrlFields value.