Find Control Name from non-UI thread - wpf

I found a nice Find Name code snippet that I'm using in a WPF solution:
public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(Control.NameProperty) as string;
if (controlName == name)
{
return child as T;
}
else
{
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
}
return null;
}
But this only works if I am on the UI thread.
I have another thread that is playing an audio file with an end sync. I want to use the code above to set a dep property on the ui thread, but I keep getting a cross-thread error.
Even trying a simple:
SoundFXPad selectedSoundFXPad = (SoundFXPad)m_parent.FindName("panelC" + numbervar);
Gives me the same error
All the other thread-safe WPF Dispatcher-Invoke codes I have seen assume you already know the name of the control. Is there a way to use the either code above in a thread-safe way to affect a UI control from another thread where the name needs to be "found"?
Thank You!

There's generally one UI thread per application (generally; you can create multiple, but it's not common). So you don't need the control name to find the dispatcher - try this:
Application.Current.Dispatcher.Invoke(new Action(
delegate {
// Put code that needs to run on the UI thread here
}));

Related

Windows Form UI Update issue with Task in C#

We are working on a windows application which caters to an engineering calculation which are essentially very long running. So we are basically trying to keep the calculation module separate and working in a separate worker thread and pass it an Action delegate in method signature which will be invoked to report the calculation progress in the UI. The delegate handler declared in the UI will be updating the UI. We found that while a huge loop is running in the calculation, the UI is not showing the periodic progress and only displaying the final result. If a Thread Sleep for 1 millisecond is introduced in the calculation loop, the UI is getting updated correctly. This is not expected behavior as we are executing the calculation using a separate Task and updating the UI using BeginInvoke calls.
I have created a simple application to demonstrate our approach and code so that it is easier to understand. It is obvious that we are missing something very simple but cannot quite pin it down. Will appreciate any insights.
Thanks for reading.
private void cmdStart_Click(object sender, EventArgs e)
{
txtResultDIsplay.Text = "";
var maxIterations = long.Parse(txtIterationNo.Text.Trim());
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<double> calculationTask = Task.Factory.StartNew<double>(
() => SumRootN(maxIterations, UpdateProgress));
var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
}
private void DisplayResult(Task<double> calculationTask)
{
txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
}
private void UpdateProgress(string msg)
{
this.BeginInvoke((MethodInvoker)delegate
{
txtResultDIsplay.Text = msg;
});
}
public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
{
int root = 20;
double result = 0;
for (long i = 1; i < maxIterations; i++)
{
Thread.Sleep(1);
result += Math.Exp(Math.Log(i) / root);
progressUpdateDelegate(result.ToString("0.00000"));
}
return result;
}
It is possible you are flooding the UI thread with your progress updates. You need to find a way to prevent lots of updates occurring.
We can solve the problem using tasks!
Task progressTask = null;
private void UpdateProgress(string msg)
{
//only schedule work if the task if not running
if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
{
//Create a task representing the update
progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
.ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
}
}
Note that locking will not do here as you want to skip the update if there is already an update occurring.

issues with BackGround worker across classes

