WPF - Updating Label Content During Processing - c#

Well I've tried several methods of getting this to work, background worker, Dispatcher.Invoke, threading within the called class and nothing seems, to work. The best solution so far is an Extension method which calls the invoke of the control. Also I've tried avoid passing the data for the label through my event classes and simply invoking within my processing code, however this made no difference.
In regards to the background component, I kept getting exceptions saying the background worker was busy, so I instantiated the class several times, however the label only visibly changed once the entire operation had been complete.
I've removed my previous code, here's everything that is relevant, as it seems the issue is difficult to resolve.
Method Being Called
private void TestUris()
{
string text = new TextRange(rtxturis.Document.ContentStart, rtxturis.Document.ContentEnd).Text;
string[] lines = Regex.Split(text.Remove(text.Length - 2), "\r\n");
foreach (string uri in lines)
{
SafeUpdateStatusText(uri);
bool result;
string modUri;
if (!uri.Contains("http://"))
{
modUri = uri;
result = StoreData.LinkUriExists(new Uri("http://" + modUri));
}
else
{
modUri = uri.Substring(7);
result = StoreData.LinkUriExists(new Uri(uri));
}
if (!result)
{
Yahoo yahoo = new Yahoo();
yahoo.Status.Sending += (StatusChange);
uint yahooResult = 0;
yahooResult = yahoo.ReturnLinkCount(modUri);
if (yahooResult > 1000 )
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 1000, "Will be processed", true)); }
else
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, (int)yahooResult, "Insufficient backlinks", false)); }
}
else
{
results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 0, "Previously been processed", false));
}
}
foreach (var record in results)
{
dgvresults.Items.Add(record);
}
EnableStartButton();
}
Yahoo Class
public class Yahoo
{
/// <summary>
/// Returns the amount of links each Uri has.
/// </summary>
public uint ReturnLinkCount(string uri)
{
string html;
Status.Update(uri, false); //this is where the status is called
try
{
html = client.DownloadString(string.Format("http://siteexplorer.search.yahoo.com/search?p=http%3A%2F%2F{0}&fr=sfp&bwm=i", uri));
}
catch (WebException ex)
{
ProcessError(ex.ToString());
return 0;
}
return (LinkNumber(html));
}
Status Classes
public class StatusEventArgs : EventArgs
{
private string _message;
private bool _isidle;
public StatusEventArgs(string message, bool isidle)
{
this._message = message;
this._isidle = isidle;
}
public bool IsIdle
{
get { return _isidle; }
}
public string Message
{
get { return _message; }
}
}
public class Status
{
public Status()
{
}
// Declaring an event, with a custom event arguments class
public event EventHandler<StatusEventArgs> Sending;
// Some method to fire the event.
public void Update(string message, bool isIdle)
{
StatusEventArgs msg = new StatusEventArgs(message, isIdle);
OnUpdate(msg);
}
// The method that invokes the event.
protected virtual void OnUpdate(StatusEventArgs e)
{
EventHandler<StatusEventArgs> handler = Sending;
if (handler != null)
{
handler(this, e);
}
}
}
Method That Changes the labels Content
private void StatusChange(object sender, StatusEventArgs e)
{
if(!e.IsIdle)
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Green;
lblstatus.Refresh();
}
else
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Grey;
lblstatus.Refresh();
}
}
The Refresh static method called:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render , EmptyDelegate);
}
Another EDIT: Staring at my code for a bit longer, I've realised, that the foreach loop will execute really quickly, the operation which takes the time, is
yahooResult = yahoo.ReturnLinkCount(modUri);
Therefore I've declared the status class (which handles the event and invokes the label etc) and subscibed to it. I've gotten better results, although it still feels random, sometimes I see a couple of label updates, and sometimes one even though the exact same URI's are passed, so weird.

I hope there is sth. helpful...
private void button1_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
int result = 0;
for (int i = 0; i < 9999999; i++)
{
result++;
Dispatcher.BeginInvoke(new Action(() =>
{
this.label1.Content = result;
}));
Thread.Sleep(1);
}
});
}

SOLVED IT YES WOOHOOOOOOOO 3 days of testing, testing, testing.
I decided to start a new project just with the extension method above and a simply for loop to test UI update functionality. I started testing different DispatchPrioraties (tested them all).
Weirdly, I found the highest priorities were the worse, for example using Send didn't update the label at all, Render updated it twice on average. This was the weird behavior I was experiencing as I tried different priorities. I discovered Background:
The enumeration value is 4. Operations are processed after all other non-idle operations are completed.
Now this sounded exactly what I didn't want, as obviously the label should update during processing, hence why I never tried it. I'm guessing that once one of my method has been completed, before the next it called, the UI is updated. I'm find of guessing, but it 100% consistently updates correctly on two separate operations.
Thanks all.

