Xamarin form :How to search in listview - c#

I have listview containing data from web API. I want to search in the listview with character wise. The problem I am facing is when I start searching, it works fine but it gets very very slow. I need some solution to fix it. Here is my code:
private async void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(" http://172.16.4.212:51583/api/GetItems");
var admtPatients = JsonConvert.DeserializeObject<List<tblItem>>(json);
ObservableCollection<tblItem> trends = new ObservableCollection<tblItem>(admtPatients);
if (string.IsNullOrEmpty(medicine.Text))
{
MyListView.ItemsSource = trends;
}
else
{
MyListView.ItemsSource = trends
.Where(x =>
x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant()) ||
x.strItemName.ToUpperInvariant().Contains(e.NewTextValue.ToUpperInvariant()));
}
//await ((MainViewModel)this.BindingContext).LoadCountNotificationAsync();
}

Each time Entry_TextChanged is triggered, the call to GetStringAsync is done, which is very time consuming. This means that whenever the user presses a key a call to the API is made. This is why it is so slow.
You are better off calling GetStringAsync in the page's OnAppearing (for example), and saving the result globally:
private List<tblItem> listOfTableItems = new List<tblItem>();
protected override void OnAppearing()
{
var json = await httpClient.GetStringAsync("http://172.16.4.212:51583/api/GetItems");
listOfTableItems = JsonConvert.DeserializeObject<List<tblItem>>(json);
}
Then, in your Entry_TextChanged you reference listOfTableItems from the examples above:
if (String.IsNullOrEmpty(e.NewTextValue))
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems);
}
else
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems
.Where(x => x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant())));
}

Related

.Net - WPF .Net Core 3.1 - CapturePicker The application called an interface that was marshalled for a different thread

Currently I'm trying to develop a solution uses .Net Core 3.1 and it's responsible to select multiple capture windows, to be shared when focused.
I've made it work using a list, but I think would be much more elegant and user friendly if I've a picker that shows a preview of the window before selecting.
I'm trying to use GraphicsCapturePicker but I'm getting the following error:
The application called an interface that was marshalled for a different thread
Here is the following methods that I'm using to open the picker:
public async void OpenCapturePicker(object sender, RoutedEventArgs e)
{
Console.WriteLine("OpenCapturePicker");
await Dispatcher.InvokeAsync(OpenCapturePicker);
}
private async void OpenCapturePicker()
{
var picker = new GraphicsCapturePicker();
picker.SetWindow(_capturableWindowHandle);
var item = await picker.PickSingleItemAsync();
if (item == null) return;
//rest of code
}
The _capturableWindowHandle is defined in the constructor of the class like:
public SomeClass(){
_capturableWindowHandle = new WindowInteropHelper(this).Handle;
}
Also I've an extension class that allows me to use the SetWindow method
public static class CaptureHelper
{
static readonly Guid GraphicsCaptureItemGuid = new Guid("79C3F95B-31F7-4EC2-A464-632EF5D30760");
public static GraphicsCaptureItem CreateItemForWindow(IntPtr windowInteropHandler)
{
var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
var interop = (IGraphicsCaptureItemInterop) factory;
var itemPointer = interop.CreateForWindow(windowInteropHandler, GraphicsCaptureItemGuid);
var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
Marshal.Release(itemPointer);
return item;
}
public static void SetWindow(this GraphicsCapturePicker picker, IntPtr hwnd)
{
var interop = (IInitializeWithWindow)(object)picker;
interop.Initialize(hwnd);
}
}
Update
Here is the link of the branch that I'm using to add this feature
Any ideas?
Update:
This Code worked for me:
if (!GraphicsCaptureSession.IsSupported()) {
Trace.WriteLine("No Capture Support");
return;
}
// var picker = new GraphicsCapturePicker();
// picker.SetWindow(_capturableWindowHandle);
// var item = await picker.PickSingleItemAsync();
var interopWindow = new WindowInteropHelper(this);
var hwnd = interopWindow.Handle;
var picker = new GraphicsCapturePicker();
picker.SetWindow(hwnd);
var item = await picker.PickSingleItemAsync();
if (item == null) {
return;
}
_windowCaptureService.StartCapture(item, _capturableWindowHandle);
Be aware: This code is not doing anything if you put a breakpoint in it... Maybe it is initialized with the debugger window in this case...??? But without breakpoints it is working on my Windows 10 machine.
First Answer:
The error message is telling you, that something was created on a different thread than it is used later on. I don't see your complete code here, but you should check, if you e.g. call the constructor of SomeClass in the same thread as
picker.SetWindow(_capturableWindowHandle);
Same thing with all other code involved here or prior to OpenCapturePicker...

