Continuously adding and removing to flow-layout panel win forms - c#

I have a c# win forms application which has a flowLayoutPanel in it.
I need to update all the children in this panel every second.
here is my current code which gets called in a system timer every 1 seconds:
public void RefreshReceiversPage()
{
if (checkBox_enableReceivers.Checked)
{
var receivers = OutgoingReceiverManager.GetCopyOfActiveRecieverHolders();
for (int i = 0; i < flowLayoutPanel_receivers.Controls.Count; i++)
{
var tmp = flowLayoutPanel_receivers.Controls[i];
flowLayoutPanel_receivers.Controls[i].Dispose();
tmp.Dispose();
}
flowLayoutPanel_receivers.Controls.Clear();
foreach (var item in receivers.ToList())
{
var tmpUc = new ucReceiverItem(item);
if (flowLayoutPanel_receivers != null)
{
try
{
flowLayoutPanel_receivers.Controls.Add(tmpUc);
}
catch
{
}
}
}
receivers = null;
}
}
now this code works perfectly for about 2 minutes and then all of a sudden I start getting error creating window handle Hence the reason for my try catch in the code above.
But after this happens the pane view goes funny and I cant recover it without starting up the program again.
I have searched high and low and I cant seem to find anything on the exception being thrown?
All that I can think is that im maybe not disposing of object properly and that its running out of memory some where?
Does any one have any suggestions or solutions?
EDIT:
here is UCRecieverItem:
public partial class ucReceiverItem : UserControl
{
public ucReceiverItem(ReceiverHolder item)
{
InitializeComponent();
ConstructItem(item);
item = null;
}
private void ConstructItem(ReceiverHolder item)
{
label_name.Text = item.ReceiverDb.Name;
label_numberOfConnections.Text = item.ReceiverOutgoingConnectionManager.GetNumberOfConnections().ToString();
label_mrFilters.Text = item.ReceiverDb.MrFilters;
label_multipleConnections.Text = item.ReceiverDb.MultipleConnections.ToString();
//
int count = item.GetActiveBufferCount();
int size = item.GetActiveBufferSize();
//
label_bufferCount.Text = count + #" / " + size;
progressBar_buffer.Maximum = size;
progressBar_buffer.Minimum = 0;
progressBar_buffer.Value = count;
}
}

This code is a problem:
for (int i = 0; i < flowLayoutPanel_receivers.Controls.Count; i++)
{
var tmp = flowLayoutPanel_receivers.Controls[i];
flowLayoutPanel_receivers.Controls[i].Dispose();
tmp.Dispose();
}
flowLayoutPanel_receivers.Controls.Clear();
It is only disposing half of the controls in the container. The other half get removed by the Controls.Clear(); call, but those controls do not get disposed — so they still exist and are using up memory.
Doing this every second compounds the problem: that's potentially a lot of controls.
The immediate work-around would be to properly dispose of the controls:
while (flowLayoutPanel_receivers.Controls.Count > 0) {
flowLayoutPanel_receivers.Controls[0].Dispose();
}
After that, I would question the need to do this every second — seems like a harsh environment for a user to work in.

Related

How can I execute a method after a content page is visible in Xamarin Forms?