Well this is going to sound stupid but you could just reference the forms namespace, and then you can do this
using System.Windows.Forms;
mylabel = "Start";
Application.doEvents();
myLabel = "update"
Application.doEvents();
now the problem using this would be you are using wpf but you can still reference forms and use this namespace. The other issue is what ever is in the que would execute directly to the ui. However this is the most simplistic way of doing label updates i could think of.

would it be easier/better to add the status info as a property on this object, and have it just fire property change notifications?
that way the label text (or whatever) could be bound to the property instead of having the async work try to update a label?
or add a method like this to update status if you have to update it?
void SafeUpdateStatusText(string text)
{
// update status text on event thread if necessary
Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate
{
lblstatus.Content = text;
}, null);
}
otherwise, i don't think we have enough details to help yet....

I hope this helps:
private delegate void UpdateLabelDelegate(DependencyProperty dp, object value);
public void UpdateLabelContent(Label label, string newContent)
{
Dispatcher.Invoke(new UpdateLabelDelegate(label.SetValue), DispatcherPriority.Background, ContentProperty, newContent);
}
Usage:
while (true)
{
UpdateLabelContent(this.lblStatus, "Next random number: " + new Random().Next());
Thread.Sleep(1000);
}

Related

C# object reference gets SOMETIMES lost in Threads that end via EventHandler

I have a strange problem with following code, that is started about every minute.
Normally everything works fine, but sometimes the HandleCalcStatistikMarkersDone function raises an error because of a NullReferenceException.
I try to explain with the code:
I have a class Strategies. This class is started about every minute to calculate and update some information in a MySQL database. This class is instantiated multiple times in separate threads within a ticker on a form.
public partial class mainForm: Form
{
//do something including ticker, that starts RunStatistik about every minute after the previous thread ended
private void RunStatistik()
{
foreach (InternalObject objPar in InternalObjects)
{
Strategies.StrategyParameter giveParms = new Strategies.StrategyParameter();
giveParms.pair= objPar.pair;
Strategies strat = new Strategies();
Thread calcStatistikThread = new Thread(new ParameterizedThreadStart(strat.CalcCoinStatistik));
calcStatistikThread.Start(giveParms);
}
}
}
Internally in the upper initiated Strategies thread, there are stared some additional threads.
Those threads have a "DoneEvent" which is raised at the end of the function.
To notice, that all threads have ended before the main thread ends, I collect all subthreads in a List CalcStatistikMarkersThreads and wait until the list is empty.
The subthreads should remove themselves out of the upper List via the ThreadDone event.
But sometimes the searched thread (CalcStatistikMarkersThreads.Find) is not found anymore and I get a NullReferenceException.
The question is why?!
Could you tell me, why? And possibly how to prevent?
Thanks in advance.
class Strategies
{ public event EventHandler ThreadDone;
private List<Thread> CalcStatistikMarkersThreads;
//do something
public void CalcCoinStatistik(object parameters)
{
StrategyParameter givenParms = (StrategyParameter)parameters;
Pair internalPair = givenParms.pair
//do something
if (CalcStatistikMarkersThreads == null)
{
CalcStatistikMarkersThreads = new List<Thread>();
}
foreach (InternalTodo in InternalToDos)
{
Strategies strat = new Strategies();
CalcStatistikMarkersParameter csp = new CalcStatistikMarkersParameter();
csp.parm1 = param;
strat.ThreadDone += HandleCalcStatistikMarkersDone;
Thread candleCalc = new Thread(new ParameterizedThreadStart(strat.CalcStatistikMarkers));
CalcStatistikMarkersThreads.Add(candleCalc);
candleCalc.Start(csp);
while (CalcStatistikMarkersThreads.Count != 0)
{
Task.Delay(1000).Wait();
}
}
}
public void CalcStatistikMarkers(object parm)
{
//do something
if (ThreadDone != null)
ThreadDone(this, new ThreadInfoEventArgs(Thread.CurrentThread.ManagedThreadId));
}
public void HandleCalcStatistikMarkersDone(object sender, EventArgs e)
{
Guid workGUID = Guid.NewGuid();
ThreadInfoEventArgs tEv = (ThreadInfoEventArgs)e;
Thread currentThread;
try
{
currentThread = CalcStatistikMarkersThreads.Find(xy => xy.ManagedThreadId == tEv.ThreadID);
//HERE THE NullReferenceException is raised sometimes
CalcStatistikMarkersThreads.Remove(currentThread);
}
catch (Exception ex)
{
throw ex;
}
}
public class ThreadInfoEventArgs : EventArgs
{
private int threadID;
public ThreadInfoEventArgs(int trID)
{
this.threadID = trID;
}
public int ThreadID
{
get { return threadID; }
}
}
}
Cheers
Air

