Set DocumentViewer.Document from another thread? - wpf-controls

I have a class with a method (CreateDocument) that fires an event at the end. The event args contain a FixedDocument. In my MainWindow code I try to set a DocumentViewer's Document like:
void lpage_DocCreated(object sender, LabelDocumentEventArgs e)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
FixedDocument fd = e.doc;
documentViewer1.Document = fd;
documentViewer1.FitToWidth();
return null;
}), null);
}
I receive "The calling thread cannot access this object because a different thread owns it." on line documentViewer1.Document = fd;
I am able to update a progress bar in another event handler that the same method fires while it is working:
Int32 progress = Int32.Parse(sender.ToString());
progBar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progBar.Value = progress;
return null;
}), null);
I can't figure out why I can't set the document when I'm essentially doing the same type of thing when I set the progress bar value.

The FixedDocument element also has thread-affinity. So if you are creating it in a separate thread than the documentViewer1, then you would get that exception.
Basically, anything that derives from DispatcherObject has a thread-affinity. FixedDocument derives from DispatcherObject, just like the viewer controls.

Related

Multithreaded program hangs on control method call

I've got a UI with 2 buttons and a textbox.
When the user presses the "Receive" button, a thread is started for an infinite work loop (receiving and printing messages from multicast). When the user presses the "Stop" button, the thread is given it's "kill flag" and then the program waits for the Join() to finish.
That infinite work loop calls back to the UI textbox with a SetTextBoxText(System::String) method. This is causing a race condition. Sometimes, the thread finishes the Join() just fine. Others, the program hangs forever on the Join().
I believe this is because when the UI thread calls Join() on its work thread, the work thread may be in the middle of its loop and is trying to invoke a locked thread. The UI thread is waiting for the Join() and is unable to do anything (like SetTextBoxText).
So, race-condition or deadlock, I need a way to check if the control is usable. If it is, then call the method to print like normal. If it is not usable, then don't try to call the SetTextBoxText method and continue like normal.
Code time, I'm actually using a UserControl, not a separate textbox, button1, and button2. I call the UserControl "MyUserControl". It contains the following code:
System::Void MyUserControl::buttonReceive_Click(System::Object^ sender, System::EventArgs^ e)
{
//each MyUserControl has their own myThread
this->myThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(this, &MyUserControl::WorkLoop));
this->workLoopFlag = true;
this->myThread->Start();
}
void MyUserControl::WorkLoop()
{
while (this->workLoopFlag == true)
{
System::Threading::Thread::BeginCriticalRegion();
//this->myThread->BeginCriticalRegion();
Process();
//this->myThread->EndCriticalRegion();
System::Threading::Thread::EndCriticalRegion();
}
}
void EntityStatePduProcessor::Process()
{
//These 2 lines are for getting the correct MyUserControl to edit. Stored in GlobalVars::myList
ThreadPredicate^ threadPred = gcnew ThreadPredicate(System::Threading::Thread::CurrentThread->ManagedThreadId);
UserControlDataCollection^ userControlDataColl = GlobalVars::myList->Find(gcnew System::Predicate<UserControlDataCollection^>(threadPred, &ThreadPredicate::IsMatch));
System::Threading::Thread::BeginCriticalRegion();
if (userControlDataColl != nullptr) //should only happen after killing a thread
{
MyUserControl^ controlToEdit = (MyUserControl^)System::Windows::Forms::Control::FromHandle(userControlDataColl->control);
if (controlToEdit != nullptr)
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
controlToEdit->SetTextBoxConsoleText("a system::string to place into the textbox");
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
else
{
System::Windows::Forms::MessageBox::Show("controlToEdit == nullptr");
}
}
System::Threading::Thread::EndCriticalRegion();
}
delegate void MyStringDelegate(System::String ^ text);
void MyUserControl::SetTextBoxConsoleText(System::String^ input)
{
if (this->InvokeRequired) // (this->consoleTextBox->InvokeRequired)
{
MyStringDelegate^ myStrDel = gcnew MyStringDelegate(this, &MyUserControl::SetTextBoxConsoleText);
this->Invoke(myStrDel, gcnew array<Object^> { input });
}
else
{
this->textBoxConsole->Text = input;
this->textBoxConsole->Refresh();
}
}
System::Void MyUserControl::buttonStop_Click(System::Object^ sender, System::EventArgs^ e)
{
this->workLoopFlag = false; // kill the endless while loop
this->myThread->Join();
}
I tried to post the code in chronological order and only include the essentials while explaining the gaps in //comments.
The code that causes the hang is in the 3rd method shown Process() outlined with //~~~.
How do I correctly ensure that the controlToEdit is still able to access and not just waiting on a Join()?