500 server error due to absence of async in controller? Need help removing an object from a list C#

I am trying to filter out all errors except one from a list of errors I send back to the front end. I realize this operation should be an async operation as my request is giving a 500 internal server error. I am new to C# and am having a hard time figuring out how to do so.
My code that gets invoked on the route request looks like:
public async Task<ActionResult> Index(ProfileParams profileParameters)
{
// ...... //
var user = await GenerateUser(Request.RequestContext);
var userState = await _userStateFactory.CreateAsync(user);
var stateTree = new BusinessProfileStateTreeModel
{
Global = await _globalStateFactory.CreateAsync(user),
Header = await _headerStateFactory.CreateAsync(user, null),
User = userState,
Modals = _modalsStateFactory.Create(),
Page = CreatePageState(),
BusinessProfile = _businessProfileReviewsStateFactory.Create(viewModel, customerReviewModel),
Analytics = await _analyticsStateFactory.CreateAsync(user, CreateDtmData(viewModel?.Categories?.PrimaryCategoryName, profileBbbInfo?.BbbName, viewModel), userState)
};
// trying to filter out errors here from the state tree alerts
var errors = filterErrorsAsync(stateTree.BusinessProfile.Display.Alerts.AllAlerts);
var metaData =
GenerateProfileMetaData(customerReviewModel.NumFound, viewModel.ProfileUrl.ToUrlString(), viewModel);
var serverSideModel =
GenerateServerSideModel(
viewModel,
metaData,
profileBbbInfo,
stateTree.Analytics.DtmData,
user);
return await ReduxViewAsync(stateTree.ToList(), serverSideModel);
}
}
The filterErrorsAsync method looks like:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
allAlerts.Clear();
allAlerts.Add(alert);
}
}
return allAlerts;
}
Can someone tell me how to achieve this correctly?
You can't loop a list and modify it at the same time. This is probably what is causing your 500 error.
It looks like you only want filter out certain errors from a list. If you want to keep your method as a loop you can do:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
List<BPAlert> temp = new List<BPAlert>(); //copy into new list
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
temp.Add(alert);
}
}
return temp;
}
If you want to be a little more modern you can also just use LINQ
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
return allAlerts.Where(alert => alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews).ToList();
}
You're attempting to modify a list while enumerating it which won't work. Since you already know which kind of error you want to filter to, you can utilize LINQ's Where method to filter out the other errors, then use Take to get the first one.
private List<BPAlert> filterErrors(List<BPAlert> allAlerts)
=> allAlerts.Where(alert => alert.AlertTypeID == (int)BusinessReportCustomTextType.CustomerReviews)
.Take(1)
.ToList();
There isn't anything asynchronous happening in this method, so no need to mark it async.

Observable | Subscribe to get only the changed objects

