Removing a Control from a Form - c#

So I've got some serious problems with removing a Control from a Form of my application. It's kinda messed up but I can't change anything. I have a form and I have a separated user Control. The control opens an exe file and shows a progress bar while loading it's bytes. And here comes the problem. I do all of it with a BackgroundWorker and when the worker_DoWorkerCompleted method is called the original form should show a MessageBox and remove the Control.
BackGround_Loader bgLoad = new BackGround_Loader();
bgLoad.Location = new Point(this.Width/2 - bgLoad.Width/2, this.Height/2 - bgLoad.Height/2);
this.Controls.Add(bgLoad);
bgLoad.BringToFront();
bgLoad.AddReferences(this.executableFile, this.SourceReader);
bgLoad.occuredEvent();
At first I set the control's location to be in the middle of the Form itself. Then I add the control to the form, and bring it to the front. After these I send the path of the executable and a RichTextBox's reference to this. With the occuredEvent I start the BackgroundWorker itself. And here comes my problem. I should show a MessageBox in the Form when the in the bgLoad the backgroundworker gets to the DoWorkerCompleted status. Kindly I have no idea how to do it. It works just perfect however the control stays in the middle of the form.

UI actions must be performed on the main UI thread. The events that get raised from the background worker thread are (obviously) in a different thread.
You need something like the following code:
private void backgroundWorker_DoWork(object sender, AlbumInfoEventArgs e)
{
// Check with an element on the form whether this is a cross thread call
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { AddToGrid(e.AlbumInfo); });
}
else
{
AddToGrid(e.AlbumInfo);
}
}
In this case AddToGrid is my method for adding a row to a DataGridView, but in your case it will be a method that does what you need to do.
Similarly for the backgroundWorker_RunWorkerCompleted method
See this MSDN example

I could find a way to solve the problem but I don't really like it. In the addReferences method I pass the Form itself and an object of the bgLoad class. Then in the RunWorkerCompleted I check if the control is on the form and if it is then I remove it.
bgLoad.AddReferences(this, bgLoad, this.executableFile, this.SourceReader);
...
private void worker_DoWorkerCompleted(object sender, DoWorkerEventArgs e) {
if(this.MainForm.Controls.Contains(this.Control) {
this.MainForm.Controls.Remove(this.Control);
}
}
Like this it works but it's awful for me.

Related

c# custom messageBox show

I need to show up custom messageBox and close it when operation is done.
Problem is messageBox appear normal but without added label wich shows message , i can see only white space...After job is done , messageBox closing work normal.
here is code
public void resetirajSve() {
MyMessageBox poruka = new MyMessageBox();
poruka.Show();
analizaPodataka();
glProstor.Rows.Clear();
poruka.Close();
}
I tried using poruka.ShowDialog() but then code wont to continue executing.. with showDialog method label apper normal...
public partial class MyMessageBox : Form
{
public MyMessageBox()
{
InitializeComponent();
}
}
Some pointers:
1.It seems your custom MessageBox actually inherits a Form. The default MessageBox is rather versatile - see some examples, so there's no need to reinvent the wheel. Focus on more important things.
2.Using ShowDialog() will block all actions from executing on the Form until the newly opened form (you call it MyMessageBox, but it's actually a Form).
3.You could just add a ProgressBar control to display the task's progress and use the wait cursor until it is finished.
4.To avoid the program window becoming unresponsive until the method has finished executing (it can be an issue if it takes a long time), you might want to take a look at asynchronous programming. Here's an example.

Error loading custom user controls from different threads

I have a WPF project and from the main window i am creating and loading some bunch of user controls, there is some large data i am loading in background and then updating a built-in control throw the dispatcher, that works fine, the problem is that some of the user controls loads a lot of data, for example the very first thing i load in the main area of my main window, what i want is to put a loading label instead, load the main window as fast as possible so the user see this label and run in background the creation of that user control and when is done add it as a child of my main container area on my main window while i remove the loading label, if i follow the same philosophy i run into the same error like when i run a task and then try to update the window without using the dispatcher. i want to be able of create the user control asynchronous then update the main window.
Code:
User Control:
public partial class CustomUserControlGallery : UserControl
{
public CustomUserControlGallery()
{
InitializeComponent();
}
...
}
On the backend class of the main window:
public partial class MainWindow : Window
{
CustomUserControlGallery _customUserControlGallery;
public MainWindow()
{
InitializeComponent();
Task t = new Task({
//Can't use the _customUserControlGallery's Dispatcher because object is uninitialized and this.Dispatcher not working either.
_customUserControlGallery = new CustomUserControlGallery(); //Error Here.
_gridContainer.Dispatcher.Invoke(new Action(() => _gridContainer.Children.Add(_customUserControlGallery)));
_loadingLabel.Visbility = Visibility.Collapse;
});
t.Start();
}
...
}
I don't know how to handle this situation with the thread associated to the user control and the main thread.
Error:
{"The calling thread must be STA, because many UI components require this."}
You're doing this wrong. All controls must be created & operate on the UI Thread. That said, you can use the BackgroundWorker class to load the data.
You typically do this by disabling the control whose data is being loaded in the background or hiding it & displaying a progress indicator in its place. Then, you start your BackgroundWorker. That can communicate how far along it is using the ReportProgress method. Finally, when it's finished running, the RunWorkerCompleted event is fired, and you use that to either enable the control, or to hide the progress indicator & show the control.
Some quick & dirty (untested) code:
Place this in your Initialize() or control constructor:
private BackgroundWorker loadData = new BackgroundWorker();
loadData.DoWork += loadData_DoWork;
loadData.ProgressChanged += loadData_ProgressChanged; // Only do this if you are going to report progress
loadData.WorkerReportsProgress = true;
loadData.WorkerSupportsCancellation = false; // You can set this to true if you provide a Cancel button
loadData.RunWorkerCompleted += loadData_RunWorkerCompleted;
private void DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker worker = sender as BackgroundWorker;
bool done = false;
while ( !done ) {
// If you want to check for cancellation, include this if statement
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// Your code to load the data goes here.
// If you wish to display progress updates, compute how far along you are and call ReportProgress here.
}
}
private void loadData_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
// You code to report the progress goes here.
}
private void loadData_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Your code to do whatever is necessary to put the UI into the completed state goes here.
}
What you are essentially saying (I think) is that Your app becomes sluggish while your control renders a large amount of data.
This is a problem that needs to be solved via virtualisation. You cannot create a control on a background thread, have it render its data behind the scenes and then pop it into existence. You can create controls on separate dispatchers, but they cannot share the same visual and logical tree, so you will not be able to have one as a child of the other.
Virtualisation is what you need to focus on. Depending on the control you can use a variety of virtualisation settings. Try googleing the subject as there is a lot of information on how to achieve this effectively. Most likely you will want to use things like virtualizing stackpanels and container recycling.
You cannot create UI controls with different Dispatchers and use them with each other. It's just not allowed. What you want to do is on your Task you do the heavy lifting work without UI updates and when it is done you push it back to the Dispatcher to update the UI.
In your case, I wouldn't even use Dispatcher.Invoke. Since you are using Task, it has a TaskScheduler.FromCurrentSynchronizationContext() that you can pass in the constructor.
What is the purpose of instantiating controls in a different thread if you're just going to put it back to the Main dispatcher? It's not expensive to do that.