Vala Threading: invocation of void method not allowed as expression

Hey i've been writing an application in which i need to create thread to perform background tasks while the GUI is being loaded. However no matter that i do i can find a way around this error:
error: invocation of void method not allowed as expression
Thread<void> thread = new Thread<void>.try("Conntections Thread.", devices_online(listmodel));
The line in question is the creating of a new thread which calls the "devices_online" method.
The Full code which is being effected is:
try {
Thread<void> thread = new Thread<void>.try("Conntections Thread.", devices_online(listmodel));
}catch(Error thread_error){
//console print thread error message
stdout.printf("%s", thread_error.message);
}
And Method is:
private void devices_online(Gtk.ListStore listmodel){
//clear the listview
listmodel.clear();
//list of devices returned after connection check
string[] devices = list_devices();
//loop through the devices getting the data and adding the device
//to the listview GUI
foreach (var device in devices) {
string name = get_data("name", device);
string ping = get_data("ping", device);
listmodel.append (out iter);
listmodel.set (iter, 0, name, 1, device, 2, ping);
}
}
Ive done so much Googleing but Vala isn't exactly the most popular language. Any help?
Like the compiler error says, you are getting a void by calling a method. Then you are trying to pass the void value into the thread constructor.
Thread<void> thread = new Thread<void>
.try ("Conntections Thread.", devices_online (listmodel));
The second cunstructor argument of Thread<T>.try () expects a delagate of type ThreadFunc<T> which you are not satisfying.
You are confusing a method call with a method delegate.
You can pass an anonymous function to fix that:
Thread<void> thread = new Thread<void>
.try ("Conntections Thread.", () => { devices_online (listmodel); });

Is there a way to override the handler called when a user clicks a checkbox in a CListCtrl? (MFC)

I am trying to disable the user's ability to alter the state of a checkbox in a List Control. I am currently changing the state pragmatically. I already handle the LVN_ITEMCHANGED message, and trying to alter the state there isn't an option due to the layout of the rest of the program. I have also tried doing a HitTest when the user clicks in the List Control and simply resetting the checkbox there but that isn't giving me the exact results I am looking for.
Is there a specific message sent or a function I can override when the user clicks the checkbox itself? I would just like to override the handler or catch the message so that it doesn't go anywhere.
Solution:
I ended up removing the LVS_EX_CHECKBOXES flag and created my own implementation. That way the there is only one way to change the icons. Reading the link from the previous question gave me an idea to set a "busy" flag, otherwise I would get stack overflow errors.
// In my dialog class
m_CListCtrl.SetImageList(&m_ImgList, LVSIL_SMALL); // Custom checkboxes (only two images)
// ...
void CMyDialog::OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
{
if(busy) { return; }
// ....
}
// When calling the SetCheck function:
busy = TRUE; // Avoid stack overflow errors
m_CListCtrl.SetCheck(index, fCheck);
busy = FALSE;
// I derived a class from CListCtrl and did an override on the get/set check:
class CCustomListCtrl : public CListCtrl
{
BOOL CCustomListCtrl::SetCheck(int nItem, BOOL fCheck)
{
TCHAR szBuf[1024];
DWORD ccBuf(1024);
LVITEM lvi;
lvi.iItem = nItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.pszText = szBuf;
lvi.cchTextMax = ccBuf;
GetItem(&lvi);
lvi.iImage = (int)fCheck;
SetItem(&lvi);
return TRUE;
}
// Just need to determine which image is set in the list control for the item
BOOL CCustomListCtrl::GetCheck(int nItem)
{
LVITEM lvi;
lvi.iItem = nItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_IMAGE;
GetItem(&lvi);
return (BOOL)(lvi.iImage);
}
}
This is not as elegant as I had hoped, but it works flawlessly.