Correct Event handling in C#

this is basically a follow up to a previous question (Triggering an event in c# from c++ and declaring LPCWSTR). I've revised my code based on the answers and comments I have received and I solved the initial issue, which was passing the event to the GpioSetupInterruptPin from a gpio api. I don't have a lot of documentation on the api but what i'm trying to achieve is: have a form with a white label; after pressing a switch, the label turns yellow.
The problem i'm having now is the event seems to trigger as soon as it's created (the "execute" message is passed to the debug dialog and the label turns yellow) but it doesn't do anything when i toggle the switch. I was told in the last question to use WaitForSingleObject but i'm not really sure where to call it and this article only added to my confusion.
public partial class Form1 : Form
{
// P/Invoke CreateEvent and WaitForSingleObject
private void GPIO_Open() //get handle for gpio
private void GPIO_Output() //output pin declaration
private void button1_Click(object sender, EventArgs e)
{
Interrupt_Setup();
}
private void Interrupt_Setup()
{
hGPIO = GPIOapi.GpioOpenHandle(); //returns a handle to the gpio
GIPO_ON = true;
Debug.WriteLine("Driver open \n" + hGPIO);
GPIO_Output(); //set output pins
GPIO_Interrupt(Trigger); //configure interrupt
}
private void GPIO_Interrupt(string trigger)
{
bool ok;
_Main();
//INTERRUPT DECALRATION
ok = GPIOapi.GpioSetupInterruptPin(hGPIO, port6, 4, GPIOapi.INT_TRIGGER_MODE.TRIGGER_MODE_EDGE,
GPIOapi.INT_TRIGGER_POLARITY.TRIGGER_POL_HIGH_RISING, trigger, true);
Thread waitThread=new Thread(WaitForTrigger);
waitThread.Start();
if (!ok)
Debug.WriteLine("NO interrupt");
else
Debug.WriteLine("Interrupt set for:" + port6 + "04" + " at " + hGPIO);
}
public static string Trigger = "InputProcessUpdateHandler";
public static IntPtr handle = CreateEvent(IntPtr.Zero, false, false, Trigger); //used P/Invoke
private static InputProcessor inputProcessor = null;
public Color[] color =
{
Color.Orchid, Color.DarkOrchid, Color.GreenYellow, Color.CornflowerBlue, Color.SteelBlue,Color.Crimson
};
public int i = 0;
public void WaitForTrigger()
{
while(true)
{try
{
if (WaitForSingleObject(handle, 0xFFFFFFFF) == false)
{
BeginInvoke(((System.Action)(() =>label2.BackColor = color[i])));
i++;
if (i > 4)
i = 0;
}
Thread.Sleep(300);
}
catch (Exception e)
{ Debug.WriteLine("exception: " + e); }}
}
}
private void _Main()
{
inputProcessor = new InputProcessor();
ShowToggle showToggle = new ShowToggle(inputProcessor);
inputProcessor.Process(label1);
}
public class ShowToggle
{
private InputProcessor _inputProcessor = null;
public ShowToggle(InputProcessor inputProcessor)
{
_inputProcessor = inputProcessor;
_inputProcessor.updateHandledBy += InputProcessUpdateHandler;
}
private void InputProcessUpdateHandler(Label label)
{
label.BackColor = Color.Yellow;
Debug.Write("execute");
}
}
public class InputProcessor
{
public delegate void InputProcessUpdateHandler(Label label);
public event InputProcessUpdateHandler updateHandledBy = null;
public void Process(Label label)
{
if (updateHandledBy != null)
updateHandledBy(label);
}
}
If anyone could help me with this, I would be very grateful.
*** I got it working but it looks a right mess. Could anyone help me straighten it out?
You code is really confusing to me. I think what you want is something like this. Bear in mind I'm typing this into the SO text editor, so don't expect it to compile and just work - it's a guide. Consider it a step above pseudocode.
public class DeviceInterrupt
{
IntPtr m_gpio;
string m_eventName;
public event EventHandler OnInterrupt;
public DeviceInterrupt(int port)
{
// get a driver handle
m_gpio = GPIO_Open();
// generate some unique event name
m_eventName = "GPIO_evt_" + port;
// wire up the interrupt
GpioSetupInterruptPin(m_gpio, port, m_eventName, ...);
// start a listener
new Thread(EventListenerProc)
{
IsBackground = true,
Name = "gpio listener"
}
.Start();
}
public void Dispose()
{
// TODO: release the handle
}
private void EventListenerProc()
{
// create the event with the name we sent to the driver
var wh = new WaitHandle(false, m_eventName);
while (true)
{
// wait for it to get set by the driver
if (wh.WaitOne(1000))
{
// we have an interrupt
OnInterrupt.Fire(this, EventArgs.Empty);
}
}
}
}
Usage would then be something like this:
var intr = new DeviceInterrupt(4);
intr.OnInterrupt += MyHandler;
....
void MyHandler(object sender, EventArgs a)
{
Debug.WriteLine("Interrupt occurred!");
}
Note
The Compact Framework doesn't support actual named system events, so the named WaitHandle I use in my code above is not a CF-supplied WaitHandle. Instead I'm using the one from the Smart Device Framework. You could also P/Invoke to CreateEvent and WaitForSingleObject yourself.

C# Cross threading. IRC stream thread to Main UI thread

I've been trying to get this little IRC program working but for some reason I'm having issues with VS and cross threading. I'm not sure if I'm not doing it the proper way or what. Here are the parts causing the issue.
Main Thread:
public partial class MainUI : Form
{
private static IRC irc = new IRC();
public MainUI()
{
InitializeComponent();
}
public static void StartIRC()
{
irc.Start();
}
}
IRC Thread:
class IRC
{
private Thread ircThread;
private bool _running = true;
private NetworkStream stream;
private StreamWriter writer;
private StreamReader reader;
private TcpClient irc;
public IRC(){
ircThread = new Thread(new ThreadStart(Run));
ircThread.IsBackground = true;
}
public void Run(){
while (_running) {
parseInStream(reader.ReadLine());
}
}
public void Start()
{
ircThread.Start();
}
private void parseInStream(String inText)
{
String[] text = inText.Split(' ');
String name;
String message;
if (text[1].Equals("PRIVMSG")) {
name = capsFirstChar(getUser(inText));
message = inText.Substring(inText.IndexOf(":", 1) + 1);
sendToChatBox(capsFirstChar(name) + ": " + message, Color.Black);
}
else if (text[1].Equals("JOIN")) {
name = getUser(inText);
sendToChatBox(capsFirstChar(name) + " has joined the channel.", Color.LimeGreen);
}
else if (text[1].Equals("PART")) {
name = getUser(inText);
sendToChatBox(capsFirstChar(name) + " has left the channel.", Color.Red);
}
}
public void sendToChatBox(String text, Color color)
{
//Trying to send the text to the chatbox on the MainUI
//Works if the MainUI.Designer.cs file has it set to static
if (MainUI.txtMainChat.InvokeRequired) {
MainUI.txtMainChat.Invoke((MethodInvoker)delegate() {
sendToChatBox(text, color);
});
}
else {
MainUI.txtMainChat.SelectionColor = color;
MainUI.txtMainChat.AppendText(text);
}
}
private String getUser(String msg)
{
String[] split = msg.Split('!');
user = split[0].Substring(1);
return capsFirstChar(user);
}
private String capsFirstChar(String text)
{
return char.ToUpper(text[0]) + text.Substring(1).ToLower();
}
}
The only way I am able to get it to work is if I enter the MainUI.Designer.cs file and change the textbox to static and then change everything from this.txtMainChatto MainUI.txtMainChat.
My main problem is that when I make any changes on the visual side all the things labeled static or things named MainUI are deleted. I'm trying to figure out what I need to do to keep this from happening. Am I doing it the right way, or is there a better way? I tried using a background worker but it was using a lot of processing power to work that way for some reason.
I've looked around the web and can't seem to find out how one might relate to my setup. I see people calling a thread from the main thread and then sending things from the main thread to the thread it called but not the other way around. There is nothing else being written to the text box so there won't be an issue with it being used by two threads at the same time.
On my main UI thread I passed in "this" so I could reference the main window from my IRC Class. MainUI.txtMainChat
irc = new IRC(this);
Then in my IRC class
MainUI main;
public IRC(MainUI main){
this.main = main;
ircThread = new Thread(new ThreadStart(Run));
ircThread.IsBackground = true;
}
Then I was able to Change
//MainUI.txtMainChat to
main.txtMainChat
Like Cameron said, Though I know I was told it's not the best approach it gets me started.
Your designer file is rebuilt every time you change your UI in the designer.
You'll need to pass your MainUi to your IRC class, or give it an abstraction of it using an interface (best option).
public interface IMainUI
{
void AddText(string text, Color color);
void UiThread(Action code);
}
public class MainUI : IMainUI
{
// Whatever else
public void AddText(string text, Color color)
{
UiThread( () =>
{
// Same code that was in your Irc.SendToChatBox method.
});
}
public void UiThread(Action code)
{
if (InvokeRequired)
{
BeginInvoke(code);
return;
}
code.Invoke();
}
}
public class IRC
{
IMainUI _mainUi;
//Other properties, and fields
public IRC(IMainUI mainUi)
{
this._mainUi = mainUi;
// Other constructor stuff.
}
// Other logic and methods
}

C# delegate method with return value

I am making a C# program which is runnning 2 threads. The main UI thread, and a seperate network thread i make myself.
I have figured out, that if i need to change something in the UI from the network thread, i will need to call a deligate method, which works just fine:
// Declared as a global variable
public delegate void ListBoxFirst();
//Then call this from the network thread
listBox1.BeginInvoke(new ListBoxFirst(InitListBox));
//Which points to this
public void InitListBox() {
listBox1.SelectedIndex = 0;
}
Now i need to be able to read a UI value (a listbox.selectedindex) from the network thread, but it shows the error "cannot implicitly convert type 'system.iasyncresult' to 'int'" if i try it the same way (of course with "int" instead of "void", and a "int a = " before the listbox1.begininvoke). I have googled a lot but im pretty new to C# so i get really lost.
Any help would be much appriciated
I figured it out:
public int ReadListBoxIndex()
{
int count = 0;
listBox1.Invoke(new MethodInvoker(delegate
{
count = listBox1.SelectedIndex;
}));
return count;
}
Called with a regular
int count = ReadListBoxIndex();
You need to call EndInvoke() to get the result.
Some code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
listBox1.Items.AddRange(new[] { "AS","Ram"});
}
protected override void OnLoad(EventArgs e)
{
listBox1.BeginInvoke(new Action(GetResult));
}
private void GetResult()
{
if (InvokeRequired)
{
Invoke(new Action(GetResult));
}
listBox1.SelectedIndex = 0;
}
}
This works also, where tn is the treenode you want added to cross thread and tnret is the treenode that is returned.
_treeView.Invoke(new Action(() => tnret = tn.Nodes.Add( name, name )));