In my Xamarin Forms app made for android as of now, I have a stack layout that gets populated based on a list that needs to fetch data from local sqlite DB. The data is around 200 rows. The stack needs to be populated when the ContentPage appears in foreground.
Since this process takes a bit of time, the navigation to this page is taking some time which leads to poor performance of app.
I have added that refresh method in constructor of page and also have tried putting it in OnAppearing override method. But the transition occurs only when the stack is populated.
Is there any way to view the page to the user first (make navigation happen) and then populate the stack?
{
private string shipmentId;
public ScanOrderPage( string shipmentID)
{
InitializeComponent();
shipmentId = shipmentID;
TapGestureRecognizer tapEvent = new TapGestureRecognizer();
tapEvent.Tapped += Scan_Button_OnClicked;
clickFrame.GestureRecognizers.Add(tapEvent);
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Initiator();
}
private async Task Initiator()
{
Indicator.IsVisible = true;
bool allGood;
if (!await App.Database.IsShipmentPresent(shipmentId))
{
int getBox = await new ShipmentBoxes().Get(shipmentId);
allGood = getBox == 1;
}
else allGood = true;
if (!allGood) return;
await Populate();
Indicator.IsVisible = false;
}
private async Task Populate()
{
BoxListStack.Children.Clear();
var shipmentData = await App.Database.GetShipmentBoxes(shipmentId);
if (shipmentData == null) return;
ShipmentIDText.Text = shipmentId;
FromText.Text = shipmentData?.FirstOrDefault()?.From;
ToText.Text = shipmentData?.FirstOrDefault()?.To;
TotalBoxesText.Text = "Total Boxes: " + shipmentData.Count.ToString();
int scanCount = 0;
for (int i = 0; i < shipmentData.Count; i++)
{
string boxCode = shipmentData.ElementAt(i).BoxCode;
if (shipmentData.ElementAt(i).Scanned) scanCount += 1;
else
{
int foo = boxCode.LastIndexOf("-", StringComparison.Ordinal);
boxCode = boxCode.Substring(0, foo + 1) + "XXXX";
}
BoxListStack.Children.Add(new ScanBoxes(shipmentData.ElementAt(i).Scanned)
{
Subject = shipmentData.ElementAt(i).Subject + " " + shipmentData.ElementAt(i).SubjectClass,
BoxCode = boxCode
});
}
ScanStatusText.Text = "Boxes Scanned: " + scanCount.ToString() + " | Boxes Pending: " +
(shipmentData.Count - scanCount).ToString();
}```
It is wrong to view that your problem is in timing. Your problem is that you are executing too much long running code on the UI thread which freezes UI. Obviously you could postpone that to the moment when the page is displayed (for example using the Timer), but the problem with frozen UI would remain just it would appear in a bit less annoying form.
As your code appears to have lines that require to be running on UI thread in order to avoid crashes I can't go line by line and fix the problem.
However to run on non-UI thread you use Task.Run . If you need to switch to the UI thread after that you use InvokeOnMainThread.
Why not try infinite scrolling. Load as you scroll through the list
https://youtu.be/sZq8K_64bc0

Cannot access List from a class within an Array

I am a beginner developer, and would very much appreciate if you can help me figure out the problem which is in my code. The code is particularly confusing, mainly because it derives from a framework. The comments should be able to somewhat allow us to understand.
// Create an IBindable List
public static List<IBindable> KyzerBindables = new List<IBindable>();
// Attach elements to a list, for better control over all of them
internal static void AttachBindablesToList(IReadOnlyList<Drawable> children)
{
// For all the children classes located in Drawable list
for (int i = 0; i < children.Count; i++) // children.Count returns 4
{
// For all of the SettingsSubsection which are present in the Drawable array
for (int l = 0; l < (children[i] as SettingsSubsection).Children.Count; l++) // (children[i] as Subsection).Children.Count returns 0.
{
// Get a specific element
var element = (children[i] as SettingsSubsection).Children[l];
// if is a SettingsCheckbox
if (element.GetType() == typeof(SettingsCheckbox))
KyzerBindables.Add((element as SettingsCheckbox).Bindable);
}
}
}
// in another class
public class KyzerSection: SettingsSection
{
public KyzerSection()
{
Children = new Drawable[]
{
new KyzerMiscellaneous(),
};
...AttachElementsToList(Children);
}
}
public class KyzerMiscellaneous: SettingsSubsection
{
[BackgroundDependencyLoader] // Calls load, framework thing.
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = "Something here",
Bindable = new BindableBool(false),
}
};
}
}
My problem is, the second for loop does not even initiate for the AttachBindablesToList. For whatever particular reason, it isn't recieving a count. I am uncertain of what I am doing wrong.
Edit:
If, in any way, the GitHub repository issue can clear some issues up, please feel free to navigate there and check the commit which contains these changes. https://github.com/Frontear/osuKyzer/issues/3
After reviewing your github repository, I believe the issue is caused at:
private void load(params here)
The above is not being called at the time of AttachBindablesToList. This results in an empty
(children[i] as SettingsSubsection).Children.Count
The best option is to create an empty instantiation method
public KyzerMiscellaneous() { /* create Drawable elements */ }
// then
[BackgroundDependancyLoader]
private void load(params here) { /* doSomething */ }
This will allow access to the children list since it has been initialized before, which therefore allows the second loop to correctly function, and pushes IBindables to your list.

Automatic function linked to an external timer

I have an automatic function, the idea is to execute in an specific time some actions.
It receives a ListView with the actions and specific times.
It runs almost pefect. I say almost because sometimes it doesn't executes one action (lets say action #10, and it has 30 actions to do) and when this happen the rest of the actions neither get executed.
I have some validations where I check if the previous actions is executed then the current one is excuted, but it doesn't make any difference, it continues stopping at some point.
Here the code for this automatic function:
Constructor
public RunAutomatic()
{
InitializeComponent();
stopwatch = new System.Windows.Forms.Timer();
stopwatch.Interval = 5;
stopwatch.Tick += new EventHandler(stopwatch_Tick);
repSeconds = 0;
for (int x = 0; x < repeatActions.Capacity; x++)
{
repeatActions.Add(x);
finished.Add(x);
}
}
Run Function
private void RunFaster()
{
if (contPos > 1)
{
ArrayList tmp = (ArrayList)repeatActions[contPos - 1];
if ((bool)tmp[tmp.Count - 1] == true) //This is where it is supposed to validate that the previous action is already executed
Execute(2);
else
{
contPos = contPos - 1;
Execute(2);
}
}
else
Execute(2);
}
I have tried to solve this but can't get over it.
Thanks to all replies.

WPF - print during a loop

I have a simple issue, but the solution appears to be tricky. I want to print using the WPF control canvas during a loop; but for each iteration, I want to udpate the canvas control.
If I want to print a canvas control in WPF, I can simply call
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
And it prints as expected to my default printer. Wonderful.
However, if I want to perform this multiple times in a loop and make an update to the canvas during each iteration, only the final iteration of the loop is printed.
private void methodName()
{
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
}
}
private void updateTextBox(string text)
{
txtTextBox.Text = text;
}
Any idea what I need to do to ensure that I get 2 print outs, the first with the txtTextBox.Text value of 0, the second time it has the value of 1?
I am about to implement something similar in my application and found out that my previous answer wasn't good enough. The problem for me was that although the canvas is updated in each iteration, it has not yet rendered itself before being sent to PrintVisual. It surprises me that you get your final iteration printed, I only got the first one. Anyway, this is how I made it work, basically queueing the print command after the already pending render operation:
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
this.canvas.InvalidateVisual(); // Maybe not needed in your case
PrintDialog dialog = new PrintDialog();
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (Action)delegate()
{
dialog.PrintVisual(this.canvas, "" + i);
});
}
Yes it's somewhat similar (but not identical) to SalGad's answer and the post you're referring to, but I'm not able to comment that answer, so please try this out, it works for me.
I also had problems with disappearing prints when using empty description for the print jobs, thus the + i. Don't know if that is a generic problem, or just something with my printer setup.
I got the idea from this post, which also mentions an alternative solution using ViewBox.
OK
I solved it.
I removed all the dispatcher object methods so it runs on a single thread.
To update the canvas, I used the canvas.UpdateLayout() method.
I also ensured that the print had finished before updating the next canvas (the next iteration).
private void methodName()
{
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
this.canvas.UpdateLayout();
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "ABC");
dialog.PrintQueue.Refresh();
while (dialog.PrintQueue.NumberOfJobs != 0)
{
bool isQueued = false;
foreach (var job in dialog.PrintQueue.GetPrintJobInfoCollection())
{
if (job.Name == "ABC")
isQueued = true;
}
if (!isQueued)
break;
Thread.Sleep(500);
dialog.PrintQueue.Refresh();
}
}
}
private void updateTextBox(string text)
{
txtTextBox.Text = text;
}
I also could have just done thread.sleep(3000) - this worked as it was enough time to ensure the print job had completed, but it was also a little bit 'hopeful' and I wanted something more secure.
Thank you to everyone for your suggestions.
If you are going to call PrintVisual multiple times you have to look into PrintDocument and DocumentPaginator.
Just a guess, but it might be worth trying RenderTargetBitmap to force rendering the canvas in each iteration, and then create an Image with that source, which then can be sent to PrintVisual. See this post for code example:
Printing viewport
Just taking a shot here, but can you try refreshing the WPF canvas controls at the start of every for loop iteration? Here is the code snippet:
// declare this
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
// the loop
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
}
}
// update this method
private void updateTextBox(string text)
{
txtTextBox.Text = text;
txtTextBox.Refresh();
Thread.Sleep(500);
}
I am sourcing this idea from here

Adding multiple expanders event programmatically wont work

I've got a problem with adding Expanded event to my Expanders. I have expanders on my Window and I want to get the effect when I expand my expander all other will go down. I write functions that let me do this and it work correct. The problem is that I have 96 expanders I don't want add 96 events for Expand and 96 events for Collapse so I thought that I can add this programmatically.
look at the code:
private void InitExpanders()
{
var expanders = GetExpanders(); // List<Expander> - list of expanders
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
expanders[i - 1].Expanded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
{
DisableBigExpanders(1); // problem is here!
});
}
}
}
this code works fine but for each expander function parameter will be 1.
Ive tried to add integer and increment it but it wont works.
private void InitExpanders()
{
var expanders = GetExpanders();
int x = 0;
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
expanders[i - 1].Expanded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
{
DisableBigExpanders(x);
});
x++;
}
}
}
Thanks for all replies.
I suspect you are finding x in the delegate is always the highest value it reached during the loop. This is due to the way the compiler instantiates the instance of the anonymous method you've defined. It looks at what captured outer variables there are around the delegate and decides whether it requires separate instances or if it can use a single instance. In this case, you have no captured outer variables; therefore, the compiler is permitted to use the same instance of the delegate and therefore the same value of x.
To get around this issue, all you need to do is add a more granular variable just before the delegate and assign it the value of x. This will work:
private void InitExpanders()
{
var expanders = GetExpanders();
int x = 0;
for (int i = 0; i < expanders.Count; i++)
{
if (i % 6 == 1)
{
int y = x++;
expanders[i - 1].Expanded += delegate
{
DisableBigExpanders(y);
};
}
}
}
See here for more info on the theory: http://en.csharp-online.net/ECMA-334%3A_14.5.15.4_Anonymous_method_evaluation

Categories

Resources