Xamarin Forms - WCF completed event not changing components - c#

Currently i'm working on a Xamarin.Forms application with WCF. The app makes a connection to the WCF host and I get a response back, only I can't do anything with the results of the response.
My code of the method that's supposed to take care of the response is:
private static void ClientOnGetHelloDataCompleted(object sender, GetHelloDataCompletedEventsArgs getHelloDataCompletedEventArgs)
{
string msg = null;
if(getHelloDataCompletedEventArgs.Error != null)
{
msg = getHelloDataCompletedEventArgs.Error.Message;
}
else if(getHelloDataCompletedEventArgs.Cancelled != null)
{
msg = "Request was cancelled";
}
else
{
lblText.Text = getHelloDataCompletedEventArgs.Results.Name;
}
}
When I debug I can see Results.Name is filled, but for some reason it doesn't update the label named lblText.
This method is placed in de App.cs (Xamarin Forms portable project).
Anyone here that can help me with this problem?

you should refresh the U.I in the main thread, here is the fix
else
{
InvokeOnMainThread(() => lblText.Text = getHelloDataCompletedEventArgs.Results.Name);
}

Related

What is the best way to check a url is valid every second (or less), with Task await or ContinueWith in a C# Window Forms Application (.NET)

I'm new to C# .Net and Visual Studio 2022 - What I'm trying to achieve is to have a timer running every second to check that a website url is valid/is up. If the url IS reachable and the current WebView2 is not showing that website, then it should navigate to it. If it's already showing that website, it should do nothing else. If it was showing that website, but now it's no longer valid, the WebView should navigate to my custom error page. If whilst on the custom error page the website becomes available again, it should (re)load the website.
In my particular scenario I'm making a webView load localhost (127.0.0.1) for now. I want to continuously check the website is ip, and if it goes down, show custom error, if it comes back, show the website.
Not sure I'm explaining that very well. From the research I have done, I believe I need Task and also await using async method.
Here's my current timer and checkurl code as well as navigtionstarted and navigationcompeted:
private void webView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
timerCheckRSLCDURL.Enabled = false;
}
private void webView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
if (e.IsSuccess)
{
Debug.WriteLine("JT:IsSuccess");
((Microsoft.Web.WebView2.WinForms.WebView2) sender).ExecuteScriptAsync("document.querySelector('body').style.overflow='hidden'");
}
else if (!e.IsSuccess)
{
Debug.WriteLine("JT:IsNOTSuccess");
webView.DefaultBackgroundColor = Color.Blue;
//webView.CoreWebView2.NavigateToString(Program.htmlString);
}
timerCheckRSLCDURL.Enabled = true;
}
private void timerCheckRSLCDURL_Tick(object sender, EventArgs e)
{
Debug.WriteLine("Timer Fired! Timer.Enabled = " + timerCheckRSLCDURL.Enabled);
CheckURL(Properties.Settings.Default.URL, Properties.Settings.Default.Port);
}
private async void CheckURL(string url, decimal port)
{
timerCheckRSLCDURL = false;
Program.isWebSiteUp = false;
string webViewURL = BuildURL();
Debug.WriteLine("Checking URL: " + webViewURL);
try
{
var request = WebRequest.Create(webViewURL);
request.Method = "HEAD";
var response = (HttpWebResponse) await Task.Factory.FromAsync < WebResponse > (request.BeginGetResponse, request.EndGetResponse, null);
if (response.StatusCode == HttpStatusCode.OK)
{
Program.isWebSiteUp = true;
}
}
catch (System.Net.WebException exception)
{
Debug.WriteLine("WebException: " + exception.Message);
if (exception.Message.Contains("(401) Unauthorized"))
{
Program.isWebSiteUp = false;
}
else
{
Program.isWebSiteUp = false;
} // This little block is unfinished atm as it doesn't really affect me right now
}
catch (Exception exception)
{
Debug.WriteLine("Exception: " + exception.Message);
Program.isWebSiteUp = false;
}
if (Program.isWebSiteUp == true && webView.Source.ToString().Equals("about:blank"))
{
Debug.WriteLine("JT:1");
Debug.WriteLine("isWebSiteUp = true, webView.Source = about:blank");
webView.CoreWebView2.Navigate(webViewURL);
}
else if (Program.isWebSiteUp == true && !webView.Source.ToString().Equals(webViewURL))
{
Debug.WriteLine("JT:2");
Debug.WriteLine("isWebSiteUp = true\nwebView.Source = " + webView.Source.ToString() + "\nwebViewURL = " + webViewURL + "\nWebView Source == webViewURL: " + webView.Source.ToString().Equals(webViewURL) + "\n");
webView.CoreWebView2.Navigate(webViewURL);
}
else if (Program.isWebSiteUp == false && !webView.Source.ToString().Equals("about:blank"))
{
Debug.WriteLine("JT:3");
Debug.WriteLine("This SHOULD be reloading the BSOD page!");
webView.CoreWebView2.NavigateToString(Program.htmlString);
}
}
private string BuildURL()
{
string webViewURL;
string stringURL = Properties.Settings.Default.URL;
string stringPort = Properties.Settings.Default.Port.ToString();
string stringURLPORT = $ "{stringURL}:{stringPort}";
if (stringPort.Equals("80"))
{
webViewURL = stringURL;
}
else
{
webViewURL = stringURLPORT;
}
if (!webViewURL.EndsWith("/"))
{
webViewURL += "/";
}
//For now, the URL will always be at root, so don't need to worry about accidentally
//making an invalid url like http://example.com/subfolder/:port
//although potentially will need to address this at a later stage
Debug.WriteLine("BuildURL returns: " + webViewURL);
return webViewURL;
}
So the timer is fired every 1000ms (1 second) because I need to actively check the URL is still alive. I think the way I'm controlling the timer is wrong - and I imagine there's a better way of doing it, but what I want to do is this...
Check website URL every 1 second
To avoid repeating the same async task, I'm trying to disable the timer so it does not fire a second time whilst the async checkurl is running
Once the async/await task of checking the url has finished, the timer should be re-enabled to continue monitoring is the website url is still up
If the website is down, it should show my custom error page (referred to as BSOD) which is some super basic html loaded from resources and 'stored' in Program.htmlString
if the the website is down, and the webview is already showing the BSOD, the webview should do nothing. The timer should continue to monitor the URL.
if the website is up and the webview is showing the BSOD, then it should navigate to the checked url that is up. If the website is up, and the webview is already showing the website, then the webview should do nothing. The timer should continue to monitor the URL.
From other research, I'm aware I shouldn't be using private async void - eg shouldn't be using it as a void. But I've not yet figured out / understood the correct way to do this
In the Immediate Window, it appears that webView_NavigationCompleted is being fired twice (or sometimes even a few times) instantly as the immediate window output will show JT:IsSuccess or JT:IsNOTSuccess a few times repeated in quick succession. Is that normal? I'm assuming something isn't correct there.
The main problem appears to be due to the timer being only 1 second. If I change the timer to fire every 30 seconds for example, it seems to work ok, but when it's every second (I may even need it less than that at some point) it's not really working as expected. Sometimes the BSOD doesn't load at all for example, as well as the webView_NavigationCompleted being fire multiple times in quick succession etc.
Could someone pretty please help me make this code better and correct.
I've searched countless websites etc and whilst there is some good info, some of it seems overwhelming / too technical so to speak. I had to lookup what "antecedent" meant earlier as it's a completely new word to me! :facepalm:
Many thanks inadvance
This answer will focus on the Task timer loop to answer the specific part of your question "check a url is valid every second". There are lots of answers about how to perform the actual Ping (like How do you check if a website is online in C#) and here's the Microsoft documentation for Ping if you choose to go that route.
Since it's not uncommon to set a timeout value of 120 seconds for a ping request, it calls into question whether it would have any value to do this on a steady tick of one second. My suggestion is that it would make more sense to:
Make a background thread
Perform a synchronous ping (wait for the result) on the background thread.
Marshal the ping result onto the UI thread to perform the other tasks you have laid out.
Synchronously wait a Task.Delay on the background thread before performing the next ping.
Here is how I personally go about doing that in my own production code:
void execPing()
{
Task.Run(() =>
{
while (!DisposePing.IsCancellationRequested)
{
var pingSender = new Ping();
var pingOptions = new PingOptions
{
DontFragment = true,
};
// https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping?view=net-6.0#examples
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
try
{
// https://stackoverflow.com/a/25654227/5438626
if (Uri.TryCreate(textBoxUri.Text, UriKind.Absolute, out Uri? uri)
&& (uri.Scheme == Uri.UriSchemeHttp ||
uri.Scheme == Uri.UriSchemeHttps))
{
PingReply reply = pingSender.Send(
uri.Host,
timeout, buffer,
pingOptions);
switch (reply.Status)
{
case IPStatus.Success:
Invoke(() => onPingSuccess());
break;
default:
Invoke(() => onPingFailed(reply.Status));
break;
}
}
else
{
Invoke(() => labelStatus.Text =
$"{DateTime.Now}: Invalid URI: try 'http://");
}
}
catch (Exception ex)
{
// https://stackoverflow.com/a/60827505/5438626
if (ex.InnerException == null)
{
Invoke(() => labelStatus.Text = ex.Message);
}
else
{
Invoke(() => labelStatus.Text = ex.InnerException.Message);
}
}
Task.Delay(1000).Wait();
}
});
}
What works for me is initializing it when the main window handle is created:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!(DesignMode || _isHandleInitialized))
{
_isHandleInitialized = true;
execPing();
}
}
bool _isHandleInitialized = false;
Where:
private void onPingSuccess()
{
labelStatus.Text = $"{DateTime.Now}: {IPStatus.Success}";
// Up to you what you do here
}
private void onPingFailed(IPStatus status)
{
labelStatus.Text = $"{DateTime.Now}: {status}";
// Up to you what you do here
}
public CancellationTokenSource DisposePing { get; } = new CancellationTokenSource();
Example 404:

