UsedRange.RowCount is not updated after an insert - c#

I'm trying to write an Extension methods that adds a generic Item T to the workbook, I've got problem since the UsedRange.RowCount is not incremented after InsertMethod is called
public static RowItem<T> AddItem<T>(this SpreadsheetGear.IWorksheet worksheet, T item) where T : class
{
int currentRow = worksheet.UsedRange.RowCount;
//int currentRow = worksheet.UsedRange.RowCount;
RowItem<T> newItem = new RowItem<T>
{
Item = item,
RowIndex = currentRow
};
var reflected = item.GetType().GetProperties();
for (int i = 0; i < reflected.Length; i++)
{
object value = reflected[i].GetValue(item);
worksheet.Cells[currentRow, i].Value = value;
}
worksheet.UsedRange.Insert(SpreadsheetGear.InsertShiftDirection.Down);
worksheet.WorkbookSet.CalculateFull();
return newItem;
}
public static IEnumerable<RowItem<T>> AddItems<T>(this SpreadsheetGear.IWorksheet worksheet, IEnumerable<T> items) where T : class
{
var lst = new List<RowItem<T>>();
foreach (var item in items)
{
var newItem = AddItem<T>(worksheet, item);
lst.Add(newItem);
}
return lst;
}
It's always 1 ...what am I doing wrong? my dummy class is
public class Dummy
{
public string Desciption { get; set; }
public double Value { get; set; }
public DateTime Data { get; set; }
}
And I add items as
using (var _ = new WorkbookViewLock(workbookView1))
{
var worksheet = workbookView1.ActiveSheet.Workbook.Worksheets[0];
worksheet.AddItem<Dummy>(dummy);
worksheet.AddItem<Dummy>(dummy2);
}

If you are starting out with a blank worksheet (are you?), then it would make sense that the first two checks you make to worksheet.UsedRange.RowCount in your code would have a value of 1.
This is because the UsedRange of a blank worksheet will always be A1, which would correspond to a UsedRange.RowCount value of 1. That accounts for a value of 1 for your first AddItem(...) call.
The second AddItem(...) call is now looking at a worksheet that is populated with data, but still only 1 row since you've only added a single Dummy object at this point.
If you were to add a third Dummy object, you would see that the UsedRange increments to a value of 2.
FYI: You might have an additional issue with with your worksheet.UsedRange.Insert(...) line, since this will insert the number of rows that UsedRange currently consists of. It seems to me that if you are adding just a single Dummy object with this extension method, you should only insert, at most, one row...and that depends on where you want each new Dummy row to get added to the worksheet--the top or bottom of the used range. If you are inserting the new Dummy object at the top of the UsedRange, you should only apply Insert(...) on the top row of the UsedRange. If you are inserting the new Dummy object at the bottom of the UsedRange, no Insert(...) call is necessary at all since there's nothing below the UsedRange to shift down.

Related

What collection should i use if i need to control the position of item and need to be able to serialize the collection