C# Problem setting Label Content in WPF App From Separate Thread

I have a window that contains a label (player1). I also have a class that gathers data asynchronously in the background inside a thread. When that data has been gathered, I want to changed the content of my label. Since the label was created by the UI and I'm trying to edit it from another thread, I tried using Dispatcher. However, after hours of trying and different examples, I can't get it to work. In it's most simple form below, the method dispatchP1 changes the value of player1 when called from my main window. However, it doesn't work when called from my class. Also, I don't receive an error or anything.
public delegate void MyDelegate();
public void dispatchP1()
{
player1.Dispatcher.BeginInvoke(new MyDelegate(p1SetContent));
}
public void p1SetContent()
{
player1.Content = "text";
}
Any help would be appreciated.
That code doesn't seem particularly problematic - but WPF has a habit of swallowing exceptions. In your App.xaml, you can handle the event DispatcherUnhandledException and put a breakpoint in there to determine if it is really throwing an exception or not.
You know you can use anonymous delegates?
player1.Dispatcher.BeginInvoke( () =>
{
player1.Content = "text";
});

Get Data from Windows Form into C# program

I want my C# program to collect data. Then, when the OK button is clicked, I want this data to be loaded into my program -- such as into variables in Main(), or into data members of a class I have constructed, and I want the form to then go away -- not be hidden, but actually closed. Alas, read as I might in the book I have, the data collected by the form stays in the form, execution proceeds within the form, and the same form is used to display the result of the program's computations. Sheesh. All I want is to capture the form's information, close the form and proceed with my program. I would appreciate some tips on geting data from a form into Main() or into a class's data members.
Thanks,
Lucky
What you want to do is perfectly acceptable, it just isn't typical.
When you use Visual Studio to generate a WinForms project, it creates one form for you and
generates a call to Application.Run(new Form1()). For this version of the Run() method, your application will exit when the "main form" (the one passed to Run(), in this case Form1) closes.
There are three overloads (versions) of Application.Run(). For your purposes, you need to use a different overload:
Application.Run(ApplicationContext)
When you use this overload of Run(), you get to control when the application exits. In a nutshell, here's one way you could do it:
Create a class which inherits
ApplicationContext.
In its constructor:
Create your form.
Subscribe to its Closing and Closed events.
Show your form.
In your FormClosing event handler,
get the data from the form.
In your FormClosed event handler, do
whatever you want to do with the
data, and then exit the thread (or do something else).
Here's a crude example, but I will leave out the code for the form itself. Assume the form simply has one TextBox which has its Modifiers property set to Public. (This is NOT an elegant way to get data from a form, but that part is up to you).
namespace Me.MyDemo
{
static class Program
{
[STAThread]
static void Main()
{
MyApplicationContext ac = new MyApplicationContext();
Application.Run(ac);
}
class MyApplicationContext : ApplicationContext
{
string _text = "";
public MyApplicationContext()
{
Form1 f1 = new Form1();
f1.FormClosing += new FormClosingEventHandler(f1_FormClosing);
f1.FormClosed += new FormClosedEventHandler(f1_FormClosed);
Console.WriteLine("I am here. Showing form in 1 second...");
Thread.Sleep(1000);
f1.Show();
}
void f1_FormClosing(object sender, FormClosingEventArgs e)
{
_text = (sender as Form1).textBox1.Text;
}
void f1_FormClosed(object sender, FormClosedEventArgs e)
{
Console.WriteLine("You wrote: " + _text);
Console.WriteLine("I will go away in 2 seconds...");
Thread.Sleep(2000);
ExitThread();
}
}
}
}
Of course, you don't have to exit the thread. You can leave it running if there are other things for your program to do. It will just run as a windowless process. Just remember that you're responsible for eventually ending it.
For more help, look at the documentation for the System.Windows.Forms.Application class, and the ApplicationContext class.
For getting the data from your form, there are many ways to approach this. The simple way is to just give your form some public properties. A more sophisticated way would be to create a data class and use data-bound controls on your form.
You're writing in WinForms? As far as I know a Windows application has to have a window, even if it's one pixel by one pixel.
Have you seen any other Windows applications that work the way that you want yours to work? Opens a window, the window closes, but the program keeps on running? This is generally considered undesired behavior, similar to viruses and trojans.
You can create a console application or a Windows service with no GUI, of course.
What is the application doing behind the scenes after the data is entered? If it's just doing some calculations and saving to disk, uploading, or printing, leave the window open for that and then exit when it's done. Possibly include a progress bar.