gRPC with WPF not working

I am trying to get the gRPC C# example working inside WPF.
The same code which is working inside a Console Application is not working. What am I missing.
The minimal class which works in the Console App and does not work in WPF looks like this:
public class GrpcClientImpl
{
private GrpcService.GrpcService.GrpcServiceClient client;
public GrpcTestClientImpl()
{
var channel = new Channel("127.0.0.1:6980", ChannelCredentials.Insecure);
client = new GrpcService.GrpcService.GrpcServiceClient(channel);
ProcessFeed().Wait();
}
public async Task ProcessFeed()
{
try
{
using (var call = client.Feed(new FeedRequest()))
{
var responseStream = call.ResponseStream;
while (await responseStream.MoveNext())
{
var result = responseStream.Current;
Console.WriteLine("received result");
}
}
}
catch (RpcException e)
{
Console.WriteLine("RPC failed " + e);
throw;
}
}
}
The responseStream.MoveNext() is where it is hanging. It does not respond to sent items and it does also not trigger an exception if the gRPC server is not there. What have I missed?
The problem is the blocking call ProcessFeed().Wait(); within the constructor.
This post explains why:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
To solve the issue, call await ProcessFeed(); from outside (not in the costructor).

Azure Service Bus 3.2.2 BeginReceive()

