I am using a TreeListView (ObjectListView) http://objectlistview.sourceforge.net/cs/index.html - and populated it with a number of items. One of the columns I made editable on double click for user input. Unfortunately, the editing is extremely slow and going from one cell edit in the Qty column (see picture further below) to the next cell edit takes about 5-10 seconds each time. Also, the cell editor takes a while to appear and disappear. Below is the code I use to populate the TreeListView:
TreeListView.TreeRenderer renderer = this.treeListView.TreeColumnRenderer;
renderer.LinePen = new Pen(Color.Firebrick, 0.5f);
renderer.LinePen.DashStyle = DashStyle.Solid;
renderer.IsShowLines = true;
treeListView.RowFormatter = delegate(OLVListItem olvi)
{
var item = (IListView)olvi.RowObject;
if (item.ItemType == "RM")
olvi.ForeColor = Color.LightSeaGreen;
};
treeListView.CanExpandGetter = delegate(object x)
{
var job = x as IListView;
if (job != null)
{
if (job.ItemType == "PA" || job.ItemType == "JC")
{
var rm = job.ItemPart.GetRawMaterial();
var subParts = job.ItemPart.SubParts.Where(v => v != null).ToList();
if (rm.Count > 0|| subParts.Count > 0)//
return true;
}
}
return false;
};
this.treeListView.ChildrenGetter = delegate(object x)
{
try
{
var job = x as IListView;
if (job != null)
{
if (job.ItemType == "PA" || job.ItemType == "JC")
{
var part = job.ItemPart;
var rm = part.GetRawMaterial();
var subParts = part.SubParts.Where(v => v != null).ToList();
var items = new List<IListView>();
items.AddRange(subParts.GetRange(0, subParts.Count).ToList<IListView>());
items.AddRange(rm.GetRange(0, rm.Count).ToList<IListView>());
return items;
}
}
return null;
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show(this, ex.Message, "ObjectListViewDemo", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return null;
}
};
var lItems= jobs.ToList<IListView>();
treeListView.SetObjects(lItems );
Expand(lItems[0]);
treeListView.RebuildAll(true);
}
public void Expand(object expItem)
{
treeListView.ToggleExpansion(expItem);
foreach (var item in treeListView.GetChildren(expItem))
{
Expand(item);
}
}
Here is a picture of the cell editing:
Why is the editing so very slow? Am I doing something wrong? What can I do to make it faster?
In your delegates you're using linear searches and several list copies (also linear). And this is for each item.
Bad performance is to be expected.
If you want to improve on this, you can pre-calculate the results instead.
Related
I've made a small tool bar that sits in a transparent form, it loads a variable sized menu from a text file and can be changed on the fly. Each button is a type of Label, the bar is just a list of buttons and adds/removes them in the correct spots. Width of the form is only a little bigger than the menu bar so that sub menu isn't cut off
Everything is working sweet except, when I reload everything part of the toolbar is lost. I've attempted to change the width so many ways, I've cleared and removed the controls from the form, refreshing the form/menu, updating it etc however nothing seems to make it work as intended EXCEPT if I call the reload function twice in a row, it works. I can't see why calling it once doesn't work but calling it twice works.
I'm fine with calling reload twice in a row as it would only be called a couple times a week.
Question: what on earth is causing this?
photo of issues first photo shows what it should look like, second is after removing a menu button and reloading, third is after adding a button and reloading
//calling this.reload() doesn't work
//calling this.reload();this.reload() works
void reload(Object o = null, EventArgs e = null)
{
this._menuBar.clear();
this.loadFromFile();
}
void loadFromFile(Object o = null, EventArgs e = null)
{
try
{
if (File.Exists("kpi.txt"))
{
string cline = "", cmenu = "", lhs = "";
menuList mb = null;
StreamReader sr = new StreamReader("kpi.txt");
while (!sr.EndOfStream)
{
cline = sr.ReadLine(); //get current line
if (cline.Length > 0 && cline[0] != ';')
{
//check if main menu/command
if (cline[0] == '[')
{
cmenu = Regex.Match(cline, #"(?<=^\[)[a-zA-Z -\#_{-~\^\r\n]+(?=\])").Value;
if (cmenu != "")
{
mb = this._menuBar.addMenuButton(cmenu);
mb.data["options"] = Regex.Match(cline, #"\/\w+$").Value;
var match = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
mb.data["count"] = (match.Success ? match.Value : "0");
mb.data["copy"] = "";
applyMenuOptions(mb, false);
}
}
//just a standard line
else
{
cline = cline.Trim();
lhs = Regex.Match(cline, #"^[^\;\<\[\]\r\n]+(?=$|\<|\;)").Value;
if (mb.getSubMenuItem(lhs) == null)
{
var newButton = mb.addSubMenu(lhs);
if (newButton != null)
{
newButton.parent = mb;
newButton.data["options"] = mb.data["options"];
newButton.data["copy"] = Regex.Match(cline, #"((?<=\;)[^\[\]\<\r\n]+(?=<|$))").Value;
var matches = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
int intout = 0;
if (int.TryParse(matches.Value, out intout))
{//no description
newButton.data["description"] = "";
newButton.data["count"] = intout.ToString();
}
else
{
newButton.data["description"] = matches.Value;
newButton.data["count"] = (matches.NextMatch().Success ? matches.NextMatch().Value : "0");
}
applyMenuOptions(newButton);
newButton.addMiddleClick(this.addcopy);
if (newButton.data["options"].Contains("i"))
{
newButton.addRightClick(this.appendInfo);
newButton.addRightClick(this.increment);
}
}
}
}
}
}
sr.Close();
this._menuBar.squish();
this.Width = this._menuBar.Width+50;
}
else
{
menuList mb = this._menuBar.addMenuButton("menu");
mb.data["options"] = "\\m";
mb.data["count"] = "0";
mb.data["copy"] = "";
mb.data["description"] = "";
applyMenuOptions(mb, false);
saveDictonary();
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to load data " + ex);
//ILog log = LogManager.GetLogger(typeof(Program));
//log.Info(ex);
}
}
public menuList addMenuButton(string s, int w = 0, int h = 0, int x = -1, int y = -1)
{
menuList mb = new menuList(this._form, s);
if (this.menuItems.Exists(z => z.Text == s)) return null;
mb.Width = (w==0?settings.intOf("ButtonWidth"):w);
mb.Height = (h==0?settings.IntOf("ButtonHeight"):h);
if (x == -1 || y == -1)
mb.Location = new Point(this.menuItems.Count > 0 ? this.menuItems.Last().Location.X + this.menuItems.Last().Width : padding);
else mb.Location = new Point(x, y);
mb.BringToFront();
mb.Show();
this.menuItems.Add(mb);
// this.Refresh();
return mb;
}
internal void clear()
{
foreach(var i in this.menuItems)
{
this._form.Controls.Remove(i);
i.clear();
i.Dispose();
}
this.menuItems.Clear();
this._form.Controls.Remove(this);
this.menuItems = new List<menuList>();
this._form.Controls.Add(this);
}
internal void squish()
{
try
{
this.Width = (this.menuItems.Count * this.menuItems.First().Width) + (2 * padding);
}
catch(Exception ex) { MessageBox.Show(""+ex); }
}
Found the culprit, bother the button class and the tool bar class were both adding themselves to the form control instead of button class adding to the tool bar (picture box) controls!
Removing transparency showed the buttons not moving when the tool bar was moved!
I am currently working on a search for a website and I am stuck with a problem, which doesn't even seem that hard to solve. I'm just not able to figure the solution out myself.
Situation: In the current state of my search I can sort the events to dates (I can select October for instance and it will only show the events which happen during october), for categories (The events each have categories) and even search text. Now when I select october as a month and festival as a category, I get all events occuring in october AND all festivals. I want only the festivals which occur in october. Any ideas on how I could achieve this?
This is what I've got so far:
var validEvents = new List<Item>();
var allEvents = ((LinkField)this.controlItem.Fields["Event Container"]).TargetItem.Children.ToList(); // getting the events
if (this.ddlMonths.SelectedIndex != 0 || this.ddlCategories.SelectedIndex != 0 || this.searchQuery.Text != string.Empty)
{
foreach (var currentEvent in allEvents)
{
var isValid = false;
// 1. Check for months
if (this.ddlMonths.SelectedIndex != 0)
{
// .. validation if event should be displayed
if (startDate <= monthDates[1].Date && endDate >= monthDates[0].Date)
{
isValid = true;
}
}
// 2. Check for categories
if (this.ddlCategories.SelectedIndex != 0)
{
foreach (var eventCategory in ((MultilistField) currentEvent.Fields["Categories"]).GetItems())
{
if (eventCategory["Category"].ToLower() == this.ddlCategories.SelectedValue.ToLower())
{
isValid = true;
}
}
}
// 3. Check for search query
if (this.searchQuery.Text != string.Empty)
{
var searchText = this.searchQuery.Text;
if (currentEvent["Title"].Contains(searchText) || currentEvent["Text"].Contains(searchText))
{
isValid = true;
}
}
if (isValid)
{
validEvents.Add(currentEvent);
}
}
}
Using LINQ you can combine search conditions as follows
var allEvents = ...;
IEnumerable<Item> events = allEvents;
if (ddlMonths.SelectedIndex != 0)
events = events.Where(condition1);
if (ddlCategories.SelectedIndex != 0)
events = events.Where(condition2);
if (searchQuery.Text != string.Empty)
events = events.Where(condition3);
var validEvents = events.ToList();
I would put
var isValid = true;
in line 8, then if any of the conditions is NOT met set it to false. And also skip any following check if isValid is already false.
Hope it helps!
EDIT - Complete code:
var validEvents = new List<Item>();
var allEvents = ((LinkField)this.controlItem.Fields["Event Container"]).TargetItem.Children.ToList(); // getting the events
if (this.ddlMonths.SelectedIndex != 0 || this.ddlCategories.SelectedIndex != 0 || this.searchQuery.Text != string.Empty)
{
foreach (var currentEvent in allEvents)
{
var isValid = true;
// 1. Check for months
if (this.ddlMonths.SelectedIndex != 0)
{
// .. validation if event should be displayed
if (!(isValid && startDate <= monthDates[1].Date && endDate >= monthDates[0].Date))
{
isValid = false;
}
}
// 2. Check for categories
if (this.ddlCategories.SelectedIndex != 0)
{
foreach (var eventCategory in ((MultilistField) currentEvent.Fields["Categories"]).GetItems())
{
if (!(isValid && eventCategory["Category"].ToLower() == this.ddlCategories.SelectedValue.ToLower()))
{
isValid = false;
}
}
}
// 3. Check for search query
if (this.searchQuery.Text != string.Empty)
{
var searchText = this.searchQuery.Text;
if (!(isValid && currentEvent["Title"].Contains(searchText) || currentEvent["Text"].Contains(searchText)))
{
isValid = true;
}
}
if (isValid)
{
validEvents.Add(currentEvent);
}
}
}
I'm trying to iterate through my RadGridView rows, but when I have more than 20 or 30 items, the loop doesn't get all rows.
For example: using this code in a radgridview with 5 items, I can get all of them and do whatever I want, but when my grid has more than 20 items, it gets only 10 rows. Is this a bug or something like that? How can I solve it?
Here's my code:
private List<object> ReturnListFounds(string text)
{
List<object> a = new List<object>();
foreach (var item in myGrid.Items)
{
if (item == null)
continue;
GridViewRow row = myGrid.ItemContainerGenerator.ContainerFromItem(item) as GridViewRow;
if (row == null)
continue;
foreach (GridViewCell cell in row.Cells)
{
if (cell != null && cell.Value != null)
{
string str = cell.Value.ToString();
if (str.Equals(text, StringComparison.InvariantCultureIgnoreCase) || str.ToLower().Contains(text.ToLower()))
{
a.Add(row.Item);
break;
}
}
}
}
return a;
}
#Edit
I found out the problem. The thing is: the method "ItemContainerGenerator.ContainerFromItem(item) as GridViewRow" returns null if the item is outside of the view area. But I'm using this method in a grid containing 123 items and I can only get the row for the 20 first items.
I need to be able to get all of the items, not just the ones in the view area. I have already tried to set the virtualization false (EnableRowVirtualization = false; EnableColumnVirtualization = false;), but it didin't work as well.
Is there a way of getting all of the rows using this method?
Have you tried this?
var rows = StrategyGridView.ChildrenOfType<GridViewRow>();
It works fine for me. Hope it helps!
I tried a lot of things to make this work and I found one. It's not the best way of doing this, but it works. I anyone has anything better, just post here! Share with us!
private List<object> ReturnListFounds(string text)
{
List<object> result = new List<object>();
for (int l = 0; l <= Items.Count; l++)
{
var cell = new GridViewCellInfo(this.Items[l], this.Columns[0], this);
if (cell.Item != null)
{
var props = cell.Item.GetType().GetProperties();
foreach (var p in props)
{
if (p == null || cell.Item == null)
continue;
var t = p.GetValue(cell.Item);
if (t == null)
continue;
var str = t.ToString();
if (str.Equals(text, StringComparison.InvariantCultureIgnoreCase) || str.ToLower().Contains(text))
{
result.Add(cell.Item);
}
}
}
}
result = new List<object>(result.Distinct());
return result;
}
I have a bit of a pickle. There are a list of images I want to grab on a website. I know how to do that much, but I have to filter out the location of the images.
Such as I'd want to grab the images in a div tag with an id "theseImages", but there are another set of images within another div tag with an id called "notTheseImages". Looping through every tag into ah HtmlElementCollection with the tag "img" would ignore the divs, because it'd also grab the images from "notTheseImages."
Is there a way I could loop through the images while doing a check to see where those images are located in the div tags?
This could help you to do the selection of your current HTML and maybe for future occassions :)
protected HtmlElement[] GetElementsByParent(HtmlDocument document, HtmlElement baseElement = null, params string[] singleSelectors)
{
if (singleSelectors == null || singleSelectors.Length == 0)
{
throw new Exception("Please give at least 1 selector!");
}
IList<HtmlElement> result = new List<HtmlElement>();
bool last = singleSelectors.Length == 1;
string singleSelector = singleSelectors[0];
if (string.IsNullOrWhiteSpace(singleSelector) || string.IsNullOrWhiteSpace(singleSelector.Trim()))
{
return null;
}
singleSelector = singleSelector.Trim();
if (singleSelector.StartsWith("#"))
{
var item = document.GetElementById(singleSelector.Substring(1));
if (item == null)
{
return null;
}
if (last)
{
result.Add(item);
}
else
{
var results = GetElementsByParent(document, item, singleSelectors.Skip(1).ToArray());
if (results != null && results.Length > 0)
{
foreach (var res in results)
{
result.Add(res);
}
}
}
}
else if (singleSelector.StartsWith("."))
{
if (baseElement == null)
{
baseElement = document.Body;
}
foreach (HtmlElement child in baseElement.Children)
{
string cls;
if (!string.IsNullOrWhiteSpace((cls = child.GetAttribute("class"))))
{
if (cls.Split(' ').Contains(singleSelector.Substring(1)))
{
if (last)
{
result.Add(child);
}
else
{
var results = GetElementsByParent(document, child, singleSelectors.Skip(1).ToArray());
if (results != null && results.Length > 0)
{
foreach (var res in results)
{
result.Add(res);
}
}
}
}
}
}
}
else
{
HtmlElementCollection elements = null;
if (baseElement != null)
{
elements = baseElement.GetElementsByTagName(singleSelector);
}
else
{
elements = document.GetElementsByTagName(singleSelector);
}
foreach (HtmlElement item in elements)
{
if (last)
{
result.Add(item);
}
else
{
var results = GetElementsByParent(document, item, singleSelectors.Skip(1).ToArray());
if (results != null && results.Length > 0)
{
foreach (var res in results)
{
result.Add(res);
}
}
}
}
}
return result.ToArray();
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// here we can query
var result = GetElementsByParent(webBrowser1.Document, null, "#theseImages", "img");
}
result would then contain the images that are under #theseImages
Mind you the GetElementsByParent is fairly untested, I just tested it for your use case and it seemed to be ok.
Don't forget to only start the query once you are sure the document is completed ;)
I know you can't modify a collection during a foreach, but I should be able to set variable values of the underlying iterator through it. For some reason the method below, every time it executes is giving be the "Collection was modified..." error:
private static IInstrument AdjustForSimpleInstrument(DateTime valueDate, IInstrument temp)
{
var instr = temp;
foreach (var component in instr.Components)
{
component.Schedule.ScheduleRows.RemoveAll(
sr =>
((sr.Payment != null) && (sr.Payment.PaymentDate != null) &&
(sr.Payment.PaymentDate.AdjustedDate.Date <= valueDate.Date)));
if (
!component.ScheduleInputs.ScheduleType.In(ComponentType.Floating, ComponentType.FloatingLeg,
ComponentType.Cap, ComponentType.Floor)) continue;
foreach (var row in component.Schedule.ScheduleRows)
{
var clearRate = false;
if (row.Payment.CompoundingPeriods != null)
{
if (row.Payment.CompoundingPeriods.Count > 0)
{
foreach (
var period in
row.Payment.CompoundingPeriods.Where(
period => ((FloatingRate)period.Rate).ResetDate.FixingDate > valueDate))
{
period.Rate.IndexRate = null;
clearRate = true;
}
}
}
else if (row.Payment.PaymentRate is FloatingRate)
{
if (((FloatingRate)row.Payment.PaymentRate).ResetDate.FixingDate > valueDate)
clearRate = true;
}
else if (row.Payment.PaymentRate is MultipleResetRate)
{
if (
((MultipleResetRate)row.Payment.PaymentRate).ChildRates.Any(
rate => rate.ResetDate.FixingDate > valueDate))
{
clearRate = true;
}
}
if (clearRate)
{
row.Payment.PaymentRate.IndexRate = null;
}
}
}
return temp;
}
Am I just missing something easy here? The loop that is causing the exception is the second, this one:
foreach (var row in component.Schedule.ScheduleRows)
I suspect this is not .NET-framework stuff, so I assume that row is connected to its collection. Modifying the contents of the row, might shift its place inside its collection, thus modifying the collection, which is not allowed during some foreach-operations.
The solution is simple: create a copy of the collection (by using LINQ).
foreach (var row in component.Schedule.ScheduleRows.ToList())
...