I have a list of Objects that I get from Realtime-Database (Firebase) with a wrapper in C# Firebase.Xamarin
private List<Job> jobList = null;
which populates at first the application is loaded using the following code:
private async void PopulateList()
{
IReadOnlyCollection<Firebase.Xamarin.Database.FirebaseObject<Job>> items = await firebase
.Child("jobs")
.OnceAsync<Job>();
jobList = new List<Job>();
foreach (var item in items)
{
jobList.Add(
new Job
{
ID = item.Object.ID,
StartDate = item.Object.StartDate,
EndDate = item.Object.EndDate,
Description = item.Object.Description
});
}
SubscribeDbChanges();
}
I want to subscribe the DB to fire an event and change/add new Objects to the list and fire an event afterwords to show or notify the user for one time, a change has been occurred. For this purpose I am using the Observable i.e Reactive Rx.Net with All
in the following way:
private void SubscribeDbChanges()
{
Observable.All<FirebaseEvent<Job>>(
firebase
.Child("jobs")
.AsObservable<Job>(),
job => !jobList
.Contains(job.Object))
.Subscribe(jobItem =>
{
});
}
Is there any thing wrong with the code? Moreover, where should I create the event that a change has arrived?
First I would recommend getting rid of the foreach/Add, it is the work for Select
private async void PopulateList()
{
jobList = (await firebase
.Child("jobs")
.OnceAsync<Job>())
.Select(item =>
new Job
{
ID = item.Object.ID,
StartDate = item.Object.StartDate,
EndDate = item.Object.EndDate,
Description = item.Object.Description
});
SubscribeDbChanges();
}
Then I would use Where. How you use All is weird, it is an extension method and you call it like a usual static method. It is possible but it is now how it should be used. Here is the code with Where:
private void SubscribeDbChanges()
{
firebase
.Child("jobs")
.AsObservable<Job>()
.Where(job => !jobList.Contains(job.Object))
.Subscribe(jobItem =>
{
});
}
Thanks to #Alexey Zimarev for giving me a quick click, and the flowing code came into appearance:
First Of all took a ConcurrentDictionary that as a data member and initialized the Firebaseclient and than populated the list ;
FirebaseClient firebase;
private ConcurrentDictionary<string, Job> jobList ;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
firebase = new FirebaseClient("https://samplehosting-XXXX.firebaseio.com/");
PopulateList();
}
Inside this function I initialized the dictionary and done the UI work, at last set the listeners respectively to monitor the change in SubscribeToDbChanges
private async void PopulateList()
{
IReadOnlyCollection<Firebase.Xamarin.Database.FirebaseObject<Job>> items = await firebase
.Child("jobs")
.OnceAsync<Job>();
jobList = new ConcurrentDictionary<string, Job>();
foreach (var job in items)
{
while (!jobList.TryAdd(job.Object.ID, job.Object)) ;
}
list = FindViewById<ListView>(Resource.Id.listJobs);
list.ChoiceMode = ChoiceMode.Single;
HomeScreenAdapter ListAdapter = new HomeScreenAdapter(this, jobList);
list.Adapter = ListAdapter;
SubscribeToDbChanges();
}
Here i set the Insertion observer for the keys that are not available in the dictionary and than set the deletion observer for the keys that are available in the dictionary.
private void SubscribeToDbChanges()
{
firebase
.Child("jobs").AsObservable<Job>()
.Where(job => !jobList.ContainsKey(job.Object.ID) && job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.InsertOrUpdate)
.Subscribe(job =>
{
if (job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.InsertOrUpdate)
{
while (!jobList.TryAdd(job.Object.ID, job.Object)) ;
}
});
firebase
.Child("jobs").AsObservable<Job>()
.Where(job => jobList.ContainsKey(job.Object.ID) && job.EventType == Firebase.Xamarin.Database.Streaming.FirebaseEventType.Delete)
.Subscribe(job =>
{
Thread remove = new Thread(() =>
{
Job removed = null;
jobList.TryRemove(job.Object.ID, out removed);
});
remove.Start();
});
}
PS: Assumption is here, I assume that the objects that we were adding in the above question and answer were not getting compared because of there memory existence. Like a new object was getting instantiated the might be different from its clone already existing in the list. Please correct me if I am wrong with respect to Linq behavior. However the above given code works in my scenario with dictionary.

WinRT DownloadProgress callback Progress Status