Winforms listbox not updating when bound data changes

The image below shows how my code works. When I press button2 the listbox is updated, but not when I press button1. Why?
Is the problem threading related? If it is, where should I add the call to (Begin)Invoke?
One interesting thing to note is that if I first press button1 and then button2 the data generated by the button1 click is shown when I click button2. So it seems like the data generated by doFoo is buffered somewhere, and then pushed to the listbox once I press button2.
EDIT:
I tried adding AddNumber to the form code, and added a call to Invoke when listBox1.InvokeRequired returns true. This solves the problem, but isn't the nicest of designs. I don't want the GUI to have to "worry" about how to add items to a list that's part of the model.
How can I keep the logic behind adding to the list internal to the list class, while still updating the gui when the list changes?
EDIT 2:
Now that we have confirmed that this is a threading issue I've updated the image to more closely reflect the design of the actual code I'm working on.
While Lucero's suggestion still solves the problem, I was hoping for something that doesn't require the form to know anything about the dll or CDllWrapper.
The model (ListBoxDataBindingSource etc) should know nothing at all about the view (listboxes, buttons, labels etc)
My guess is that this is due to the update message being handled on the wrong thread. Background: each thread has its own message queue. Messages posted into the message queue will land in the same thread as the caller by default. Therefore, the callback will maybe post a message on the wrong thread.
Try this: move the AddNumber() method to the form and use Invoke() (inherited by Control) to add the item in the correct thread. This may get rid of the issue.
Edit to reflect your followup:
The UI doesn't have to know about your component. What you need is just a proper synchronization between adding the item to your list and the UI, since UI updates will oly work if the thread matches. Therefore, you might want to supply the Control to your class which wraps the BindingList, and then do the Invoke on the list itself. This makes the list worry about triggering the upate on the UI thread and does take the worry from both the UI and the external component of invoking the handler on the correct thread.
Like this:
internal class ListBoxDataBindingSource {
private readonly Control uiInvokeControl;
private readonly BindingList<Item> list = new BindingList<Item>();
public ListBoxDataBindingSource(Control uiInvokeControl) {
if (uiInvokeControl == null) {
throw new ArgumentNullException("uiInvokeControl");
}
this.uiInvokeControl = uiInvokeControl;
CDIIWrapper.setFP(AddNumber);
}
public void AddNumber(int num) {
Item item = new Item(num.ToString());
if (uiInvokeControl.InvokeRequired) {
uiInvokeControl.Invoke(list.Add, item);
} else {
list.Add(item);
}
}
private BindingList<Item> List {
get {
return list;
}
}
}
I know this is old, although I had a very similar problem.
Here was the solution: BindingList not updating bound ListBox.
Instead of having the setFP set the callback to lbDataBindingSource.AddNumber, create a private method in your code to handle the callback and then call lbDataBindingSource.AddNumber from that callback.
void MyForm_Load(object sender, EventArgs e)
{
//...
cdll.setFP(FPCallback);
}
private void FPCallback(int num)
{
lbDataBindingSoruce.AddNumber(num);
}
I need to call my view model to add things to the bindinglist, so I need to write an anonymous function
Reference to Lucero's Answer and following post:
Anonymous method in Invoke call
My Code:
listBox.Invoke((Action)delegate
{
MyViewModel.AddItem(param1, param2);
});

Categories