i need a collection and i am not sure which one to use. I have used List before but i need to also be sure about the specific position. If user views an item A i will ads it to the collection and if he sees another item B i will add that item on top of the first one and so on, but the limit number fot he items is 3 so i would remove the first item, also i need to be able to seriliaze the collection. I have tried Dictionary, but i could use XmlSerializer, so i have tried to use Lst<KeyValue<>> and now i am trying an array like this. Also had a look on Queue but i have found out that using XmlSerializer could also be an issue. Any suggestion for what collection i can use?
class Program
{
static void Main(string[] args)
{
string[] myObjArray = new string[3] ;
if(myObjArray[0] == null)
{
myObjArray[0] = "article1";
Console.WriteLine(myObjArray[0]);
}
if (myObjArray[1] == null )
{
myObjArray[1] = "article2";
Console.WriteLine(myObjArray[1]);
}
if (myObjArray[2] == null)
{
myObjArray[2] = "article3";
Console.WriteLine(myObjArray[2]);
}
var input = Console.ReadLine();
myObjArray[0] = input;
Console.WriteLine(myObjArray[0]);
}
}
You can use a List<Item> and use the Index as position and methods Insert and Delete to achieve your goal. If the position if encapsulated in the entity, you can create methods to manage it.
So when you add an item you will check if the count is over than the allowed and delete the first if nedeed.
[Serializable]
public class MyList
{
private readonly List<Item> Items = new List<Item>();
public int Count { get { return Items.Count; } }
public int MaxCount { get; set; } = 0;
public void Add(Item item)
{
if ( MaxCount > 0 && Items.Count >= MaxCount )
Items.RemoveAt(0);
Items.Add(item);
}
public void Insert(int index, Item item)
{
Items.Insert(index, item);
}
public int FindById(int id)
{
for ( int index = 0; index < Items.Count; index++ )
if ( Items[index].Id == id )
return index;
return - 1;
}
// Add all over new methods and wrapping methods needed
}
This code use 0 to indicate that the max count is not considered, but if the list may not accept items, it can manage -1 for that, so 0 indicates that the list is closed.
Perhaps you can use a LinkedList that is searializable but you need to implement it for XML:
https://learn.microsoft.com/dotnet/api/system.collections.generic.linkedlist-1
How to Xml serialize a LinkedList?
So with that you can easily manage items as you wrote:
Add a cell between two cells.
Add a cell before a cell.
Add a cell after a cell.
Add a cell at the start.
Add a cell at the end.
Remove the first.
Remove the last.
And so on...
Hence you can add automatic delete the first cell if the count is over the allowed.
When should I use a List vs a LinkedList
LinkedList in C# - tutorialspoint.com
Linked Lists - dotnetcademy.net
C# | LinkedList Class - geeksforgeeks.org
Linked List Implementation in C# - geeksforgeeks.org

Even same strings does not match

Hello i have a datagridview which has datasource of list and this list is:
public class UniqueNounWithFreq
{
public int freq { get; set; }
public string word { get; set; }
public UniqueNounWithFreq(string word, int freq)
{
this.freq = freq;
this.word = word;
}
}
if (e.KeyChar == (char)13)
{
foreach (DataGridViewRow item in dataGridView_w2wm2.Rows)
{
if (!item.Cells[2].Value.ToString().Contains(textBox1.ToString().ToLower()))
{
item.Visible = false;
}
else
{
item.Visible = true;
}
}
}
When I want to hide a row with key press it throws
Row associated with the currency manager's position cannot be made invisible exception
Which you can see here : Unable To set row visible false of a datagridview. I tried the method sugested there but it did not work for me. Also when I check the lengths of the strings I wrote even if they are same they does not match. if you can help me I appreciate that.
Using textBox1.ToString() will generate something like "System.Windows.Controls.TextBox: TextBox" - it will create a string of the control type.
You should be using textBox1.Text to get the actual contents of the textbox - it is a string, so does not need converting.
Following on from PeterBruins comment using .Contains(textBox1.Text, StringComparer.CurrentCultureIgnoreCase) would be better than converting to lower case.
You could simplify setting the Visible property without use of an if statement to :
item.Visible = item.Cells[2].Value.ToString().Contains(textBox1.Text,
StringComparer.CurrentCultureIgnoreCase);

DataGridView with covariance?