Async the content in textbox

I want to display the content asynchronously in textbox? Do anyone know the bug of my code listed below? I want to implement the text in textbox will be updated per second with new value? also i want to ask why checking the InvokeRequired each time before calling the invoke method for the controller?
private void Counting(int Num)
{
int i = 0;
string counter = null;
while (i < Num)
{
Thread.Sleep(1000);
counter = string.Format(" {0}", i);
tbxStatus.BeginInvoke(new UpdateStatusDelegate(UpdateStatus), new string[] { counter });
}
}
public void UpdateStatus(string data)
{
tbxStatus.Text += data;
}
public delegate void UpdateStatusDelegate(string data);
public delegate void CountDelegate(int num);
private void btnStart_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new CountDelegate(Counting), new object[] { 5 });
}
else
Counting(5);
}
Thanks
Your InvokeRequired test should be inside the delegate:
public void UpdateStatus(string data)
{
if (this.tbxStatus.InvokeRequired)
{
UpdateStatusDelegate d = new UpdateStatusDelegate(UpdateStatus);
this.Invoke(d, new object[] { data });
}
else
{
this.tbxStatus.Text = data;
}
}
You can also make this a lot easier and cleaner looking if you use the following extension methods:
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
So if I want to call a method that I have defined in my UI thread (like a method defined in your form instance) you can do so with the following code (with no need to create any delegates or anything):
formInstance.SafeInvoke(f => f.myFormMethod("parameter1","parameter2"));
in your case, you could do the following:
formInstance.SafeInvoke(f => f.UpdateStatus(myCounterInt.toString));
or something like that.
I've written about using this on my blog but don't give me the credit as I was writing about this CodeProject article
I have recently come to a similar problem.
I am writing a networked game and I wanted to append text to a text box I have, however I would get an error saying I was trying to change a windows form control from a different thread, thus causing a not safe thread. This is because I am using ASyncCallback on my server side.
I had this previously: (The console method is a simple method that gets a string and appends it to the textbox).
console("message");
And to solve the problem I had to change it to this, solving the problem for now:
this.Invoke((MethodInvoker)delegate { console("message"); });
Hopefully this helps others that come across this problem. It probably isn't the best approach possible but it did the trick for me.
Regards.

Categories

Resources