Getting edit box text from a modal MFC dialog after it is closed

From a modal MFC dialog, I want to extract text from an edit box after the dialog is closed. I attempted this:
CPreparationDlg Dlg;
CString m_str;
m_pMainWnd = &Dlg;
Dlg.DoModal();
CWnd *pMyDialog=AfxGetMainWnd();
CWnd *pWnd=pMyDialog->GetDlgItem(IDC_EDIT1);
pWnd->SetWindowText("huha max");
return TRUE;
It does not work.
The dialog and its controls is not created until you call DoModal() and as already pointed, is destroyed already by the time DoModal() returns. Because of that you cannot call GetDlgItem() neither before, nor after DoModal(). The solution to pass or retrieve data to a control, is to use a variable in the class. You can set it when you create the class instance, before the call to DoModal(). In OnInitDialog() you put in the control the value of the variable. Then, when the window is destroyed, you get the value from the control and put it into the variable. Then you read the variable from the calling context.
Something like this (notice I typed it directly in the browser, so there might be errors):
class CMyDialog : CDialog
{
CString m_value;
public:
CString GetValue() const {return m_value;}
void SetValue(const CString& value) {m_value = value;}
virtual BOOL OnInitDialog();
virtual BOOL DestroyWindow( );
}
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
SetDlgItemText(IDC_EDIT1, m_value);
return TRUE;
}
BOOL CMyDialog::DestroyWindow()
{
GetDlgItemText(IDC_EDIT1, m_value);
return CDialog::DestroyWindow();
}
Then you can use it like this:
CMyDialog dlg;
dlg.SetValue("stackoverflow");
dlg.DoModal();
CString response = dlg.GetValue();
Open your dialog resource, right-click on the textbox and choose "Add variable", pick value-type and CString
In the dialog-class: before closing, call UpdateData(TRUE)
Outside the dialog:
CPreparationDlg dlg(AfxGetMainWnd());
dlg.m_myVariableName = "my Value";
dlg.DoModal();
// the new value is still in dlg.m_myVariableName
DoModal() destroys the dialog box before it returns and so the value is no longer available.
It's hard to tell why you are setting m_pMainWnd to your dialog. To be honest, I'm not really sure what you are trying to do there. That's bound to cause problems as now AfxGetMainWnd() is broken.
Either way, you can't get the dialog box's control values after the dialog has been destroyed.
I often use
D_SOHINH dsohinh = new D_SOHINH();
dsohinh.vd_kichthuoc=v_kichthuocDOC;
dsohinh.vd_sohinh=v_soluongDOC;
if(dsohinh.DoModal()==IDOK)
{
v_soluongDOC=dsohinh.vd_sohinh;
v_kichthuocDOC=dsohinh.vd_kichthuoc;
}
SetModifiedFlag(true);
UpdateAllViews(NULL);
With dsohinh is Dialog form that you want to get data to mainform .
After get data then call SetModifiedFlag(true) to set view data updated.
call UpdateAllViews(NULL) to Set data to mainform
This solution may seem long, meaning that so much code has been written for this seemingly small task.
But when we have a list or tree inside the child window where all the items are created in the child window
and the items have to be moved to the parent window,
then it makes sense.
This source code can easily create a window and transfer information from the window before closing to the parents.
//copy the two functions in your code
//1- bool peek_and_pump(void)
// template<class T,class THISCLASS>
//2- void TshowWindow(int id,T *&pVar,THISCLASS *ths)
//and make two member variable
// bool do_exit;
// bool do_cancel;
//in child dialog class.
//set true value in do_exit in child dialog for exit
CchildDialog *dlg;
template<class T,class THISCLASS>
void TshowWindow(int id,T *&pVar,THISCLASS *ths)
{
T *p=pVar;
if(!p)
p= new T;
if(p->m_hWnd)
{
p->SetForegroundWindow();
}
else
{
delete p;
p= new T;
if(!(p->m_hWnd && IsWindow(p->m_hWnd)))
{
p->Create(id,ths);
if(IsWindow(p->m_hWnd))
p->ShowWindow(TRUE);
}
}
pVar=p;
}
bool peek_and_pump(void)
{
MSG msg;
#if defined(_AFX) || defined(_AFXDLL)
while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
if(!AfxGetApp()->PumpMessage())
{
::PostQuitMessage(0);
return false;
}
}
long lIdle = 0;
while(AfxGetApp()->OnIdle(lIdle++))
;
#else
if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
#endif
return true;
}
void CparentPage::OnBnClick1()
{
if(dlg)
{
dlg->DestroyWindow();
}
TshowWindow<CchildDialog,CparentPage>(IDD_DIALOG_child,dlg,this);
dlg->GetDlgItem(IDC_EDIT_1)->SetWindowText("");
dlg->m_temp_window.EnableWindow(FALSE);//enable or disable controls.
dlg->UpdateData(false);//for to be done enable of disable or else.
dlg->do_exit=false;
dlg->do_cancel=false;
while(dlg->do_exit==false)
{
peek_and_pump();//wait for dlg->do_exit set true
}
if( dlg->do_cancel==false )
{
CString str1;
dlg->GetDlgItem(IDC_EDIT_1)->GetWindowText(str1);
//or other member variale of CchildDialog
//after finish all work with dlg then destroy its.
}
dlg->DestroyWindow();
}
void CchildDialog::OnBnClickedOk()
{
UpdateData();
OnOK();
do_exit=true;
do_cancel=false;
}
void CchildDialog::OnBnClickedCancel()
{
OnCancel();
do_exit=true;
do_cancel=true;
}

