Good day.
I have created a little program that creates color palettes. It's okay, it works correctly. Program Screenshot
The whole code is here. The release is here
The texture created is used in 3D modeling as a material. Program works like this:
I have a little nuisance with this part of code which consumes a lot of RAM.
For example if user choses range 0-255, tonestep 17 then there will be 3375 color boxes. (--(255x255x255)/(17x17x17)--) Consumes 70mb.
if user choses range 0-255, tonestep 5 then there will be 132651 color boxes. (--(255x255x255)/(5x5x5)--) Consumes 600mb!
There are three marks that consume memory the most. When I first created the app I assumed that after each cycle variables "rectangle" and "brushColor" would be released, garbage-collected and reused, but I was completely wrong. They have WeakReference.
In the end I tried caching these variables but then program started to work unexpectedly. No color boxes on final window or all boxes are one color. Or this "Specified Visual is already a child of another Visual or the root of a CompositionTarget". I could not resolve these errors without breaking program so I left it as it is.
So, what better approaches one could use to reduce memory consumption in this case? My implementation is good enough for me but I'd like an advice on how can I make it better. Maybe I missed something obvious or, on the contrary, did something very inefficient like storing color in the rectangle etc.
private void shuffle_colors1(List<StackPanel> stackPanels, Double colorBoxCount, int boxesInSingleRow, int stackPanelsCounter)
{
for (int redC = tempR; redC < crMax; redC++)
{
for (int greenC = tempG; greenC < cgMax; greenC++)
{
for (int blueC = tempB; blueC < cbMax; blueC++)
{
var rectangle = new Rectangle(); // MARK 1
rectangle.Width = cBoxWidth;
rectangle.Height = cBoxHeight;
rectangle.Margin = border;
stackPanelColor = Color.FromRgb((byte)redC, (byte)greenC, (byte)blueC);
brushColor = new SolidColorBrush(stackPanelColor); // MARK 2
displayedColors++;
rectangle.Fill = brushColor;
stackPanels[stackPanelsCounter].Children.Add(rectangle);
colorBoxCount++;
if (colorBoxCount >= boxesInSingleRow)
{
AddNewStackPanel(stackPanels, stackPanelsCounter); // MARK 3
stackPanelsCounter++;
colorBoxCount = 0;
}
blueC += bts - 1;
}
greenC += gts - 1;
}
redC += rts - 1;
}
}
It will be difficult to tell the actual problem without actually doing some debugging, but I might provide some tips:
From your description it sounds like you already does this, but if you make sure to clear out any thing that contain a reference to rectangles or any other UI object they should be eligible for collection. In your example code I cannot see anything that ensures stack panels are re-created or cleared.
Not removing event handlers is another common reason for leaks.
But there can also be bugs. I have had some problems with windows accessibility functions keeping references to UI elements after the application itself has released everything. Windows seem to release references after some time, but I still consider it a bug if memory is not freed after a collection. The symptom for this is when UI objects that should be collected is only has a pinnable handle or gcroot as a root reference, with no user code involved. But I fail to find a reference to the problem.
Wpf objects are typically not super cheap to create, so you might want to reuse objects instead of recreating a large number of them. As long as you are not changing the number of boxes I see no reason why you could not simply update the color property of your brushes.
While using separate UI elements might be reasonable for a smaller grid, 130k elements sound to much. So one alternative is just to limit the "tone step" variable to some reasonably large value.
But if you want to support very large number of colors you will probably benefit from just using a WriteableBitmap to generate your desired color layout. That way your memory usage is directly proportional to the number of pixels you need, with very little overhead.
Some features, like doing a mouse-over effect, would be more complicated, but most things should still be possible by using regular wpf elements on top of the bitmap to show selection or whatever you want.