I want to pass data from a child page of the MainPage in my app. The problem, from what I can tell is that there is a splash loading screen that navigates to the MainPage and I want to write an "If navigated from child page" block to include in the OnNavigatedTo.
Here is my MainPage.xaml.cs code:
protected override void OnNavigatedTo(NavigationEventArgs args)
{
if (base.OnNavigatedFrom(args) = ChildPage)
{
// Code for: If navigated from child page
ReturnData returnData = args.Parameter as ReturnData;
this.myNewString = returnData.myString;
}
base.OnNavigatedTo(args);
}
I found out that you cannot return data the way it is used by passing data to another page for Windows 8 Apps. You cannot return data they way you pass it i.e. this.Frame.Navigate(typeof(MainPage), passData); For the solution, I created a 'Completed' event and and called it from the MainPage C# code behind.
MainPage.xaml.cs:
protected override void OnNavigatedFrom(NavigationEventArgs args)
{
if (args.SourcePageType.Equals(typeof(ChildPage)))
(args.Content as ChildPage).Completed += OnChildPageCompleted;
base.OnNavigatedFrom(args);
}
void OnChildPageCompleted(object sender, ReturnData args)
{
// Code to run if returned back to MainPage from ChildPage
this.myNewString = returnData.myString;
(sender as ChildPage).Completed -= OnChildPageCompleted;
}
ReturnData.cs:
namespace MyTestApp
{
class ReturnData
{
public String myString { set; get; }
}
}
ChildPage.xaml.cs:
public event EventHandler<ReturnData> Completed;
ChildPage.xaml.cs:
protected override void OnNavigatedFrom(NavigationEventArgs args)
{
if (Completed != null)
{
// Create ReturnData object
ReturnData returnData = new ReturnData();
returnData.myString = newStringChanged;
// Fire Completed event
Completed(this, returnData);
}
base.OnNavigatedFrom(args);
}
Related
I'm struggling to pass data between a thread started in a separate class from my main form. I believe (I could be wrong) that I should use an event. The problem I have is my subscribers are always null as I call the BluetoothScan class and start the thread before the event is subscribed to:
BluetoothScan bluetoothScan = new BluetoothScan(this);
bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;
How do I subscribe to the event before starting the thread?
I have my Main Form:
using System;
using System.Windows.Forms;
//https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0#System_Windows_Forms_Control_Invoke_System_Delegate_System_Object___
namespace YieldMonitor
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
}
private void BtnConnectBT_Click(object sender, EventArgs e)
{
//Start looking for the yield monitor device.
BluetoothScan bluetoothScan = new BluetoothScan(this);
bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;
}
static void OnBluetoothDeviceDiscovered(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Message recieved from event");
}
}
}
My class that looks for bluetooth devices and if the right one is found should fire the event:
using InTheHand.Net.Sockets;
using System;
using System.Linq;
namespace YieldMonitor
{
class BluetoothScan
{
public event EventHandler BluetoothDeviceDiscovered;
public BluetoothScan(MainForm mainForm)
{
System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
Run();
}
public void Run()
{
System.Diagnostics.Debug.WriteLine("Running BluetoothScan Class");
string myDeviceName;
ulong myDeviceAddress;
BluetoothClient btClient = new BluetoothClient();
BluetoothDeviceInfo[] btDevices = btClient.DiscoverDevices().ToArray();
foreach (BluetoothDeviceInfo d in btDevices)
{
System.Diagnostics.Debug.WriteLine(d.DeviceName);
System.Diagnostics.Debug.WriteLine(d.DeviceAddress);
//have we found the device we are looking for?
if (d.DeviceName == "DSD TECH HC-05")
{
myDeviceName = d.DeviceName;
myDeviceAddress = d.DeviceAddress;
//Send out found adapter to the next stage
OnBluetoothScanned(EventArgs.Empty);
break;
}
}
}
protected virtual void OnBluetoothScanned(EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Running OnBlueToothScanned");
EventHandler handler = BluetoothDeviceDiscovered;
if (handler != null)// we have a subscriber to our event
{
System.Diagnostics.Debug.WriteLine("BluetoothScanned is Not empty");
handler(this, e);
}
else
{
System.Diagnostics.Debug.WriteLine("BluetoothScanned is Empty");
}
}
}
}
EDIT
I've found some nice solutions using Tasks where I need to update a label once a task is completed ie.
bool myDevicePaired = false;
var eventDevicePaired = new Progress<bool>(boDevicePaired => myDevicePaired = boDevicePaired);
await Task.Factory.StartNew(() => BluetoothPair.Run(myDeviceAddress, eventDevicePaired), TaskCreationOptions.LongRunning);
//Register the device is paired with the UI
if (myDevicePaired)
{
BtnConnectBT.Text = "Disconnect?";
}
Which is working well for Tasks that have an end that I am waiting for example waiting for a bluetooth device to connect.
But I'm beginning to pull my hair out with System.InvalidOperationException: 'Cross-thread operation not valid: Control 'tbInfo' accessed from a thread other than the thread it was created on.' error when trying to update a form text box.
Example:
in my MainForm Class:
I create what I've called an Event Reciever...
private void BluetoothSocketEventReciever(object sender, EventArgs e)
{
Debug.WriteLine("Event!!!"); //writes data to debug fine
tbInfo.AppendText("Event!!!!"); //causing error
}
I create a task to read from the device...
private void ReadDataFromDevice(UInt64 myDeviceAddress)
{
BluetoothSocket bluetoothSocket = new BluetoothSocket(myDeviceAddress);
bluetoothSocket.BluetoothDataRecieved += BluetoothSocketEventReciever;
Task.Factory.StartNew(() => bluetoothSocket.Run(), TaskCreationOptions.LongRunning);
}
In my BluetoothSocket class I have an endless while loop which will be reading data from a socket (hopefully) At the moment its just creating an empty EventArgs to trigger the Event every second:
namespace YieldMonitor
{
class BluetoothSocket
{
ulong myDeviceAddress;
public event EventHandler BluetoothDataRecieved;
public BluetoothSocket (ulong deviceAddress)
{
myDeviceAddress = deviceAddress;
}
public void Run()
{
System.Diagnostics.Debug.WriteLine("Were in BluetoothSocket ... Address: " + myDeviceAddress);
while (true)
{
Thread.Sleep(1000);
Debug.WriteLine("In BluetoothSocket - Address = " + myDeviceAddress);
OnBluetoothDataRecieved(EventArgs.Empty);
}
}
protected virtual void OnBluetoothDataRecieved(EventArgs e)
{
EventHandler handler = BluetoothDataRecieved;
if (handler != null)
{
handler(this, e);
} else
{
//No subscribers
}
}
}
}
I'm sure I'm missing something simple here but how can I pass the data from the endless loop to the text box on the main form?
EDIT
Think I've just sorted it.
private void BluetoothSocketEventReciever(object sender, EventArgs e)
{
Debug.WriteLine("Event!!!");
tbInfo.Invoke((Action)delegate
{
tbInfo.AppendText("Event!!!");
});
//tbInfo.AppendText("Event!!!!");
}
Is this the correct way to do it?
You can Pass the event handler as a parameter on the constructor
public event EventHandler BluetoothDeviceDiscovered;
public BluetoothScan(MainForm mainForm, EventHandler bluetoothDeviceDiscovered)
{
System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
BluetoothDeviceDiscovered += bluetoothDeviceDiscovered
Run();
}
Personally, i'm not so fun of calling method on constructor. It can be source of bugs or performance issues
Constructor
In class-based object-oriented programming, a constructor
(abbreviation: ctor) is a special type of subroutine called to create
an object. It prepares the new object for use, often accepting
arguments that the constructor uses to set required member variables.
You can pass eventhandler as parameter and call Run later
Hi I'm trying to make a single .html page that displays out the readings of my Raspberry Pi in Real-Time.
The Data is being sent over which triggers the Delegate.
However, the function that the Delegate triggers isn't able to interact with any Control in my ASP.Net Page.
Here's how my code looks like:
public partial class newIndex : System.Web.UI.Page
{
DataComms dataComms;
public void commsDataReceive(string strData)
{
// This line is able to successfully print out the strData
System.Diagnostics.Debug.WriteLine(strData);
// This line doesn't throw an error but doesn't work
Label1.Text = strData;
}
private void InitComms()
{
dataComms = new DataComms();
dataComms.dataReceiveEvent += new DataComms.DataReceivedDelegate(commsDataReceive);
}
protected void Page_Load(object sender, EventArgs e)
{
InitComms();
}
}
My theory is that after the Page loads I'm unable to make any changes to it dynamically?? So I can solve this using an UpdatePanel Right?
Well I tried that too and it didn't work also as I had to call UpdatePanel1.Update() which is accessing the control UpdatePanel.
If anyone could help I'll be so thankful!
Server pages do not work in this way. Most likely page is already sent to browser by the time commsDataReceive is fired. I guess you have only one "client" to show this information so you can make dataComms as static and update code to follow. This is certainly not the best solution but should work for your requirement
public partial class newIndex : System.Web.UI.Page
{
static DataComms dataComms;
static string lastData="";
public void commsDataReceive(string strData)
{
// This line is able to successfully print out the strData
System.Diagnostics.Debug.WriteLine(strData);
// This line doesn't throw an error but doesn't work
lastData = strData;
}
protected void Page_Load(object sender, EventArgs e)
{
if (dataComms != null)
{
dataComms = new DataComms();
dataComms.dataReceiveEvent += new DataComms.DataReceivedDelegate(commsDataReceive);
}
Label1.Text = lastData;
}
}
Assume that i'm in the Page_1 while click the button have to navigate to Page_2.In Page_2 Api call has to done.
MyIssue is when i'm clicking the Button it doesn't navigate to Page_2 immediately it waits for the API response.
How to Navigate Immediately to Page_2 without waiting for the APi response.
Code:
Page_1.cs
public partial class Page_1 : ContentPage
{
public Page_1()
{
InitializeComponent();
}
private void Btn_click(object sender, EventArgs e)
{
Navigation.PushAsync(new Page_2());
}
}
Page_2:
public Page_2()
{
InitializeComponent();
}
protected override void OnAppearing()
{
HttpClient httpClient = new HttpClient();
var obj = httpClient.GetAsync("//Api//").Result;
if (obj.IsSuccessStatusCode)
{
}
}
Same code works good in iOS as expected
You could load your data in an other Task to prevent blocking the UI.
protected override void OnAppearing()
{
Task.Run( () => LoadData());
base.OnAppearing();
}
private async void LoadData()
{
HttpClient httpClient = new HttpClient();
var obj = await httpClient.GetAsync("//Api//");
if (obj.IsSuccessStatusCode)
{
// If you need to set properties on the view be sure to use MainThread
// otherwise you won't see it on the view.
Device.BeginInvokeOnMainThread(() => Name = "your text";);
}
}
As per your question you are calling the API on Page constructor that's why it's taking time to load web API then navigating on page2. If you want to navigate on page2 before a load the api. Check below code
public partial class Page2 : ContentPage
{
bool IsLoading{ get; set; }
public Page2()
{
InitializeComponent();
IsLoading = false;
}
protected async override void OnAppearing()
{
base.OnAppearing();
if (!IsLoading)
{
IsLoading=true
**Call the Web API Method Here**
}
IsLoading=false
}
}
I'm developing an application with windows-10-t platform on raspberry-pi3. The application has several pages and listens GPIO ports asyncrhonously in the background. It collects data from GPIO and sends to the WCF-Service, after a bit the UI should be updated by the data coming from the WCFService. I've also tried using Tasks, Dispatcher.Invoke etc. but nothing worked properly. I can collect data coming from GPIO but cannot update UI. What am I doing wrong?
Here is the background GPIO listener class with static variables (I'm listening GPIO in other pages too.):
public sealed class GPIO{
private static MainPage mainpage;
public static event EventHandler ProgressUpdate;
public static void InitGPIO(MainPage sender)
{
mainpage = sender;
DataPin.DebounceTimeout = TimeSpan.FromMilliseconds(50);
DataPin.ValueChanged += DataPin_ValueChanged;
}
public static void DataPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
{
if (e.Edge == GpioPinEdge.FallingEdge)
{
Task.Run(() => AddData(0));
}
}
public static async void AddData(int prm_Data)
{
// WCF-Service Operation
await Service.wsClient.GPIOValueAddition(prm_Data);
GPIO.ProgressUpdateOperation();
}
private static void ProgressUpdateOperation()
{
mainpage.GPIO_ProgressUpdate(typeof(GPIO), new EventArgs());
}
}
And here is the page that contains the UI to be updated:
public sealed partial class MainPage : Page
{
public MainPage()
{
GPIO.InitGPIO(this);
GPIO.ProgressUpdate += GPIO_ProgressUpdate;
}
public void GPIO_ProgressUpdate(object sender, EventArgs e)
{
// WCF-Service Operation
service_data = (int)Service.wsClient.GetDataFromServicetoUpdateUI(parameter).Result;
// UI-update
txtUpdate.Text = service_data.ToString();
}
}
EDIT: I forgot to add the exception. "The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))" exception is thrown at AddData function called in DataPin_Valuechanged.
I found the solution in here : https://stackoverflow.com/a/27698035/1093584
Here is the new update-UI function :
public void GPIO_ProgressUpdate(object sender, EventArgs e)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
service_data = await Service.wsClient.GetDataFromServicetoUpdateUI(parameter);
// UI-update
txtUpdate.Text = service_data.ToString();
});
}
In a background agent I create (or update) one of application live tiles and this works as expected.
Problem is that when I click this live tile screen flickers but my app is not "restarted" nor "shown".
What's wrong?
I attach small part of the code, but ask for more is you need.
MAIN PAGE
public partial class MainPage : PhoneApplicationPage
{
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
public MainPage()
{
InitializeComponent();
// Runs background agent: code is simplified
StartAgent();
}
}
BACKGROUND AGENT
public class TileAgent : ScheduledTaskAgent
{
protected override void OnInvoke(ScheduledTask task)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
Vars.UpdateTiles();
});
NotifyComplete();
}
}
STATIC CLASS
public class Vars
{
private static Uri uri = new Uri(
"/MainPage.xaml?tile",
UriKind.RelativeOrAbsolute);
private static RadExtendedTileData ExtendedData
{
get
{
return new RadExtendedTileData()
{
VisualElement = frontTile,
BackVisualElement = backTile,
};
}
}
public static void UpdateTiles()
{
// I perform some task here
// Then I create/update live tile
Telerik.Windows.Controls.LiveTileHelper.CreateOrUpdateTile(
ExtendedData, uri);
}
}
Try /MainPage.xaml?tile=true instead of /MainPage.xaml?tile...
And move NotifyComplete() into the dispatcher call. Otherwise it will be called before the operation has been completed...