I am writing a universal app primarily targeting Windows Phone using SQLite-net.
During the course of operation, the user is presented with an option to download multiple files. At the end of each file download, I need to mark the file in the db as completed. I am using BackgroundDownloader in order to download files - the WP8.0 app used Background Transfer Service and that worked great. Files can be huge (some 200+ mbs, user content) and i am not looking forward to wrapping the downloads in the HttpClient or WebClient.
However, it seems that the progress callback doesn't work with awaits unless I actually breakpoint in the method.
The following are listings from a sample app i quickly put together that demonstrates the behaviour:
Model:
public class Field
{
[PrimaryKey]
[AutoIncrement]
public int Id { get; set; }
public bool Done { get; set; }
}
MainPage codebehind (i am creating a db here only for the purposes of this example!):
private async void Button_Click(object sender, RoutedEventArgs e)
{
using (var db = new SQLiteConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//Main.db"))
{
db.CreateTable<Field>();
db.Commit();
}
this.DbConnection = new SQLiteAsyncConnection(Windows.Storage.ApplicationData.Current.LocalFolder.Path + "//My.db");
var dl = new BackgroundDownloader();
dl.CostPolicy = BackgroundTransferCostPolicy.Always;
var transferUri = new Uri("http://192.168.1.4/hello.world", UriKind.Absolute);
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(
"Content",
CreationCollisionOption.OpenIfExists);
var localFile = await folder.CreateFileAsync("cheesecakes.file", CreationCollisionOption.ReplaceExisting);
var d = dl.CreateDownload(transferUri, localFile);
d.Priority = BackgroundTransferPriority.High;
var progressCallback = new Progress<DownloadOperation>(this.DownloadProgress);
await d.StartAsync().AsTask(progressCallback);
}
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
if (download.Progress.Status == BackgroundTransferStatus.Completed)
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
}
If i breakpoint inside the DownloadProgress and then press F5 i get both Debug messages, and my db gets a new record.
However, if i just let the code execute, i never see "DONE" printed to me and neither is my db updated.
I tried wrapping the code in a new task:
await Task.Run(
async () =>
{
Debug.WriteLine("taskrun");
.... OTHER CODE FROM ABOVE...
});
But again, i only get to see 'taskrun' if i breakpoint in the callback.
UPDATE I actually think this is more related to checking the status. E.g. the statements outside of the check are executed, but only once, whereas anything inside the check is not executed.
Is there any way to force that callback to be invoked when the download is completed?
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
var value = download.Progress.BytesReceived * 100 download.Progress.TotalBytesToReceive;
new System.Threading.ManualResetEvent(false).WaitOne(1000);
if (download.Progress.Status == BackgroundTransferStatus.Completed )
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
}
I had this problem too, and I solved this by sleeping for 1000 ms, which worked really well for me.
Not sure what is causing this, but I was able to get the sample app to work reliably by manually checking the bytes to download as opposed to relying on the DownloadOperation.Progress.Status:
private async void DownloadProgress(DownloadOperation download)
{
Debug.WriteLine("Callback");
var value = download.Progress.BytesReceived * 100 / download.Progress.TotalBytesToReceive;
if (download.Progress.Status == BackgroundTransferStatus.Completed || value >= 100)
{
var f = new Field();
f.Done = true;
await this.DbConnection.InsertAsync(f);
Debug.WriteLine("DONE");
}
This gets me to 'DONE' every time.

Asynchronous call in web service

I'm using web service for my wp7 app and i have a difficulty in finding out whether the asynchronous call to the service returns the value or not. i have a validation depends on the response(result). The following the code snippet for calling the web service and appropriate changes that should be made to the UI.
private void TLP_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
objevnt.indexChanged(1, lpcountry.SelectedIndex, "depart");
if(objevnt.CName.Count>0)
lpcity.ItemsSource = objevnt.GetCityDetails();
}
public void indexChanged(int Travel,int index,string journey)
{
string slCountry = null;
switch (Travel)
{
case 1:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
//= "depart";
travelMode = journey;
break;
case 2:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
travelMode = journey;
break;
case 3:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
travelMode = journey;
break;
}
GetCities = "http://Solutions/mobileservice/Citylist/countrycode/" + slCountry;
WebClient myClientcity = new WebClient();
myClientcity.DownloadStringAsync(new Uri(GetCities, UriKind.RelativeOrAbsolute));
myClientcity.DownloadStringCompleted += new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted);
}
private void myClientcity_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string _Countries = null;
if (e.Error == null)
{
_Countries = e.Result;
parseCtry(_Countries);
}
}
private void parseCtry(string xml)
{
XDocument myXdoc = XDocument.Load(new StringReader(xml));
IEnumerable<XElement> lstElm = myXdoc.Descendants("GetCityList");
lstCtryDetails.lstCitylist.Clear();
foreach (XElement ele in lstElm.Elements())
{
if (ele.Name.ToString() != "Message")
{
// Fetch t Details
if (!ele.IsEmpty && ele.Name.ToString() == "City")
{
lstCtryDetails.lstCitylist.Add(new cityList() { CityId = ele.Element("CityCode").Value, CityName = ele.Element("CityName").Value, CityLatitude = ele.Element("Latitude").Value, CityLongitude = ele.Element("Longitude").Value });
CName.Add(ele.Element("CityName").Value);
//Countchk = true;
}
}
}
lsCity = lstCtryDetails.lstCitylist;
//chkloop = true;
}
public List<cityList> GetCityDetails()
{
if (lsCity.Count > 0)
return lsCity;
return null;
}
Now i need to get the list of values from the getcitydetails() method. but that is reurning null due to asynchronous call. how to get the count of list, to do appropriate validations. Thanks in advance.
You're setting the ItemsSource potentially before your WebClient has returned it's result, nor had chance to call parseCtry.
If you move:
lpcity.ItemsSource = objevnt.GetCityDetails();
to the end of parseCtry (i.e. where the comment //chkloop = true; is) then it will evaluate to have the correct count - but you may run into cross-thread UI access issues, but you should be able to check with something like (at the start of DownloadStringCompleted):
if (Dispatcher.CheckAccess())
Dispatcher.BeginInvoke(new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted), xml) // put us back on the UI thread if required
You may be better off changing the type of lsCity to be an ObservableCollection<cityList> and binding to it.
Have you tried adding the callback before performing the webrequest?
myClientcity.DownloadStringCompleted += new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted);
myClientcity.DownloadStringAsync(new Uri(GetCities, UriKind.RelativeOrAbsolute));
=== Updated ===
From your comments I think I understand that problem. What you want is to data bind an ObservableCollection<T> to your UI element lpCity. If for instance if lpCity was data bound to lsCity (implementing ObservableCollection<T>), once lpCity is updated via a asynchronous web request, the UI element will update automatically.
I would write out a code skeleton for you, I'm away from my Windows PC.

Categories

Resources