Qt thread call issues

Please help me. I am struck-up with thread concept. Actually my problem : I want to display the cities List in the combobox. I am getting cities list from the webservice. I am using thread for update the combo box value after webserice call finished.
Here I can call the webservice. But I couldn't get the Reply.
I am using the following code.
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
CGNetwork *cgNetwork = new CGNetwork();
ui->setupUi(this);
renderThread = new RenderThread(cgNetwork);
renderThread->start();
connect(renderThread,SIGNAL(finished()),this,SLOT(initControls()));
}
void MainWindow::initControls()
{
CGMainWindowUtility *pointer = CGMainWindowUtility::instance();
QStringList cityitems;
cityitems <<tr("All");
cityitems.append(pointer->getCityList());
QStringListModel *cityModel = new QStringListModel(cityitems, this);
ui->cityComboBox->setModel(cityModel);
}
RenderThread.cpp:
RenderThread::RenderThread(CGNetwork *cgnetwork)
{
cityUrl = "http://112.138.3.181/City/Cities";
categoryUrl = "http://112.138.3.181/City/Categories";
}
void RenderThread::run()
{
qDebug()<< "THREAD Started";
CGNetwork *cgnetworks = new CGNetwork();
cgnetworks->getCityList(cityUrl);
}
CGNetwork.cpp:
void CGNetwork ::getCityList(const QUrl url)
{
cityGuideNetworkAccessManager = new QNetworkAccessManager(this);
qDebug()<<"connection";
connect(cityGuideNetworkAccessManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseCityList()));
const QNetworkRequest cityRequest(url);
cityReply= cityGuideNetworkAccessManager->get(cityRequest);
connect(cityReply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError()));
}
void CGNetwork::parseCityList()
{
qDebug()<<"Parsing";
cgParser = new CGJsonParser();
cgParser->CityRead(cityReply);
}
Since QNetworkAccessManager works asynchronously, there's no need for a separate thread. You can call getCityList directly from your main thread and it won't block.
I think your slots weren't called because your QThread::run returned before any of the work its been doing had a chance to complete, since getCityList just initiated an http request without waiting for it (because QNetworkAccessManager::get doesn't block like I said above).
Also as a side note, your slots aren't getting the same parameters as their corresponding signals, I don't remember if Qt supports this.

Resources