i'm working on my first C++ project that requires a form and I've seemed to have gone in way over my head. largely I believe my issue has to do with that I'm not multi threading my form, but I'm unsure of the proper implementation. I was hoping someone could point out to me exactly where I've gone and made myself look like a muppet. (warning for the feint of heart, I used global variables to quickly create a proof of concept, once I have everything working more or less correctly I'll go back and properly protect everything)
*edit: I guess to clarify, it looks like the issue is that i execute everything in the main thread, its is possible to create a single new thread for the entire form or do i need to create a new thread for each individual control on the form?
Winmain.cpp
main function where I initialize the form and update the information on the form / refresh the form.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
using namespace Interface;
cooldowns ^ CDwin = gcnew cooldowns;
CDwin->Show();
CDwin->Location = Point(0,0);
while (true)
{
CDwin->timer1->Text = timer1Duration.ToString();
CDwin->timer1Progress->Value = timer1Value;
CDwin->timer1->Refresh();
CDwin->timer1Progress->Refresh();
//collect info to populate CDwin values for next cycle
//something tells me this sleep could be part of the problem?
Sleep(50);
}
}
form.h
#pragma once
namespace Interface {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
/// <summary>
/// Summary for cooldowns
/// </summary>
public ref class cooldowns : public System::Windows::Forms::Form
{
public:
cooldowns(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~cooldowns()
{
if (components)
{
delete components;
}
}
public: System::Windows::Forms::ProgressBar^ timer1Progress;
protected:
public: System::Windows::Forms::Label^ timer1;
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->timer1Progress = (gcnew System::Windows::Forms::ProgressBar());
this->timer1 = (gcnew System::Windows::Forms::Label());
this->SuspendLayout();
//
// timer1Progress
//
this->timer1Progress->ForeColor = System::Drawing::Color::Fuchsia;
this->timer1Progress->Location = System::Drawing::Point(3, 12);
this->timer1Progress->Maximum = 30000;
this->timer1Progress->Name = L"timer1Progress";
this->timer1Progress->Size = System::Drawing::Size(244, 18);
this->timer1Progress->Style = System::Windows::Forms::ProgressBarStyle::Continuous;
this->timer1Progress->TabIndex = 0;
this->timer1Progress->Value = 10000;
//
//timer1
//
this->timer1->AutoSize = true;
this->timer1->ForeColor = System::Drawing::Color::White;
this->timer1->Location = System::Drawing::Point(253, 108);
this->timer1->Name = L"Timer1";
this->timer1->Size = System::Drawing::Size(34, 13);
this->timer1->TabIndex = 9;
this->timer1->Text = L"00.00";
//
// cooldowns
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->BackColor = System::Drawing::Color::FromArgb(static_cast<System::Int32>(static_cast<System::Byte>(64)), static_cast<System::Int32>(static_cast<System::Byte>(64)),
static_cast<System::Int32>(static_cast<System::Byte>(64)));
this->ClientSize = System::Drawing::Size(294, 138);
this->Controls->Add(this->Timer1);;
this->Controls->Add(this->timer1Progress);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None;
this->Name = L"cooldowns";
this->StartPosition = System::Windows::Forms::FormStartPosition::Manual;
this->Text = L"cooldowns";
this->TopMost = true;
this->Load += gcnew System::EventHandler(this, &cooldowns::cooldowns_Load);
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private: System::Void cooldowns_Load(System::Object^ sender, System::EventArgs^ e) {
}
};
}
edit after a lot of trial and error the issue seems to surround the CDwin->Show();. if i switch it to ShowDialog(); it no longer is unresponsive, unfortunately the values of the progress bars do not update either, which i believe is where the multi-threading comes into play.
I think you should use ShowDialog so your form has a message loop, and put your "every 50 ms" stuff in an event handler listening to your timer's Tick event.
As the first form you should always use System::Windows::Forms::Application::Run(CDwin), you can only use Show() and ShowDialog() methods when you already have run a form.
if you use Run() you will encounter some problems. This functions only returns something after form is closed that means codes after the Run() code will not be run until form is closed.
So you must forget about working with your form in main function. I recommend you to create two forms for example "Form1" and "Form2" (using threads is another way but this one's easier).
Use "Form1" as the base form that is hidden and it is used for updating the application by refreshing timer.
Use "Form2" as showed form to user. For doing this you should run "Form2" inside "Form1" using Show() or ShowDialog().
So that means:
// This is Form1 or base.
#include "Form2.h"
Form2 ^openForm2 = gcnew Form2();
openForm2->Show();
// while(true) Refresh the other form and other codes...
Related
Hello,
Since a few weeks, we are trying to "transform" a MFC dialog into a "MFC form" which can be embedded into a WinForm User Control.
We've succeeded to do that:
We made a WinForm User Control, called Dlg_WU_MFC_Container
When created, the UC creates the MFC form called CDlgEdgeType
Then, every time the UC is resized or moved, we also move and resize the MFC form
Here is the code (I tried to remove a lot of unnecessary stuff..):
Dlg_WU_MFC_Container.h:
#pragma once
public ref class Dlg_WU_MFC_Container : public System::Windows::Forms::UserControl
{
private:
CDlgEdgeType* _dialog;
CWnd *_wnd;
private: //---Local Controls
System::ComponentModel::IContainer^ components;
public:
Dlg_WU_MFC_Container();
~Dlg_WU_MFC_Container();
!Dlg_WU_MFC_Container();
template<class T, class HP>
void InitializeContainer() {
CDlgEdgeType =
}
private:
void RefreshEmbeddedSize();
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->SuspendLayout();
//
// Dlg_WU_MFC_Container
//
this->AutoScaleDimensions = System::Drawing::SizeF(96, 96);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Dpi;
this->ForeColor = System::Drawing::SystemColors::WindowText;
this->Margin = System::Windows::Forms::Padding(0);
this->Name = L"Dlg_WU_MFC_Container";
this->Size = System::Drawing::Size(816, 480);
this->SizeChanged += gcnew System::EventHandler(this, &Dlg_WU_MFC_Container::evSizeChanged);
this->VisibleChanged += gcnew System::EventHandler(this, &Dlg_WU_MFC_Container::evVisibleChanged);
this->ResumeLayout(false);
}
#pragma endregion
private: System::Void evSizeChanged(System::Object^ sender, System::EventArgs^ e);
private: System::Void evVisibleChanged(System::Object^ sender, System::EventArgs^ e);
};
Dlg_WU_MFC_Container.cpp:
#include "Dlg_WU_MFC_Container.h"
#include "DlgEdgeType.h"
Dlg_WU_MFC_Container::Dlg_WU_MFC_Container()
{
InitializeComponent();
_wnd = NULL;
_dialog = new CDlgEdgeType();
}
Dlg_WU_MFC_Container::~Dlg_WU_MFC_Container()
{
if (components)
{
delete components;
}
this->!Dlg_WU_MFC_Container();
}
Dlg_WU_MFC_Container::!Dlg_WU_MFC_Container()
{
// We need to detach the handle to free it for other usage
if (_wnd) {
_wnd->Detach();
delete _wnd;
_wnd = NULL;
}
if (_dialog) {
delete _dialog;
_dialog = NULL;
}
}
System::Void Dlg_WU_MFC_Container::evSizeChanged(System::Object^ sender, System::EventArgs^ e) {
RefreshEmbeddedSize();
}
// Inform the embedded form to adapt to its new size
void Dlg_WU_MFC_Container::RefreshEmbeddedSize() {
if (_dialog && _isShown) {
CRect containerWnd;
containerWnd.left = this->Left;
containerWnd.right = this->Right;
containerWnd.top = this->Top;
containerWnd.bottom = this->Bottom;
_dialog->ReplaceEmbeddedForm(containerWnd);
}
}
System::Void Dlg_WU_MFC_Container::evVisibleChanged(System::Object^ sender, System::EventArgs^ e) {
// _isShown basically useless.. !
if (Visible != _isShown) {
_isShown = Visible;
if (_dialog) {
if (Visible) {
void *handle = Handle.ToPointer();
if (handle) {
// We need to create a new CWnd which will contain
// the handle of the current WinForm control where
// the embedded MFC will be contained
_wnd = new CWnd();
_wnd->Attach((HWND)handle);
_dialog->Create(_wnd);
RefreshEmbeddedSize();
}
} else {
// When the control is not shown anymore, we need to free
// the handle so another control can use it (the handle
// is stored in the MFC permanent map)
_wnd->Detach();
_dialog->DestroyWindow();
delete _wnd;
_wnd = NULL;
}
}
}
}
CDlgEdgeType.cpp:
void CDlgEdgeType::ReplaceEmbeddedForm(CRect &rect) {
if (!GetSafeHwnd()) {
return;
}
SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
This stuff is working "great" : CDlgEdgeType is well shown in our application and when the application is resized or moved, everything goes well.
Here is my issue:
CDlgEdgeType has Dlg_WU_MFC_Container as parent, great. But the latter doesn't know that the MFC form is a "child".. so the focus is kind of lost and the Arrow keys and the Tab keys simply do not work.
A thing you should know is that Dlg_WU_MFC_Container is added to TabPages of a custom TabControl. So, if the user tries to navigate through MFC form's controls or he tries to navigate through a TreeView with the Arrow keys, the TabControl seems to take over the focus and will change tab.. which is not convenient D:
I have no idea how to solve that issue, neither my colleagues.
Maybe the way we integrate the MFC is wrong, but there are no really topics about that (we see more "Embedding WinForm forms into MFC"..). Also, as our software has a big history, we cannot simply recreate CDlgEdgeType. It's a big dialog and, in fact, we have 7 dialogs like that, the code implements template but I removed them for the clarity of this message..
Thank you !
Sbizz.
Well, we find a way out.. this may not be the best way to do it, but it is working (or at least, it seems to work !).
At first, I've managed to send the keys to the MFC form:
bool Dlg_WU_MFC_Container::ProcessDialogKey(Keys keyData) {
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_KEYDOWN, (WPARAM)keyData, (LPARAM)0);
return true;
}
Since the TabControl is taking the control through an WM_ACTIVATE, we tried to "override" it by sending also WM_ACTIVATE to the MFC form, so the result is the following:
bool Dlg_WU_MFC_Container::ProcessDialogKey(Keys keyData) {
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), (LPARAM)0);
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_KEYDOWN, (WPARAM)keyData, (LPARAM)0);
return true;
}
The only thing is that the "Tab" key doesn't seems to work but after investigation, it is not needed by our users, so... :D But I think it's just related to the third parameter of WM_ACTIVATE (previous control). It must be used to know which control must be focused after Tab is pressed.
Sbizz
I'm just trying to learn some Windows Forms stuff for reference, using C++/CLI. I created a project in VS 2010 called LibraryScan using the default Windows Forms options and I've modified the form with a few controls. All the code changes I've made are in Form1.h (see below). I've added the whole of Form1.h because I imagine that, if you just created a normal VS 2010 C++/CLI Windows Forms application you could replace the autogenerated Form1.h with what's below (although there is the imagelist to consider; item [0] is a generic document icon, [1] is a closed folder and [2] is an open folder).
Basically you use the "Browse..." button to select a folder, then press the "Scan" button and the aim is that it will recurse through the root folder and its subfolders to find all the files in it. The name of each file is added to a multiline TextBox, and the tree structure is generated in a TreeView.
The problem I'm having is that, without the line:
System::Threading::Thread::Sleep(1);
in the listFolder() function the UI is kind of unresponsive while scanning the folders. The TextBox gets updated ok, but the TreeView doesn't show until the scan is finished and, while scanning, you can't resize or move the application window. It's fine with the Sleep(1) in, albeit a little slow!
As I said, I'm new to Windows Forms but have some experience of MFC (although most of my 27+ years software development experience is in embedded stuff so..) including using event pumps to try to get round this sort of stuff. However the reading I've done so far seems to suggest the BackgroundWorker class and RunWorkerAsync()/ReportProgress() etc are the way to go in C++/CLI/Windows Forms but most of the questions and examples are in C# and all my searching for unresponsive guis with BackgroundWorker end up with solutions that I can't see are much different to what I'm doing!
Any help gratefully appreciated.
#pragma once
ref class ProgressObject;
namespace LibraryScan {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::IO;
/// <summary>
/// Summary for Form1
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Label^ label1;
protected:
private: System::Windows::Forms::TextBox^ textBox1;
private: System::Windows::Forms::Button^ button1;
private: System::Windows::Forms::FolderBrowserDialog^ folderBrowserDialog1;
private: System::Windows::Forms::OpenFileDialog^ openFileDialog1;
private: System::Windows::Forms::TextBox^ textBoxFiles;
private: System::Windows::Forms::Label^ label2;
private: System::Windows::Forms::Button^ buttonScan;
private: System::Windows::Forms::TreeView^ treeViewFiles;
private: System::Windows::Forms::ImageList^ imageList1;
private: System::ComponentModel::BackgroundWorker^ fileLister;
private: System::ComponentModel::IContainer^ components;
private:
/// <summary>
/// Required designer variable.
/// </summary>
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(Form1::typeid));
this->label1 = (gcnew System::Windows::Forms::Label());
this->textBox1 = (gcnew System::Windows::Forms::TextBox());
this->button1 = (gcnew System::Windows::Forms::Button());
this->folderBrowserDialog1 = (gcnew System::Windows::Forms::FolderBrowserDialog());
this->openFileDialog1 = (gcnew System::Windows::Forms::OpenFileDialog());
this->textBoxFiles = (gcnew System::Windows::Forms::TextBox());
this->label2 = (gcnew System::Windows::Forms::Label());
this->buttonScan = (gcnew System::Windows::Forms::Button());
this->treeViewFiles = (gcnew System::Windows::Forms::TreeView());
this->imageList1 = (gcnew System::Windows::Forms::ImageList(this->components));
this->fileLister = (gcnew System::ComponentModel::BackgroundWorker());
this->SuspendLayout();
//
// label1
//
this->label1->AutoSize = true;
this->label1->Location = System::Drawing::Point(28, 13);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(39, 13);
this->label1->TabIndex = 0;
this->label1->Text = L"Folder:";
//
// textBox1
//
this->textBox1->Location = System::Drawing::Point(74, 13);
this->textBox1->Name = L"textBox1";
this->textBox1->Size = System::Drawing::Size(409, 20);
this->textBox1->TabIndex = 1;
//
// button1
//
this->button1->Location = System::Drawing::Point(489, 13);
this->button1->Name = L"button1";
this->button1->Size = System::Drawing::Size(75, 23);
this->button1->TabIndex = 2;
this->button1->Text = L"Browse...";
this->button1->UseVisualStyleBackColor = true;
this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
//
// folderBrowserDialog1
//
this->folderBrowserDialog1->Description = L"Select the directory that you want to scan.";
this->folderBrowserDialog1->ShowNewFolderButton = false;
//
// openFileDialog1
//
this->openFileDialog1->FileName = L"openFileDialog1";
//
// textBoxFiles
//
this->textBoxFiles->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom)
| System::Windows::Forms::AnchorStyles::Right));
this->textBoxFiles->Location = System::Drawing::Point(34, 62);
this->textBoxFiles->Multiline = true;
this->textBoxFiles->Name = L"textBoxFiles";
this->textBoxFiles->ScrollBars = System::Windows::Forms::ScrollBars::Both;
this->textBoxFiles->Size = System::Drawing::Size(596, 419);
this->textBoxFiles->TabIndex = 3;
this->textBoxFiles->WordWrap = false;
//
// label2
//
this->label2->AutoSize = true;
this->label2->Location = System::Drawing::Point(31, 43);
this->label2->Name = L"label2";
this->label2->Size = System::Drawing::Size(31, 13);
this->label2->TabIndex = 4;
this->label2->Text = L"Files:";
//
// buttonScan
//
this->buttonScan->Enabled = false;
this->buttonScan->Location = System::Drawing::Point(570, 13);
this->buttonScan->Name = L"buttonScan";
this->buttonScan->Size = System::Drawing::Size(75, 23);
this->buttonScan->TabIndex = 5;
this->buttonScan->TabStop = false;
this->buttonScan->Text = L"Scan";
this->buttonScan->UseVisualStyleBackColor = true;
this->buttonScan->Click += gcnew System::EventHandler(this, &Form1::buttonScan_Click);
//
// treeViewFiles
//
this->treeViewFiles->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom)
| System::Windows::Forms::AnchorStyles::Right));
this->treeViewFiles->ImageIndex = 0;
this->treeViewFiles->ImageList = this->imageList1;
this->treeViewFiles->Location = System::Drawing::Point(636, 62);
this->treeViewFiles->Name = L"treeViewFiles";
this->treeViewFiles->SelectedImageIndex = 0;
this->treeViewFiles->Size = System::Drawing::Size(392, 419);
this->treeViewFiles->TabIndex = 6;
this->treeViewFiles->AfterCollapse += gcnew System::Windows::Forms::TreeViewEventHandler(this, &Form1::treeViewFiles_AfterCollapse);
this->treeViewFiles->AfterExpand += gcnew System::Windows::Forms::TreeViewEventHandler(this, &Form1::treeViewFiles_AfterExpand);
//
// imageList1
//
this->imageList1->ImageStream = (cli::safe_cast<System::Windows::Forms::ImageListStreamer^ >(resources->GetObject(L"imageList1.ImageStream")));
this->imageList1->TransparentColor = System::Drawing::Color::Transparent;
this->imageList1->Images->SetKeyName(0, L"Generic_Document.png");
this->imageList1->Images->SetKeyName(1, L"Folder_16x16.png");
this->imageList1->Images->SetKeyName(2, L"FolderOpen_16x16_72.png");
//
// fileLister
//
this->fileLister->WorkerReportsProgress = true;
this->fileLister->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form1::fileLister_DoWork);
this->fileLister->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &Form1::fileLister_ProgressChanged);
this->fileLister->RunWorkerCompleted += gcnew System::ComponentModel::RunWorkerCompletedEventHandler(this, &Form1::fileLister_RunWorkerCompleted);
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(1040, 493);
this->Controls->Add(this->treeViewFiles);
this->Controls->Add(this->buttonScan);
this->Controls->Add(this->label2);
this->Controls->Add(this->textBoxFiles);
this->Controls->Add(this->button1);
this->Controls->Add(this->textBox1);
this->Controls->Add(this->label1);
this->Name = L"Form1";
this->Text = L"Form1";
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
// Show the FolderBrowserDialog.
System::Windows::Forms::DialogResult result = folderBrowserDialog1->ShowDialog();
if ( result == System::Windows::Forms::DialogResult::OK )
{
String^ folderName = folderBrowserDialog1->SelectedPath;
textBox1->Text = folderName;
buttonScan->Enabled = true;
}
}
private: System::Void buttonScan_Click(System::Object^ sender, System::EventArgs^ e)
{
// Scan the folder listed in textBox1 and add the files to textBoxFiles
String^ folder = textBox1->Text;
fileLister->RunWorkerAsync(folder);
}
private: long listFolder(String^ folderName, TreeNode^ rootNode, BackgroundWorker^ worker)
{
// Scan the folder passed in and add the files to textBoxFiles
TreeNode^ newNode = gcnew TreeNode(folderName);
newNode->ImageIndex = 1;
newNode->SelectedImageIndex = 1;
worker->ReportProgress(0, gcnew ProgressObject(rootNode, newNode));
array<String^>^ file = Directory::GetFiles( folderName );
Array::Sort(file);
for (int i = 0; i < file->Length; i++)
{
TreeNode^ fileNode = gcnew TreeNode(file[i]);
worker->ReportProgress(0, gcnew ProgressObject(newNode, fileNode));
System::Threading::Thread::Sleep(1);
}
// Now scan the directories under this one
array<String^>^ dir = Directory::GetDirectories(folderName);
Array::Sort(dir);
for (int i = 0; i < dir->Length; i++)
{
listFolder(dir[i], newNode, worker);
}
return 0L;
}
private: System::Void treeViewFiles_AfterCollapse(System::Object^ sender, System::Windows::Forms::TreeViewEventArgs^ e)
{
e->Node->ImageIndex = 1;
e->Node->SelectedImageIndex = 1;
}
private: System::Void treeViewFiles_AfterExpand(System::Object^ sender, System::Windows::Forms::TreeViewEventArgs^ e)
{
e->Node->ImageIndex = 2;
e->Node->SelectedImageIndex = 2;
}
private: System::Void fileLister_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e)
{
BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
e->Result = listFolder(safe_cast<String^>(e->Argument), nullptr, worker);
}
private: System::Void fileLister_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e)
{
ProgressObject^ prog = safe_cast<ProgressObject^>(e->UserState);
if (prog->currentNode != nullptr)
{
if (prog->rootNode == nullptr)
{
treeViewFiles->Nodes->Add(prog->currentNode);
}
else
{
prog->rootNode->Nodes->Add(prog->currentNode);
}
textBoxFiles->AppendText(String::Concat(prog->currentNode->Text, "\n"));
}
}
private: System::Void fileLister_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e)
{
this->textBoxFiles->AppendText(L"Scan Complete");
}
ref class ProgressObject : public Object
{
public:
TreeNode^ rootNode;
TreeNode^ currentNode;
ProgressObject(TreeNode^ theRootNode, TreeNode^ theCurrentNode)
: rootNode(theRootNode),
currentNode(theCurrentNode)
{
}
};
};
}
Your program suffers from the 3rd most common threading bug. Numbers 1 and 2 are threading races and deadlock, BackgroundWorker is pretty good at helping you avoid those nasty ones. But it doesn't do anything to help you avoid number 3, a fire-hose bug. Mental image here is trying to drink from a running fire-hose, no matter how hard you swallow you can never avoid spilling water.
The water here are the TreeNodes that the worker produces. Your file system is fast and can spit them out at a very high rate. Especially so the second time you run your program and all the file data comes from the file system cache. Tens of thousands per second.
The mouth is the UI thread in your program, it needs to add these nodes to the TreeView and generates a paint to make them visible. At best it can add hundreds per second.
The UI thread starts dispatching the invoke request as soon as your worker first calls ReportProgress(). It executes the request by calling your ProgressChanged event handler. Problem is, as soon as that is done, there's yet another invoke request waiting. It can never catch up and get the invoke queue emptied. It burns 100% core, doing nothing but calling your ProgressChanged event handler.
And stops doing its normal duty, dispatching operating system notifications. Which includes input, you see that it is no longer responsive to mouse clicks and keyboard input. And painting, a low priority task that only gets performed when nothing else needs to be done.
It is not deadlocked or frozen, you'll see it come back alive as soon as your worker thread completes. Usually several seconds after the worker is done, it still needs to work down the backlog of invoke requests that are in the queue. And it works when you turn down the fire-hose, slowing down the worker with Thread.Sleep() so you can swallow fast enough.
You'll need more than one workaround to keep the threads balanced:
Don't call ReportProgress() so often, collect nodes in a List<TreeNode^>^ until you have, say, a hundred of them. Keep thread-safety in mind, you have to create a new List after the ReportProgress() call.
Use the TreeView.Items.AddRange() method in your ProgressChanged event handler, much more efficient than adding them one at a time.
Avoid TreeView doing too much work to get the nodes painted, call its BeginUpdate() method when you start the worker, EndUpdate() in your RunWorkerCompleted event handler. You'll miss out on the "live" update that way but that's of little concern, the user doesn't stand any chance to read them that fast anyway.
Consider not updating the TreeView at all until the worker is done, given that the live view isn't very interesting anyway. Do so in the RunWorkerCompleted event handler.
I am trying to write a program that uses a global vector of a struct in a c++ windows form. Below is the code. The commented areas are attempts I've made to create a vector. Most of them do not compile. Some of them compile, but the program will not display. I do not understand the nature of this error because I was able to create vectors of structs in command line programs. Any help would be appreciated!
#pragma once
#include <vector>
#include <cliext/vector>
namespace student_database {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
/// <summary>
/// Summary for Form11
/// </summary>
public ref class Form11 : public System::Windows::Forms::Form
{
public:
Form11(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form11()
{
if (components)
{
delete components;
}
}
private:
ref struct student{
int index;
String^ name;
cliext::vector <String^> essays;
};
cliext::vector <student^> student_vector; // builds without errors, but program does not start
// array <student^>^ Qarray;
// static cliext::vector<student^>::generic_container^ student_vector = gcnew cliext::vector<student^>;//(0);
// cliext::vector<student^> ^c;
// c = gcnew(cliext::vector<student^>);
//cliext::vector <student> student_vector = gcnew cliext::vector <student>;
//cliext::vector<student^> ^student_vector = gcnew(cliext::vector<student^>);
// cliext::vector <student^>^ student_vector;
// Microsoft::VisualC::StlClr::Ivector<student^> ^student_vector;
// Ivector<student^> ^student_vector;
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->SuspendLayout();
//
// Form11
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(284, 262);
this->Name = L"Form11";
this->Text = L"Form11";
this->Load += gcnew System::EventHandler(this, &Form11::Form11_Load);
this->ResumeLayout(false);
}
#pragma endregion
private: System::Void Form11_Load(System::Object^ sender, System::EventArgs^ e) {
}
};
}
I want to disable the dropdown shadow on the dropdown of a ToolStripDropDownButton. If the dropdown menu contains items that have dropdowns themselves (e.g. multi-level menu) then setting the DropShadowEnabled to false on the ToolStripDropDownButton causes the top level dropdown to appear at the wrong position. See attached picture.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
toolStripDropDownButton1.DropDown.DropShadowEnabled = false;
}
}
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripDropDownButton1 = new System.Windows.Forms.ToolStripDropDownButton();
this.item1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.subitem1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStrip1.SuspendLayout();
this.SuspendLayout();
//
// toolStrip1
//
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripDropDownButton1});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(292, 25);
this.toolStrip1.TabIndex = 0;
this.toolStrip1.Text = "toolStrip1";
//
// toolStripDropDownButton1
//
this.toolStripDropDownButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.toolStripDropDownButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.item1ToolStripMenuItem});
this.toolStripDropDownButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton1.Image")));
this.toolStripDropDownButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripDropDownButton1.Name = "toolStripDropDownButton1";
this.toolStripDropDownButton1.Size = new System.Drawing.Size(29, 22);
this.toolStripDropDownButton1.Text = "toolStripDropDownButton1";
//
// item1ToolStripMenuItem
//
this.item1ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.subitem1ToolStripMenuItem});
this.item1ToolStripMenuItem.Name = "item1ToolStripMenuItem";
this.item1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.item1ToolStripMenuItem.Text = "item1";
//
// subitem1ToolStripMenuItem
//
this.subitem1ToolStripMenuItem.Name = "subitem1ToolStripMenuItem";
this.subitem1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.subitem1ToolStripMenuItem.Text = "subitem1";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.toolStrip1);
this.Name = "Form1";
this.Text = "Form1";
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton1;
private System.Windows.Forms.ToolStripMenuItem item1ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem subitem1ToolStripMenuItem;
}
This is very typical lossage in the ToolStripItem classes. Clearly it is a bug, it probably got introduced when they applied a hack to work around a Windows problem. You can still see the internal bug number in the Reference Source:
public bool DropShadowEnabled {
get {
// VSWhidbey 338272 - DropShadows are only supported on TopMost windows
// due to the flakeyness of the way it's implemented in the OS. (Non toplevel
// windows can have parts of the shadow disappear because another window can get
// sandwiched between the SysShadow window and the dropdown.)
return dropShadowEnabled && TopMost && DisplayInformation.IsDropShadowEnabled;
}
set { // etc... }
}
But without a corresponding fix in the setter and the renderer.
The flakeyness they mentioned actually got fixed in Vista, you are still running on XP so you can't see it. Drop shadows are done differently on the Aero desktop and it is a system setting whether or not they are enabled. So using the property is entirely ineffective on Aero.
These ToolStripItem class bugs didn't get fixed after the .NET 2.0 release, about the entire Winforms team moved over to the WPF group. And they are certainly not getting fixed now, no point filing a bug at connect.microsoft.com although you are free to do so.
With the added wrinkle that the property just cannot have an effect anymore on later versions of Windows since it is now a system setting, the only logical thing to do here is to throw in the towel. Don't change the property.
i could run the How To Play a File code sample in c++ win32 console application, but when i try to implement it by winforms i get these Link errors:
Error 2 error LNK2020: unresolved token (0A000016) IID_IMediaEvent
Error 3 error LNK2020: unresolved token (0A000017) IID_IMediaControl
and some more link errors .....
here is the code of the form:
#include <dshow.h>
#pragma comment(lib, "Strmiids.lib")
namespace Form1 {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
/// <summary>
/// Summary for Form1
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->SuspendLayout();
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(284, 262);
this->Name = L"Form1";
this->Text = L"Form1";
this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
this->ResumeLayout(false);
}
#pragma endregion
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
// Initialize the COM library.
HRESULT hr = CoInitialize(NULL);
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph. IMPORTANT: Change this string to a file on your system.
hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);
if (SUCCEEDED(hr))
{
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}
};
}
How can i setup the build environment in winforms to do the DirectShow programming?
im using windows SDK v7.1 And vc++ 2010
You are not getting a great diagnostic. The problem is that DirectShow is native code. But you are letting the compiler think it is okay to compile it in managed mode. Which works surprisingly well, until the linker takes a nosedive. You need to make it look like this:
#pragma once
#pragma managed(push, off)
#include <dshow.h>
#pragma managed(pop)
#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "ole32.lib")
// etc..
This probably generates a flurry of errors. Right-click the project in the Solution Explorer window, Properties, Configuration Properties, General. Change Common Language Runtime support from /clr:pure to /clr. This played a sample .avi file properly when I tried it. In a DirectShow window, not the form. The sample code was designed only to work in a console application. You should also remove the calls to CoInitialize and CoUninitialize, .NET already initializes COM. Improving the error handling is advisable. Consider embedding Windows Media Player instead.