this is my first question in this forum, hope it will not be duplicated somewhere because i have searched for the respons for almost 4 weeks without making any progress.
here is my situation,
im developing an application that need to do a lot of background operation, for that reason i creat 2 BKW, the first one used to load data from a DB and put it inside an observable collection , 'no need to report progress or support cancelation for this one' :
private Boolean loadTestSteps()
{
// Create a background worker thread that don't report progress and does not
// support cancelation
BackgroundWorker wk_LoadTestSteps = new BackgroundWorker();
wk_LoadTestSteps.DoWork += new DoWorkEventHandler(wk_LoadTestSteps_DoWork);
wk_LoadTestSteps.RunWorkerAsync();
return true;
}
observable collection class :
public class clsTestStep : DependencyObject
{
public static DependencyProperty TestStepProperty = DependencyProperty.Register(
"TestStep", typeof(String), typeof(clsTestStep));
public string TestStep
{
get { return (string)GetValue(TestStepProperty); }
set { SetValue(TestStepProperty, value); }
} and so on for the rest of items....
now the main backGround that should do the longer operation and in the same time report the progress to the main UI ,declared like so
private void InitializeBackGroundWork()
{
_wk_StartTest = new BackgroundWorker();
// Create a background worker thread that ReportsProgress &
// SupportsCancellation
// Hook up the appropriate events.
_wk_StartTest.DoWork += new DoWorkEventHandler(_wk_StartTest_DoWork);
_wk_StartTest.ProgressChanged += new ProgressChangedEventHandler
(_wk_StartTest_ProgressChanged);
_wk_StartTest.RunWorkerCompleted += new RunWorkerCompletedEventHandler
(_wk_StartTest_RunWorkerCompleted);
_wk_StartTest.WorkerReportsProgress = true;
_wk_StartTest.WorkerSupportsCancellation = true;
_wk_StartTest.RunWorkerAsync();
}
in the do work events, exactly in the foreach loop i encontered an error saying : you cannot access this object because another thread own it :
void _wk_StartTest_DoWork(object sender, DoWorkEventArgs e)
{
//Loop through each test step and perform Test
foreach (clsTestStep item in _testStep)
{
Thread.Sleep(200);
temp[0] = item.TestStep;
temp[1] = item.Delay.ToString();
temp[2] = item.NumberRepetition.ToString();
temp[3] = item.Mode.ToString();
//Report % of Progress, Test step Name,and the paragraph from Class PerformTest
_wk_StartTest.ReportProgress(counter,
temp[0]);
counter += 1;
_performTest.Fdispatcher(temp, out _paragraph);
//_si.PgBarMax = Convert.ToDouble(_testStep.Count);
}
//Report completion on operation completed
_wk_StartTest.ReportProgress(counter);
}
what im missing here please, because my head is gonna explod from searching !!!
It sounds like your ObservableCollection is created and so owned by an other thread so your _wk_StartTest_DoWork method can't access it.
Where your _testStep variable comes from ?
By the way, in a multithread environment when many thread access the same data you should prefer the use of ConcurrentBag class instead of an ObservableCollection. ConcurrentBag is thread safe.
for the ones that may enconter this kind of problem ^^
finnaly i have found a way to acess class even if its not owned by the current thread here a nice article explaining step by step how to do this here

Overwrite registeredName

I've a method that when called returns a LinearGradientBrush with random color for the GradientStop and I use on them mylabel.RegisterName for later use in a storyboard animation.
To prevent the error on first call where no registered name are present I do this:
try
{
myLabel.UnregisterName("GS1");
myLabel.UnregisterName("GS2");
myLabel.UnregisterName("GS3");
myLabel.UnregisterName("GS4");
}
catch
{
}
I have not found a way to overwrite registered name. There's a better way to do this?
What about UnregisterName?
(That's what I get for attempting to answer when I can only read part of the question...)
Digging around a bit more, may have found another (WAY simpler) alternative:
var isGs1Defined = NameScope.GetNameScope(myLabel).FindName("GS1") == null;
or packaged up nicely:
public static bool IsNameRegistered(DependencyObject depObject, string name)
{
var namescope = NameScope.GetNameScope(depObject);
if(namescope == null)
return false;
return namescope.FindName(name) != null;
}

Dispatching events into right thread

I have developed a wrapper for a library that uses a callback to notify events. This callback is called using another thread than UI's thread, so the wrapper uses the following script to call the event handlers into the right thread for a WinForm application.
void AoComm::Utiles::Managed::DispatchEvent( Delegate^ ev, Object^ sender, Object^ args )
{
ComponentModel::ISynchronizeInvoke^ si;
array<Delegate^>^ handlers;
if(ev != nullptr)
{
handlers= ev->GetInvocationList();
for(int i = 0; i < handlers->Length; ++i)
{
// target implements ISynchronizeInvoke?
si = dynamic_cast<ComponentModel::ISynchronizeInvoke^>(handlers[i]->Target);
try{
if(si != nullptr && si->InvokeRequired)
{
IAsyncResult^ res = si->BeginInvoke(handlers[i], gcnew array<Object^>{sender, args});
si->EndInvoke(res);
}else{
Delegate^ del = handlers[i];
del->Method->Invoke( del->Target, gcnew array<Object^>{sender, args} );
}
}catch(System::Reflection::TargetException^ e){
Exception^ innerException;
if (e->InnerException != nullptr)
{
innerException = e->InnerException;
}else{
innerException = e;
}
Threading::ThreadStart^ savestack = (Threading::ThreadStart^) Delegate::CreateDelegate(Threading::ThreadStart::typeid, innerException, "InternalPreserveStackTrace", false, false);
if(savestack != nullptr) savestack();
throw innerException;// -- now we can re-throw without trashing the stack
}
}
}
}
This code works pretty well, but I have read about Dispatcher class for WPF that do the same than my code (and more, of course).
So, is there something (class, mechanism, ...) equivalent to Dispatcher class for WinForms?
Thanks.
Right, this isn't the right way to do it. Winforms and WPF have different synchronization providers, they install theirs in System::Threading::SynchronizationContext::Current.
To use it, copy the Current value in your constructor. When you are ready to fire the event, check if it is nullptr. If it was then your object got constructed in a worker thread and you should fire your event directly. If it isn't then use the Post() method to run a helper method on the UI thread. Have that helper method fire the event.

Finding Controls of the Same Type

Does anybody have a good way of finding ALL the controls within an object that is of the same type? Here's my scenario, I have a tab control and within each tab control exists a user control (ALL of which match the same base type e.g. MyBaseClassControl). I want to be able to find that user control WITHOUT having to use the control.FindName("controlName") method, rather I would like to get a handle on the control by type (e.g. base class). The VisualTreeHelper class seems to do nothing for me as it only returns native Silverlight objects.
Given this:
public static IEnumerable<DependencyObject> AllChildren(this DependencyObject root)
{
var children = root.DirectChildren().ToList();
return children.Union(children.SelectMany(o => o.AllChildren()));
}
public static IEnumerable<DependencyObject> DirectChildren(this DependencyObject parent)
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
yield return System.Windows.Media.VisualTreeHelper.GetChild(parent, i);
}
You could do this:
myObj.AllChildren().OfType<MyBaseClassControl>();

Resources