Why is UILabel not updating? - xamarin.ios

I have created a view that shows lost connection messages to users which pops over the current view. I want to update the view periodically based on connection status changes.
I can properly get the view and change the text of a label (verified with WriteLines), but nothing changes on the actual display. I even tried removing the view and readding it and calling SetNeedsDisplay, but nothing seems to help.
I have a global variable called OverView:
public static UIView OverView;
I create the label subview, add it to the overview and pop the overview in front of the current view:
UILabel labelTitle = new UILabel();
labelTitle.Text = title;
UIView labelTitleView = (UIView) labelTitle;
labelTitleView.Tag = 5000;
OverView.AddSubview(labelTitleView);
curView.InsertSubviewAbove(OverView, curView);
curView.BringSubviewToFront(OverView);
And then at a later time, I try to modify it like this from another function:
if ((OverView != null) && (OverView.Subviews != null))
{
for (int i = 0; i < OverView.Subviews.Length; i++)
{
WriteToConsole("Type: " + OverView.Subviews[i].GetType());
if (OverView.Subviews[i] is UILabel)
{
WriteToConsole("Found Label with Tag: " + ((UILabel)(OverView.Subviews[i])).Tag + " Text: " + ((UILabel)(OverView.Subviews[i])).Text);
if (((UILabel)(OverView.Subviews[i])).Tag == 5000)
{
WriteToConsole("Setting subview Title to: " + lostConnectionTitle);
lock (overViewLocker)
{
appReference.InvokeOnMainThread(delegate
{
UILabel tempLabel = ((UILabel)(OverView.Subviews[i]));
tempLabel.Text = lostConnectionTitle;
OverView.Subviews[i].RemoveFromSuperview();
OverView.AddSubview(tempLabel);
OverView.BringSubviewToFront(tempLabel);
OverView.SetNeedsLayout();
OverView.SetNeedsDisplay();
WriteToConsole("SetNeedsDisplay");
});
}
}
}
}
}

Have you tried to use delegate methods on your label, and change their value when events occur ?
For example, if your event is clicking on a button, you should have something like that:
yourLabel.Text = "Init";
buttonExample.TouchUpInside += (sender, e) => {
yourLabel.Text = "I touched my button";
};
When your View loads, you'll see "Init" and your button and once you click on it, the label text changed.
Xamarin has some explanation about events and delegate methods here.
I hope that helped.

Related

UI thread slow to respond to Progress updaters on async Task method using VS2022 & Net6.0