I need to upgrade our service bus nuget package to 3.2.2 (think the evenprocessor host requires it) but I have always kept our service bus project lib at 2.8.2. This is mainly due to the fact that BeginReceive() and EndReceive() looks to have been removed. Is there any reason or anyway I can easily convert this
public void StartReceiving(RecieverCallback callback, TimeSpan waittime, object state = null)
{
this._recieverCallback = callback;
_queueClient = this.MessagingFactory.CreateQueueClient(QueueName, ReceiveMode);
// make initial async call
_asyncresult = _queueClient.BeginReceive(waittime, ReceiveDone, _queueClient);
}
public void ReceiveDone(IAsyncResult result)
{
if (result != null)
{
try
{
var tmpClient = result.AsyncState as QueueClient;
var brokeredMessage = tmpClient.EndReceive(result);
if (brokeredMessage != null)
{
if (ReceiveMode == ReceiveMode.PeekLock)
{
brokeredMessage.Complete();
}
var tmpMessage = brokeredMessage.GetBody<T>();
ProcessMessageProperties(tmpMessage, brokeredMessage);
_recieverCallback(tmpMessage);
}
}
catch (Exception ex)
{
_logger.Fatal("ReceiveDone: {0}", ex.Message);
Console.WriteLine(ex.Message);
}
}
// do recieve for next message
_asyncresult = _queueClient.BeginReceive(ReceiveDone, _queueClient);
}
Image showing the error
Following image shows what happens if I upgrade servicebus to 3.2.2 which I believe will solve the original error (program running 3.2.2, lib project running 2.8.x)
I figured it out see link
https://gist.github.com/sitereactor/8953583
If anyone has a similar issue, let me know and will post my code but its 95% the same as per the link.

How come JsonConvert.DeserializeObject in WP8 app works, ni WP8 ScheduledTask doesn't

