I'm writing a little WPF Apllication where I track some Items. There are a total of 30+ Items, that can be tracked, but none of them have to. This is reflected in a List I populate in the settings section (the TrackedItems object below).
There is a total of 10 Stackpanels I use to display the items. I've made a Collection for them for easier access (StackedpanelsForTrackedItems)
The User has the Option to reduce the panels that are displayed to 3, 5 or 10 (Preferences.NumberNodesDisplayed contains that number) to reduce clutter. So regardless of how many items are tracked, there should never be more SPs visible than this option allows.
Also the application should recognize if the amount of items, that are currently tracked is fewer than the currently selected option.
For example, if i currently only track 6 items, but option is set to 10, it should only display the first 6 SPs regardless.
I came up with this logic:
Collection<UIElement> StackpanelsForTrackedItems = new Collection<UIElement>();
foreach (UIElement element in StackpanelItemParent.Children)
{
if (element is StackPanel)
{
StackPanel sp = (StackPanel)element;
StackpanelsForTrackedItems.Add(sp);
}
}
int i = 0;
foreach (UIElement item in StackpanelsForTrackedItems)
{
if ((i < TrackedItems.Count) && (i < Preferences.NumberNodesDisplayed))
item.Visibility = Visibility.Visible;
else
item.Visibility = Visibility.Collapsed;
i++;
}
Now if i select lets say 6 Items (which means TrackedItems.Count = 6) and I choose 10 Items to be displayed at maximum (which means Preferences.NumberNodesDisplayed = 10) it should still set the visibility of
StackpanelsForTrackedItems[6] to StackpanelsForTrackedItems[9] at 'collapsed', but it doesnt, it always displays every 10 SPs.
How can the if-statement be true if (i < TrackedItems.Count) is already false?
I think I have a major flaw in my logic, but I cant for my life find it.
Help please :)
Edit: as requested
<StackPanel x:Name="StackpanelItemParent" Orientation="Vertical">
<StackPanel x:Name="StackpanelItem1" Orientation="Horizontal">
// stuff here...
</StackPanel>
<StackPanel x:Name="StackpanelItem2" Orientation="Horizontal">
//stuff here...
</StackPanel>
<StackPanel x:Name="StackpanelItem3" Orientation="Horizontal">
// stuff here...
</StackPanel>
<StackPanel x:Name="StackpanelItem4" Orientation="Horizontal">
// stuff here...
</StackPanel>
<StackPanel x:Name="StackpanelItem5" Orientation="Horizontal">
// stuff here...
</StackPanel>
//
//
//
<StackPanel x:Name="StackpanelItem10" Orientation="Horizontal">
// stuff here...
</StackPanel>
</StackPanel>
The Collection gets populated correctly when i debug
StackpanelsForTrackedItems[0] would be StackpanelItem1
StackpanelsForTrackedItems[1] would be StackpanelItem2
StackpanelsForTrackedItems[2] would be StackpanelItem3 etc.
I see no error there :-/
EDIT2:
I found the error,and I'm just too dumb I guess
when I ran the app without breakpoints and changed the items to track, this behaviour above occured
so i went ahead an set breakpoints to track down all variables, this however prevented my app to execute the method, where the TrackedItems object gets populated again...
a simple TrackedItems.Clear() fixed everything, obviously once the method is called again later, there would be 12 items in that list and not 6 anymore
i feel really embarrased for asking the question now :(
at least I managed to track it down on my own, and I learned a thing or two from the comments, so it wasnt completely wasted I guess
thanks everybody for trying to help though, much appreciated!
I have simulated the logic into a list of Booleans. True means to be shown while false means hidden.
The following the user has selected 10 items to be tracked but six only exist:
var StackpanelsForTrackedItems = Enumerable.Range(0, 10)
.Select (e => true)
.ToList();
int tracked = 6;
int max = 10;
StackpanelsForTrackedItems.Select ((sp, index) => sp = (index < max) && (index < tracked));
Result:
For
int tracked = 6;
int max = 3;
Result
For the WPF visibility change
.Select ((sp, index) => sp.Visibility =
((index < max) && (index < tracked)) ? Visibility.Visible
: Visibility.Collapsed);
Related
I display data in a data grid and want to filter the data with range sliders (slider with two handles). The change-event of the range slider only sets the string variable filterTrigger. The filter itself is triggered via mouseup event.
private void ApplyFilter()
{
if (filterTrigger != "")
{
filteredData.Clear();
suitableData.ForEach((item) =>
{
filteredData.Add(item); // create not referenced copy of list suitableData that was created in time consuming calculations
});
switch (filterTrigger)
{
case "foo":
// remove too small and too large Foos
_ = filteredData.RemoveAll(x => x.Foo > fooRangeSliderHandlesMinMax.ElementAt(1) || x.Foo < fooRangeSliderHandlesMinMax.ElementAt(0));
// set new minimum and maximum of range of range slider
barRangeSliderMinimum = filteredData.Min(x => x.Bar) - 0.1;
barRangeSliderMaximum = filteredData.Max(x => x.Bar) + 0.1;
// set new position of range slider handles
barRangeSliderHandlesMinMax = new double[2] { Math.Max(barRangeSliderHandlesMinMax.ElementAt(0), barRangeSliderMinimum + 0.1), Math.Min(barRangeSliderHandlesMinMax.ElementAt(1), barRangeSliderMaximum - 0.1) };
break;
case "bar":
_ = filteredData.RemoveAll(x => x.Bar > barRangeSliderHandlesMinMax.ElementAt(1) || x.Bar < barRangeSliderHandlesMinMax.ElementAt(0));
fooRangeSliderMinimum = filteredData.Min(x => x.Foo) - 0.1;
fooRangeSliderMaximum = filteredData.Max(x => x.Foo) + 0.1;
fooRangeSliderHandlesMinMax = new double[2] { Math.Max(fooRangeSliderHandlesMinMax.ElementAt(0), fooRangeSliderMinimum + 0.1), Math.Min(fooRangeSliderHandlesMinMax.ElementAt(1), fooRangeSliderMaximum - 0.1) };
break;
default:
break;
}
// remove values of foo if filterTrigger was "bar" and vice versa
_ = filteredData.RemoveAll(x => x.Foo > fooRangeSliderHandlesMinMax.ElementAt(1) || x.Foo < fooRangeSliderHandlesMinMax.ElementAt(0) || x.Bar > barRangeSliderHandlesMinMax.ElementAt(1) || x.Bar < barRangeSliderHandlesMinMax.ElementAt(0));
// update data grid data
IFilteredData = filteredData;
dataGrid.Reload();
filterTrigger = "";
}
}
The code is working fluently when I comment out all the lines that start with a discard _. But of course, I need these lines. The problem is, that they need much processor power. It is still working but when I move the mouse with clicked handle of a filter, the handle is extremely lagging (and my laptop sounds like a helicopter).
I know that a part of the last filter is redundant, because when filterTrigger is foo, foo was already filtered. But filtering only what was not filtered before, will not alone solve the problem, because above I only show two filters but there are actually about ten filters.
So, is there a way I could optimize this code?
When optimizing code the first rule is to measure, preferably with a profiler that can tell you exactly what part of the code takes most of the time.
Second rule would be to use a optimal algorithm, but unless you have a huge number of items and some reasonable way to sort or index said items, linear time is the best you can do.
Here are some guesses and suggestions of things that might be improved:
Avoid using .ElementAt, this might create a new enumerator object, and that will take some time. Especially inside inner loops. Prefer to use indexers and/or store it in local variables instead.
Avoid using Linq. Linq is great for readability, but it will have some overhead. So when optimizing it might be worthwhile to use regular loops to see if the overhead is significant or not.
Try to do all processing in one go. Instead iterating over all items once to find the minimum and once to do the maximum, do both at the same time. Memory is slow, and by doing as much processing of an item as possible when it is already cached helps reduce memory traffic.
I would consider replacing RemoveAll with a loop that copies items that pass the check to a empty list. This should help ensure items are copied at most once.
A rule of thumb when optimizing is to use low level language features. These are often easier for the jitter to optimize well. But it may make the code harder to read, so use a profiler to optimize the places that need it the most.
I am using quad-tree structure for my data processing application in c#, it is similar to hashlife algorithm. Getting data N x N (eg. 2000 x 2000) dimension data from quad-tree is very very slow.
how can i optimize it for extracting large data from quad tree.
Edit:
Here is the code i used to extract the data in recursive manner
public int Getvalue(long x, long y)
{
if (level == 0)
{
return value;
}
long offset = 1 << (level - 2);
if (x < 0)
{
if (y < 0)
{
return NW.Getvalue(x + offset, y + offset);
}
else
{
return SW.Getvalue(x + offset, y - offset);
}
}
else
{
if (y < 0)
{
return NE.Getvalue(x - offset, y + offset);
}
else
{
return SE.Getvalue(x - offset, y - offset);
}
}
}
outer code
int limit = 500;
List<int> ExData = new List<int>();
for (int row = -limit; row < limit; row++)
{
for (int col = -limit; col < limit; col++)
{
ExData.Add(Root.Getvalue(row, col));
//sometimes two dimension array
}
}
A quadtree or any other structure isn't going to help if you're going to visit every element (i.e. level 0 leaf node). Whatever code gets the value in a given cell, an exhaustive tour will visit 4,000,000 points. Your way does arithmetic over and over again as it goes down the tree at each visit.
So for element (-limit,-limit) the code visits every tier and then returns. For the next element it visits every tier and then returns and so on. That is very labourious.
It will speed up if you make the process of adding to the list itself recursively visiting each quadrant once.
NB: I'm not a C# programmer so please correct any errors here:
public void AppendValues(List<int> ExData) {
if(level==0){
ExData.Add(value);
} else{
NW.AppendValues(ExData);
NE.AppendValues(ExData);
SW.AppendValues(ExData);
SE.AppendValues(ExData);
}
}
That will append all the values though not in the raster-scan (row-by-row) order of the original code!
A further speed up can be achieved if you are dealing with sparse data. So if in many cases nodes are empty or even 'solid' (all zero or one value) you could set the nodes to null and then use zero or the solid value.
That trick works well in Hashlife for Conway Life but depends on your application. Interesting patterns have large areas of 'dead' cells that will always propagate to dead and rarely need considering in detail.
I'm not sure what 25-40% means as 'duplicates'. If they aren't some fixed value or are scattered across the tree large 'solid' regions are likely to be rare and that trick may not help here.
Also, if you actually need to only get the values in some region (e.g. rectangle) you need to be a bit cleverer about how you work out which sub-region of each quadrant you need using offset but it will still be far more efficient than 'brute' force tour of every element. Make sure the code realises when the region of interest is entirely outside the node in hand and return quickly.
All this said if creating a list of all the values in the quad-tree is a common activity in your application, a quad-tree may not be the answer you need. A map simply mapping (row,col) to value is pre-made and again very efficient if there is some common default value (e.g. zero).
It may help to create an iterator object rather than add millions of items to a list; particularly if the list is transient and destroyed soon after.
More information about the actual application is required to understand if a quadtree is the answer here. The information provided so far suggests it isn't.
I'm aware (from similar posts) that infinite while loops are notorious for causing Unity3d to crash. I'm tring to impliment a while loop within something I'm working on, which I'm fairly sure isn't 'infinite', yet causes the game to crash.
The idea of the logic is to check a list of integers for consecutive numbers and use that as the basis to apply a bonus. The list contains 'effective shots', and has a new int added every time a shot is fired - the more consecutive effective shots, the higher the bonus.
Here's what I have:
int count = 0;
int firstItem = 0;
int multiples = 3;
int currentMultiple = 0;
int bonusX = 0;
foreach (int i in effectiveShots)
{
if (count == 0)
{
firstItem = i;
count = 1;
}
else if (i == firstItem + count)
{
count++;
}
}
while (count >= multiples)
{
currentMultiple = multiples;
if (count == currentMultiple + multiples)
{
bonusX += 1;
}
if (bonusX > 10 || gameOver)
break;
UnityEngine.Debug.Log(bonusX);
}
The logic to check for consective entries in the effectiveShots list was taken from #Jon Skeet's answer here. Though this appears to work, I think that this may be the issue. As soon as a shot is missed, count needs to be reset. Any ideas or suggestions?
The while loop should then be entered once the count of consecutive effective shots has reached the first multiple, i.e. 3 shots. Then, for every set of consequtive effective shots thereafter, increment the bonus, for example:
3 shots: bonusX = 1
6 shots: bonusX = 2
9 shots: bonusX = 3
12 shots: bonusX = 4
and repeat this until `count` is no longer >= 3, i.e. the player missed a shot.
The issue is that as soon as I hit 3 consequtive shots and enter this loop, the game crashes. I dont think I would call this an infinite loop, since missing a shot - setting count == 0 - would mean the while conditions are no longer true, so drop out of the loop (I think?). I also added an additional check to break out of the loop under certain circumstances, but this doesnt make a difference.
If you are able to give a steer as to how to fix this crashing, or have a suggestion on a better approach in general, it would be appreciated!
Nothing in your while loop changes the value of either count or multiples and so the condition will always evaluate to the same value => Infinite loop
Is there any way to get the index of the first item currently showing, when the list is scrollable?
I'm making a CharMap with some extensions and just found that ListView can't contain 64k items (see code below)
for (var i = char.MinValue; i < char.MaxValue; i++)
{
var c = Convert.ToChar(i);
if (!char.IsControl(c))
lv1.Items.Add(""+c);
}
so decided to load chars when scroll is at some appropriate points (ie first/last 15%) but ListView doesn't give absolute position of the scrollbar.
It does feel a little hackish, but maybe it will do the job:
int getFirstVisibleItem(ListView lv)
{
ListViewHitTestInfo HI;
for (int i = 0; i < Math.Min(lv.ClientSize.Width, lv.ClientSize.Height); i += 3)
{
HI = lv.HitTest(i, i);
if (HI.Item != null) return HI.Item.Index;
}
return -1;
}
This does not directly help with your scrolling issue but should find the first visible Item as you have asked. If your Items have extremely weird (ie non-square) shapes you may want to change the travesal code a little..
For your requirement implement ListView with custom scrollbar. So you have more control over your scroll position. You decide when to what action based on scroll position. This might be helpful Code
I would like to know how many rows are actually displayed by a WPF DataGrid.
I tried looping over DataGridRow and checking IsVisible, but it seems that rows report IsVisible = true even when they are not in the DataGrid viewport.
How can I count the number of visible rows correctly?
I've asked this question also on MSDN forum and got a good answer:
private bool IsUserVisible(FrameworkElement element, FrameworkElement container) {
if (!element.IsVisible)
return false;
Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}
I had the same problem with rows showing as Visible = true even when they weren't.
Trying to come up with a solution, I posted this question: Visible rows in DataGrid is off by 1 (counted using ContainerFromItem).
Here's what worked for me:
uint VisibleRows = 0;
var TicketGrid = (DataGrid) MyWindow.FindName("TicketGrid");
foreach(var Item in TicketGrid.Items) {
var Row = (DataGridRow) TicketGrid.ItemContainerGenerator.ContainerFromItem(Item);
if(Row != null) {
if(Row.TransformToVisual(TicketGrid).Transform(new Point(0, 0)).Y + Row.ActualHeight >= TicketGrid.ActualHeight) {
break;
}
VisibleRows++;
}
}
For further guidance, there are some /* comments */ in my answer on the linked question, as well as a thread of user comments on the question itself that led to the answer.
a simple hack come to mind,
loop over all rows and check if item has a container?
dataGrid.GetContainerFromItem(dataGrid.Items[row]);
hope this helps
If you need it for another xaml element just add a reference via "ElementName" and the property via "Items.Count" to your content property(in this case "Text"). You might also use a converter to parse the value.
<TextBlock Text="{Binding ElementName=ComponentDataGrid, Path=Items.Count, Converter={StaticResource IntToStringConverter}}"/>