I’ve run into a performance obstacle and I’m uncertain of the cause, all of this is running under VS2022 & Net6.0. As this is my 1st time using this combination of a modal windows form, and progress bar, with the work running on a background thread and two Progress objects updating the UI, the progress bar, and a text label, I don’t know where to attack the problem. Prior to placing the workload on a background thread, everything was snappy, searching a thousand files with about 600 lines of text in each, in about a minute. Naturally, the windows form was frozen during this, which is why the workload was placed on a background thread.
After doing so, the workload will be 25-50% complete before the UI starts displaying the values from the Progress objects, and overall, the entire process now takes 10x as long to complete. Progress objects aren’t skipping over any values sent to them, the UI thread just seems slow in getting the information. Likewise, if I try to drag the modal form to a new spot on the desktop it’s unresponsive for 20—30 seconds before it finally moves. One more thing, I can step through the code on the background thread and see it calling the Progress updaters, but the UI thread is just very slow in responding to them.
I could use some suggestions on how to uncover the problem or if clearly evident, point out where the likely problem could be. Here are the essential controls and methods used.
public class SearchProgressForm : Form
{
private System.Windows.Forms.Button btnSearch = new Button();
private System.Windows.Forms.TextBox txtTextSearch = new TextBox();
private System.Windows.Forms.Label lblSearchFile = new Label();
private System.Windows.Forms.ProgressBar SearchProgressBar = new ProgressBar();
public event LogSearchEventHandler SearchSucceededEvent;
protected void OnSearchSucceeded(LogSearchEventArguments p_eventArguments)
{
LogSearchEventHandler handler = SearchSucceededEvent;
if (handler != null)
{
handler(this, p_eventArguments);
}
}
private void InitializeComponent()
{
this.btnSearch.Name = "btnSearch";
this.btnSearch.Text = "Search";
this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click);
this.lblSearchFile.Text = "Searching File: ";
this.txtTextSearch.Text = "search string";
}
public SearchProgressForm() { }
private void btnSearch_Click(object sender, EventArgs e)
{
this.SearchByText(this.txtTextSearch.Text);
}
private void SearchByText(string p_searchParameter)
{
// Setup a progress report for thr ProgressBar
var _progressBarUpdate = new Progress<int>(value =>
{
this.SearchProgressBar.Value = value;
this.SearchProgressBar.Refresh();
});
var _progressFileNameUpdate = new Progress<string>(value =>
{
this.lblSearchFile.Text = "Searching File For : " + value;
this.lblSearchFile.Refresh();
});
// Start search on a backgroud thread and report progress as it occurs
Task.Run(async () => await this.SearchByStringAsync(p_searchParameter, _progressBarUpdate, _progressFileNameUpdate));
}
private async Task SearchByStringAsync(string p_searchParameter, IProgress<int> p_progressBar, IProgress<string> p_progressFileName)
{
await Task.Delay(1);
TextFileReader textFileReader = new TextFileReader();
LogSearchEventArguments logSearchEventArguments = null;
long _sessionloopCount = 0;
long _totalTextLinesCount = this.GetTotalSearchCount(p_searchParameter, SearchType.TextString);
// Get file names from SQL table
var _logFiles = DataOperations.LogFileSortableList(null);
foreach (var log in _logFiles)
{
// Format a file name to be read from the file system
string _fileName = log.Directory + "\\" + log.FileName;
p_progressFileName.Report(log.FileName);
// If we've raised an event for this file, then stop iterating over remaning text
if (logSearchEventArguments != null)
{
logSearchEventArguments = null;
break;
}
// Read in file contents from file system
List<string> _fileContents = textFileReader.ReadAndReturnStringList(_fileName);
long _fileTotalRecordCount = _fileContents.Count;
long _fileRecordCount = 0;
foreach (var _line in _fileContents)
{
if (_line.ToUpper().Contains(p_searchParameter.ToUpper()))
{
// Raise an event so search parameter and file name can be captured in another form
logSearchEventArguments =
new LogSearchEventArguments
(
"TextSearch", p_searchParameter, SearchType.TextString, true, log,
new DateTime(
Convert.ToInt32("20" + log.FileName.Substring(14, 2)),
Convert.ToInt32(log.FileName.Substring(16, 2)),
Convert.ToInt32(log.FileName.Substring(18, 2)))
);
// We found a match, so no further searching is needed in this log file,
// and it's been flagged in the DB, so raise the event to save search parameter and file name
// then break out of this loop to get the next file to search in.
this.OnSearchSucceeded(logSearchEventArguments);
break;
}
// These calcs are based on actual searches performed
_fileRecordCount++;
_sessionloopCount++;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
// Because we exit a search as soon as the 1st match is made, need to resynch all counts
// and update the progress bar accordingly
if (_fileRecordCount < _fileTotalRecordCount)
{
long _countDifference = _fileTotalRecordCount - _fileRecordCount;
// Add count difference to sessionLoopCount and update progress bar
_sessionloopCount += _countDifference;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
}
//Search is complete set Progress to 100% and report before exiting
p_progressBar.Report(100);
// Close the modal SearchForm and exit
this.Close();
}
}
I solved this problem but I'm still not certain of what caused it. I eliminated the method "private void SearchByText(string p_searchParameter)" and moved the code there into the btnSearch_Click event handler so I could call my background worker "SearchByStringAsync" directly from the button click event handler.
I also updated the EFCore NuGet Packages, which were version Net6.0 to version 6.0.4, because of single line of code in my Async background method, "var _logFiles = DataOperations.LogFileSortableList(null)".
That call returned a Sortable BindingList, using BindingList <T>. Between the NuGet updates and a minor change on a custom comparer method in my BindingList <T> class, the windows modal form now updates the ProgressBar and Label text as expected, and the form now responds immediately to user interaction.

Unity Vuforia Google VR - Can't make onPointerEnter to GameObject change material for itself

I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.

Got a SIGSEGV while setting the Text of my TextField from a ABPeoplePickerModule

In my App the User needs to type in some Address Information.
I provide an Addressbook Button for that since Version 1.0.
That worked fine in my previous tests, but now I get a SIGSEGV when I try to set my UITextField.Text property. This error occurs everytime when I try to Pick an Address from the PeoplePicker in MonoTouches Debug mode, when I try that on my iPhone the eror occurs randomly between setting the TextFields and pushing the next ViewController.
Is There anything wrong in my Code or is it a bug?
//AB Button Event
addressBook.TouchUpInside += delegate {
//Create new Picker
ABPeoplePickerNavigationController PP = new ABPeoplePickerNavigationController();
//Cancel Event
PP.Cancelled += delegate {
NavigationController.DismissViewController(true, null);
};
//Select Event
PP.SelectPerson += delegate(object sender, ABPeoplePickerSelectPersonEventArgs e) {
//Detailed Person View
ABPersonViewController pv = new ABPersonViewController();
//Action when clicked on a Property
pv.PerformDefaultAction += delegate(object person, ABPersonViewPerformDefaultActionEventArgs ev) {
//If the Property was an Address
if(ev.Property.ToString() == "Address"){
string lands = "", orts = "", plzs = "";
try{
//Read the Detailed Address Data from the clicked property
ABMultiValue<PersonAddress> addresses = ev.Person.GetAllAddresses();
PersonAddress[] values = addresses.GetValues();
lands = values[ev.Identifier.Value].Country.ToString();
orts = values[ev.Identifier.Value].City.ToString();
plzs = values[ev.Identifier.Value].Zip.ToString();
}catch{
Console.WriteLine("Fehlerhafte Addresse");
}
//Set the Textfield with the new information
//iPhone Simulator in Debugmode crashes here everytime
land.Text = lands;
ort.Text = orts;
plz.Text = plzs;
//Close the Module
NavigationController.DismissViewController(true, null);
}
};
//Set selected Person for the person View
pv.DisplayedPerson = e.Person;
//Open Person View with navigation from the PeoplePicker Controller
PP.PushViewController(pv, true);
};
//Open PeoplePicker Controller Module
NavigationController.PresentViewController(PP, true, null);
};
Thanks in advance!
The just released MonoTouch 6.0.5 has a fix for a similar issue.

MonoTouch Dialog elements are not updating/repainting themselves

I have the following in a Section:
_favElement = new StyledStringElement (string.Empty);
_favElement.Alignment = UITextAlignment.Center;
if (_room.IsFavourite) {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
} else {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
}
_favElement.Tapped += favElement_Tapped;
Then when I press the element I want the following to happen:
private void favElement_Tapped ()
{
if (_room.IsFavourite) {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
} else {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
}
_room.IsFavourite = !_room.IsFavourite;
}
However the image and text does not change in the actual element when the element is tapped. Is there a refresh method or something that must be called? I've also tried changing the Accessory on Tapped as well and nothing changes. The properties behind do reflect the correct values though.
An alternative to reloading the UITableView is to reload the Element using code like this (copied from Touch.Unit):
if (GetContainerTableView () != null) {
var root = GetImmediateRootElement ();
root.Reload (this, UITableViewRowAnimation.Fade);
}
assuming that your code is in DialogViewController,add this
this.ReloadData();
but in your case I recommend you to use BooleanImageElement

Activating views in regions in Prism

I have problem that I don't seem to be able to solve. I have a created a test project, using MEF and Prism4. I've created a test project where I have 2 views and each of them register themselves inside a region, and also a button in another region. When the button is clicked, I want the view of change to the correct view. The code I think is wrong is below, anyone have any ideas what I am doing wrong here ?
public void Initialize()
{
regionManager.RegisterViewWithRegion(RegionNames.MainRegion, typeof(Views.Module1View));
Button button = new Button() { Content = "Module1" };
button.Click += (o, i) =>
{
var region = this.regionManager.Regions[RegionNames.MainRegion];
if (region != null)
{
region.Activate(typeof(Views.Module1View));
}
};
regionManager.AddToRegion(RegionNames.NavigationRegion, button);
}
I get the following error ...
The region does not contain the specified view.
Parameter name: view
Solved it - amazing what a good nights sleep will do! I had to get the view from the ServiceLocator.
public void Initialize()
{
regionManager.RegisterViewWithRegion(RegionNames.MainRegion, () =>
ServiceLocator.Current.GetInstance<Views.Module2View>());
Button button = new Button() { Content = "Module2" };
button.Click += (o, i) =>
{
var view = ServiceLocator.Current.GetInstance<Views.Module2View>();
var region = this.regionManager.Regions[RegionNames.MainRegion];
if (region != null)
{
region.Activate(view);
}
};
regionManager.AddToRegion(RegionNames.NavigationRegion, button);
}

Resources