I got a REST service running in AWS which returns some information. When I access the service from within my app, all works well. When I use the exact same code in a scheduledtask, it doesn't.
The weird thing is, there is no error, no exception, no hint as to what is going on other than that when stepping through the code in Visual Studio, when hitting F10 on the line where JsonConvert.DeserializeObject is called it doesn't step over the line executing it, but it is the same as hitting F5.
My code is:
private void infoRetrieval_DownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
string response = e.Result;
if (response != null)
{
try
{
CDateTime info = null;
CJsonDateTime r = JsonConvert.DeserializeObject<CJsonDateTime>(response);
info = new CDateTime(r);
if (info != null)
{
// Indicate that there is a new info
}
}
catch (Exception exc)
{
// The exception can't be handled in a meaningful way, so it's ignored.
}
}
}
}
The code that does the actual call to the webservice looks like this, and it is also the exact same code I use both in my App and in my agent as part of the OnInvoke method:
infoRetrievalClient = new WebClient();
infoRetrievalClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(infoRetrieval_DownloadCompleted);
infoRetrievalClient.DownloadStringAsync(new Uri(<the URI to the webservice>));
Note that I am using the exact same code in my App as well as my ScheduledTaskAgent. Furthermore, I checked and the string retrieved in both cases is exact the same. Problem here is just that I can't parse the JSON and use the data.
The call to NotifyComplete is in the OnInvoke.
Any help is highly appreciated as I'm currently stuck.
Iwan
I fixed it, it turned out that Igor's question put me on the right track. I moved the NotifyComplete to the callback of the web-request and now it is all processing nicely.
Thanks Igor.
Iwan

NullReferenceException when trying to send bytes via Bluetooth in C#

This is first time I am doing with Bluetooth connection in C#. I am trying to send byte (data type) from application written in C# over Bluetooth.
This is my code:
public class ConnectionManager
{
private StreamSocket socket;
private DataWriter dataWriter;
public void Initialize()
{
socket = new StreamSocket();
}
public void Terminate()
{
if (socket != null)
{
socket.Dispose();
}
}
public async void connect(HostName hostName)
{
if (socket != null)
{
await socket.ConnectAsync(hostName, "1");
dataWriter = new DataWriter(socket.OutputStream);
}
}
//sending data via Bluetooth
public void sendCommand(byte command)
{
dataWriter.WriteByte(command);
}
}
private ConnectionManager connectionManager;
// Constructor
public MainPage()
{
InitializeComponent();
connectionManager = new ConnectionManager();
}
private async void AppToDevice()
{
PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
var pairedDevices = await PeerFinder.FindAllPeersAsync();
if (pairedDevices.Count == 0)
{
Debug.WriteLine("No devices found.");
}
else
{
foreach (var pairedDevice in pairedDevices)
{
if (pairedDevice.DisplayName == "HC-06")
{
connectionManager.connect(pairedDevice.HostName);
continue;
}
}
}
}
private void send_Click(object sender, RoutedEventArgs e)
{
byte command = Convert.ToByte(commandTextBox.Text);
connectionManager.sendCommand(command);
}
private void connect_Click(object sender, RoutedEventArgs e)
{
AppToDevice();
}
When I enter some value (for example 1 or 2) in commandTextBox and tap on Send button application crashes. This is the error message: An exception of type 'System.NullReferenceException' occurred in TestBluetooth.DLL but was not handled in user code
Can someone help me?
Sounds like a very basic error. Some object is null. It will happen when you try to access a method or a property on an object that is null.
Troubleshooting:
Check the stacktrace of the error. It says where it fails.
Turn on break on all exceptions when you debug so you can see where exactly it fails, and see which object is null.
If you can not debug it, add a try/catch around all the code in every method you have and use a MessageBox.Show(ex.ToString()) to display the full error with stacktrace when it fails.
If the error occurs in a third party dll, make sure to turn off Debugging->"Enable Just My Code" in Options in Visual Studio.
Download and install Red Gate's Reflector if it fails outside your code and you dont have the source for it. Reflector will "extract" out the code of the DLL and show you exactly the line it fails (or so I believe it does).
When you get an error saying "was not handled in user code" it means an Exception was thrown and not handled by a try/catch by you. I have a finger on the async method you have there.
If I ever play around with threads, i ALWAYS have a try/catch around all my code within the threaded code. I recommend adding a try/catch in the AppToDevice() method. Also add it for all your Form events. Show a message of error to the user or handle it and hide the error from the user.
Check these:
Best Practice for Exception Handling in a Windows Forms Application?
Exception handling in threads

Categories

Resources