I have code like below (I've generified and reduced it to represent just the issue at hand). The code works, that is it takes in a DataGridView.DataSource and ulitmately, using EPPlus, outputs the data to an Excel file. My question relates to covariance and how to use it, I think.
So you see it builds newList based on the type that it has found in the DataSource. Then a little further down it adds the data using the Properties, someClassObject.Name, .Address and .Phone that are unique to this type.
My problem is that there are about 75 different classes that could be passed in through the DataGridView parameter. Each class has its own unique properties (i.e. not necessarily Name, Address, Phone) though all of the objects in given DataGridView.DataSource are of the same class.
I could have a giant switch statement based on type.FullName and then each would have its own for loop to assign the Property values to the cell. That would work but would be incredibly cumbersome. Is there a better way to do this?
public void openExcelReport(ref DataGridView dataGridView, bool bolSave = false, bool bolOpen = true, string pageTitle = "EXPORTED DATA")
{
// put dataGridView.DataSource into a List
object firstItem = null;
var myDataSource = dataGridView.DataSource;
var myList = ((System.Windows.Forms.BindingSource)dataGridView.DataSource).List;
firstItem = ((System.Collections.IList)myList)[0];
var type = firstItem.GetType();
Type PROJECT1_TYPE = typeof(Project1.SomeClass);
Type PROJECT2_TYPE = typeof(Project2.SomeOtherClass); // many more of these
dynamic newList = null;
if (type.FullName.Equals(PROJECT1_TYPE.FullName))
{
newList = new List<Project1.SomeClass>();
foreach (Project1.SomeClass someClassObject in myList)
{
newList.Add(someClassObject);
}
}
ExcelPackage package = new ExcelPackage();
using ((package)) // use EPPlus
{
// Create the worksheet
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Worksheet 1");
// Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
System.Data.DataTable dataTable = new System.Data.DataTable();
dataTable.Columns.Add("Id");
dataTable.Columns.Add("FirstColumn", typeof(string));
dataTable.Columns.Add("SecondColumn", typeof(string));
dataTable.Columns.Add("ThirdColumn", typeof(string));
dataTable.Columns[0].AutoIncrement = true;
var column_id = 0;
foreach (Project1.SomeClass someClassObject in "FirstColumn")
{
DataRow dataRow = dataTable.NewRow();
dataRow["FirstColumn"] = someClassObject.Name;
dataRow["SecondColumn"] = someClassObject.Address;
dataRow["ThirdColumn"] = someClassObject.Phone
dataTable.Rows.Add(dataRow);
column_id += 1;
}
// worksheet is now populated, so save Excel File
...
}
Instead of doing the DataRow creation within this function, you could move it out to the class implementations using a common interface to enforce it, for instance:
public interface DataRowConvertable
{
DataRow GetDataRow();
}
public class SomeClass : DataRowConvertable
{
public SomeClass() { }
public SomeClass(string name, string address, string phone)
{
Name = name;
Address = address;
Phone = phone;
}
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public DataRow GetDataRow()
{
DataRow row = GetDataTable().NewRow();
row["Name"] = this.Name;
row["Address"] = this.Address;
row["Phone"] = this.Phone;
return row;
}
public static DataTable GetDataTable()
{
DataTable table = new DataTable("SomeClassTable");
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Address", typeof(string));
table.Columns.Add("Phone", typeof(string));
return table;
}
}
You could take it further, but this should give you a good alternative and a place to start. You can either leave the GetDataTable function public, and use that as well to create your table instance, or make it private and only use it internally. I would opt for the former and use that in your function to initialize the table before filling it. You could even get rid of the static modifier and add it to your interface, but I prefer the static usage of it in this instance since it is not reliant on the instance of the class and the data involved, only on the structure.
Either way, you could then change the code you have above to look like this:
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Worksheet 1");
System.Data.DataTable dataTable = Project1.SomeClass.GetDataTable();
foreach (Project1.SomeClass someClassObject in myList)
{
dataTable.Rows.Add(someClassObject.GetDataRow());
}
If you need an incremented id column, you could easily add that in the GetDataTable/GetDataRow functions and update them just as you were above.
This is just a quick example, it could very likely be cleaned up and optimized some, but it still conveys the idea. Hope it helps you out some.

Adding to columns based on length

I have a ListView with two columns, Boxes and Files. I'm adding items to a list of strings, and then populating the ListView with that list of strings. I want to make it so all items that are 8 characters long go into the Boxes column and all items that are 9 characters go into the Files column. So far, I've tried to iterate through using a for loop and utilize an if else statement to add the items, but I seem to be doing something wrong. Here's my current code:
public void PopulateItemsList()
{
BoxAndFileList.Items.Clear();
ScanIdBox.Text = string.Empty;
for (int i = 0; i < BoxNumberRepository._boxAndFileList.Count; i++)
{
var item = BoxNumberRepository._boxAndFileList.Item[i];
if (item.Length == 8)
{
BoxAndFileList.Items.Insert(0, item);
}
else
{
BoxAndFileList.Items.Insert(1, item);
}
}
}
I'm iterating through my list (_boxAndFileList) and trying to utilize Insert() to insert items into the specific index of the columns (Boxes is 0, Files is 1). I can clearly see that Item is a legitimate property of a string list, yet VS keeps saying that list contains no definition of it. How can I go about doing this? And also, I haven't received outside feedback on this way of doing things yet, so if there's a better way, please let me know.
Edit: BoxNumberRepository is a class that news up a list called _boxAndFileList. Code below:
public class BoxNumberRepository : Scan_Form
{
public static List<string> _boxAndFileList = new List<string>();
public void AddItem(string item)
{
_boxAndFileList.Add(item);
}
public void Delete(string item)
{
_boxAndFileList.Remove(item);
}
public IEnumerable<string> GetAllItems()
{
return _boxAndFileList;
}
}
Thanks to Alessandro D'Andria for that suggestion. That was correct. However, all the items are still just adding to the first column, even if they're 9 characters. How can I get 9 character items to add to the second column?
The problem that you are having is that you have to add both the box and file to the list item at the same time.
EDIT: Changed cartesian product to a left outer join.
EDIT: Added comments and fixed a syntax bug
private List<string> _boxAndFileList = new List<string> { "12345678", "123456789", "1234", "123456778" };
public void PopulateItemsList()
{
//clear the list
BoxAndFileList.Items.Clear();
//add the labels to the top of the listbox
BoxAndFileList.Columns.Add("Boxes");
BoxAndFileList.Columns.Add("Files");
//set the view of the list to a details view (important if you try to display images)
BoxAndFileList.View = View.Details;
//clear scan id box
ScanIdBox.Text = string.Empty;
//get all the items whos length are 8 as well as a unique id (index)
var boxes = _boxAndFileList.Where(b => b.Length == 8).Select((b, index) => new { index, b }).ToList();
//get all the items whos length are NOT 8 as well as a unique id (index)
var files = _boxAndFileList.Where(f => f.Length != 8).Select((f, index) => new { index, f }).ToList();
//join them together on their unique ids so that you get info on both sides.
var interim = (from f in files
join b in boxes on f.index equals b.index into bf
from x in bf.DefaultIfEmpty()
select new { box = (x == null ? String.Empty : x.b), file = f.f });
//the real trick here is that you have to add
//to the listviewitem of type string[] in order to populate the second, third, or more column.
//I'm just doing this in linq, but var x = new ListViewItem(new[]{"myBox", "myFile"}) would work the same
var fileboxes = interim.Select(x => new ListViewItem(new []{ x.box, x.file})).ToArray();
//add the array to the listbox
BoxAndFileList.Items.AddRange(fileboxes);
//refresh the listbox
BoxAndFileList.Refresh();
}
Your _boxAndFileList is a List<string> so you should be declare item as string type instead var type:
string item = BoxNumberRepository._boxAndFileList.Item[i];
All your code should be like this:
public void PopulateItemsList()
{
BoxAndFileList.Items.Clear();
ScanIdBox.Text = string.Empty;
for (int i = 0; i < BoxNumberRepository._boxAndFileList.Count; i++)
{
string item = BoxNumberRepository._boxAndFileList.Item[i];
if (item.Length == 8)
{
BoxAndFileList.Items.Insert(0, item);
}
else
{
BoxAndFileList.Items.Insert(1, item);
}
}
}

Get index with value in Checked List Box

I am actually finding that chkContactType.Items is empty when I step through the code. I even added a Watch to chkContactType.Items.Count and it is never anything but 0. I am severly confused now as it obviously isn't as my Insert method works fine which uses these same boxes and inserts the Value Member for each item....
I have some checked list box controls that I need to set the CheckState based on the item value as that is what is stored in the DB for an exsiting record. Unfortunately, I only see a way to set this by index which is not stored. The index is local to the control so, for example, control ContactType has 15 items in it. Index is 0-14. Item Value is 39,40,41,42,43,44,45,46,47,48,49,50,2077,2078,2079 respectively. How can I either get the index value with the Value Member value OR set the checkstate of each returned item with the Value Member value?
Thanks
private void PaintDetails(Guid cNoteID)
{
var cNoteDetailDT = CurrentCaseNote.GetCNoteDetail(cNoteID);
LoadCaseNoteDetailData(cNoteDetailDT.Rows[0]);
// Load Contact Type Data for this CaseNote
// contactTypeDT returns ItemID of chk items
// that were checked for this Guid
using (var contactTypeDT = CurrentCaseNote.GetCNoteContactType(cNoteID))
{
if (contactTypeDT.Rows.Count > 0)
foreach (DataRow row in contactTypeDT.Rows)
{
LoadContactTypeData(row);
}
}
}
private void LoadContactTypeData(DataRow row)
{
// This does not work
var theItem = row["ItemID"].ToString();
// itemIndex always ends up set to -1
var itemIndex = chkContactType.Items.IndexOf(theItem);
chkContactType.SetItemChecked((int) itemIndex, true);
// This works I just need to supply the correct index
chkContactType.SetItemChecked(0,true);
}
EDIT in response to comment
This is how I populate the Checked ListBox. I know there is a "magic number" there. I am working on it. It relates to the CategoryID in the DB of ContactType.
// Contact Type Check List Box
chkContactType.DataSource = CurrentCaseNote.GetMaintItems(1);
chkContactType.DisplayMember = "ItemDescription";
chkContactType.ValueMember = "ItemID";
and then CurrentCaseNote BLL(kinda)-->
public static DataTable GetMaintItems(int iCat)
{
IQueryable<tblCaseNotesMaintItem> tItems = CaseNoteDAL.GetCNTable();
return (tItems.Where(item => item.CategoryID == iCat & item.IsActive).OrderBy(
item => item.OrderID).Select(item => new {item.ItemID, item.ItemDescription})).CopyLinqToDataTable();
}
and finally the DAL -->
public static Table<tblCaseNotesMaintItem> GetCNTable()
{
return dcCaseNotes.GetTable<tblCaseNotesMaintItem>();
}
EDIT 2
This is what my code NOW looks like but still no go. It is like ItemCount is never populated....
// Load Contact Type Data for this CaseNote
using (var contactTypeDT = CurrentCaseNote.GetCNoteContactType(cNoteID))
{
if (contactTypeDT.Rows.Count > 0)
foreach (DataRow row in contactTypeDT.Rows)
{
LoadContactTypeData(row);
}
}
}
private void LoadContactTypeData(DataRow row)
{
// This does not work
var theItem = row["ItemID"];
for (int i = 0; i < chkContactType.ItemCount; i++)
{
if(theItem == chkContactType.GetItemValue(i))
chkContactType.SetItemChecked(i,true);
}
}
This seems to work:
int index = checkedListBox1.Items.IndexOf("42");
checkedListBox1.SetItemChecked(index, true);
For j As Integer = 0 To chklst_distributorlist.Items.Count - 1
If chklst_distributorlist.GetItemText(chklst_distributorlist.Items.Item(j)) = ds1.Tables(0).Rows(0)("suppliername").ToString Then
chklst_distributorlist.SetSelected(j, True)
chklst_distributorlist.SetItemCheckState(j, CheckState.Checked)
End If
Next
I also faced the same issue.
The index of checkedListBox1.Items.IndexOf("value"); is always -1.
I managed to get the index using following code.
for (int i = 0; i < checkedListBox1.Items.Count; i++)
{
if (((System.Data.DataRowView)(checkedListBox1.Items[i])).Row.ItemArray[0].ToString() == "Value")
checkedListBox1.SetItemCheckState(i, CheckState.Checked);
}

Categories

Resources