SearchBox windows App 8.1 , searching a substring "contained" - c#

Building an App for windows store 8.1, i implement my searchbox, following the tutorial.
But, it works good only searching a substring that "startWith" another.
If i try to use something like "contains" i got a crush, an unhandled exception.
It works at the first, but if i change the query in the searchBox it crush ... often, not allways.
I can't understand the debugger message,because it refers to the app code...
That's my debugger message
Debugger:Debugger Break
A break in the debugger session because a user paused the session.
Time: 13/04/2014 12:34:34
Thread:<No Name>[6220]
and refers to
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
}
my e object got an Exception : incorrect Parameter, handled : false....
that's my SuggestionRequested function, where there is the problem (i think)
public async void OnSuggest(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
TransporterExt tr_search = new TransporterExt();
ObservableCollection<TransporterExt> querySuggestions = new ObservableCollection<TransporterExt>();
var queryText = args.QueryText != null ? args.QueryText.Trim() : null;
if (string.IsNullOrEmpty(queryText)) return;
suggested.Clear();
tr_search.name = queryText;
try
{
var suggestionCollection = args.Request.SearchSuggestionCollection;
querySuggestions = await TransporterService.Search(tr_search);
if (querySuggestions != null && querySuggestions.Count > 0 )
{
int i = 0;
foreach (TransporterExt tr in querySuggestions)
{
string strB = sender.QueryText;
string strA = tr.name;
if(await Utility.Compare(strA,strB))
//using this condition all works fine
//if (tr.name.StartsWith(sender.QueryText, StringComparison.CurrentCultureIgnoreCase))
{
string name = tr.name;
string detail = tr.trId.ToString();
string tag = i.ToString();
string imageAlternate = "imgDesc";
suggestionCollection.AppendResultSuggestion(name, detail, tag, imgRef, imageAlternate);
if(tr!=null)
suggested.Add(tr);
i++;
}
}
}
}
catch (Exception exc)
{
//Ignore any exceptions that occur trying to find search suggestions.
Debug.WriteLine("Exception " + exc.Message);
Debug.WriteLine(exc.StackTrace);
}
deferral.Complete();
}
and the Contains utility
public static async Task<bool>Compare(string A, string B) {
bool contains = Regex.Match(A, B, RegexOptions.IgnoreCase).Success;
Debug.WriteLine("regex ="+contains);
return contains;
}
I got this SearchBox in a UserControl, and that's the code of my UC
public delegate void SuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args);
public event Windows.Foundation.TypedEventHandler<SearchBox, SearchBoxSuggestionsRequestedEventArgs> SearchBoxSuggestionsRequested;
private void SearchBoxSuggestions(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
if (SearchBoxSuggestionsRequested != null)
SearchBoxSuggestionsRequested(sender, args);
else
Debug.WriteLine("TruckFormUCException");
}
It's possible to search with another logic instead of "startswith"?... where's my fault?

Related

Async function freezes UI thread

I have an async function which still freezes / lags the UI thread for me when I execute it. This is my function calling it.
private void TcpListenerLogic(object sender, string e)
{
Application.Current.Dispatcher.BeginInvoke((Action)async delegate {
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
// Get properties for new anchor
string testInformation = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + results.test_id);
}
}
catch (Exception exception)
{
// Writing some Trace.WriteLine()'s
}
});
}
And this is the async function that freezes my UI Thread
public static async Task<string> getJsonFromURL(string url)
{
try
{
string returnString = null;
using (System.Net.WebClient client = new System.Net.WebClient())
{
returnString = await client.DownloadStringTaskAsync(url);
}
return returnString;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return null;
}
}
I already tried to make everything in TcpListenerLogic run in a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
}).Start();
Which resulted in the whole UI completely freezing. And I tried to make TcpListenerLogic async and await the dispatcher, which also made everything freeze permanently. I also tried to make TcpListenerLogic async and leave the dispatcher. The dispatcher is only there because I normally have some UI code in there, which I left out for my tests.
I have ventured far through the internet, but no BackgroundWorker, ThreadPool or other methods helped me in my endeavour.
If anyone has help for this particular problem, or a resource that would improve my understanding of async functions in C#, I would much appreciate it.
Edit
As requested a deeper insight in how this event handler is called.
I have System.Net.Websocket, which is connected to the Backend API I am working with and triggers an event, everytime he receives new Data. To guarantee the socket listens as longs as it is open, there is a while loop which checks for the client state:
public event EventHandler<string> TcpReceived;
public async void StartListener(string ip, int port, string path)
{
try
{
using (client = new ClientWebSocket())
{
try
{ // Connect to backend
Uri serverUri = new Uri("ws://" + ip + ":" + port.ToString() + path );
await client.ConnectAsync(serverUri, CancellationToken.None);
}
catch (Exception ex)
{
BackendSettings.IsConnected = false;
Debug.WriteLine("Error connecting TCP Socket: " + ex.ToString());
}
state = client.State;
// Grab packages send in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
try
{
// **Just formatting the received data until here and writing it into the "message" variable**//
TcpReceived(this, message);
// Close connection on command
if (result.MessageType == WebSocketMessageType.Close)
{
Debug.WriteLine("Closing TCP Socket.");
shouldstayclosed = true;
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
break;
}
state = client.State;
}
catch
{
BackendSettings.IsConnected = false;
state = client.State;
}
}
state = client.State;
}
}
catch (Exception ex)
{
// Some error messages and settings handling
}
}
The Event has a handler attached:
TcpReceived += TcpListener_TcpReceived;
And this is the Handler, which calls the previously seen "TcpListenereLogic".
private void TcpListener_TcpReceived(object sender, string e)
{
TcpListenerLogic(sender, e);
//App.Current.Dispatcher.BeginInvoke(new Action(() => {
// TcpListenerLogic(sender, e);
//}));
//new Thread(() =>
//{
// Thread.CurrentThread.IsBackground = true;
// TcpListenerLogic(sender, e);
//}).Start();
}
I previously had the "TcpListenereLogic" as the handler, but I wanted to try different methods to call it. I also left in the commented out part, to show how the call of "TcpListenereLogic" looked already. All my attempts were with all mentioned setups and sadly lead to nothing.
Thank you very much #TheodorZoulias for helping me to find the solution to my problem.
It turns out it wasn't the async function itself, but rather how often it gets called. It got called roughly ~120 times every second.
My solution starts by calling the Listener method over a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
MainWindow.tcpListener.StartListener(ip, portNumber, "/api/");
}).Start();
To limit the amount of calls that happen every second I added a dispatcher timer, that resets a bool after it has been used for a call, by my Event.
readonly System.Windows.Threading.DispatcherTimer packageIntervallTimer =
new System.Windows.Threading.DispatcherTimer();
bool readyForNewPackage = true;
private void ReadyForPackage(object sender, EventArgs e)
{
readyForNewPackage = true;
}
public async void StartListener(string ip, int port, string path)
{
packageIntervallTimer.Interval = TimeSpan.FromMilliseconds(50);
packageIntervallTimer.Tick += (s, e) => { Task.Run(() => ReadyForPackage(s, e)); };
packageIntervallTimer.Start();
Then I wrapped everything inside the while loop into an if condition based on the bool, the most important part was to have my "event EventHandler TcpReceived" in there:
// Grab packages sent in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
if (readyForNewPackage == true)
{
readyForNewPackage = false;
try
{
....
TcpReceived(this, message);
....
}
catch
{
...
}
}
}
I added my TcpListenerLogic to the Eventhandler:
TcpReceived += TcpListenerLogic;
And my TcpListenerLogic now looked like this (names have been changed):
private async void TcpListenerLogic(object sender, string e)
{
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
string testID = "";
if (results.test_id is JValue jValueTestId)
{
testID = jValueTestId.Value.ToString();
}
else if (results.test_id is string)
{
testID = results.test_id;
}
// Get properties for new object
string information = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + testID );
if (information != null)
{
await App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Create object out of the json string
TestStatus testStatus = new TestStatus();
testStatus.Deserialize(information);
if (CommunicationCommands.isNameAlreadyInCollection(testStatus.name) == false)
{
// Add new object to the list
CommunicationCommands.allFoundTests.Add(testStatus);
}
}));
{
}
catch (Exception exception)
{
....
}
}
Adding a new Thread to execute any step results in problems, so keep in mind that all this uses the thread created at the beginning for "StartListener"

Xamarin:Forms: NullReferenceException after popping modal page (No MVVM)

I have a project with a TabbedPage layout. One of the TabbedPages has an ItemsSource with an ItemSelected event handler that pushes a modal page. When I pop the modal page, I receive a System.NullReferenceException: 'Object reference not set to an instance of an object.' break. I am not currently using MVVM, and I have set try/catch blocks on everything on the page I can think of, but I cannot find where the exception is, but Visual Studio seems to be indicating that the exception is not in my code. Call stack:
0xFFFFFFFFFFFFFFFF in System.Diagnostics.Debugger.Mono_UnhandledException_internal
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.Diagnostics/Debugger.cs:125,4
0x20 in Android.Runtime.DynamicMethodNameCounter.1
0x12 in System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:157,13
0x6 in System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__7_0 at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1021,49
0xC in Android.App.SyncContext.
0xE in Java.Lang.Thread.RunnableImplementor.Run
0x8 in Java.Lang.IRunnableInvoker.n_Run
0x11 in Android.Runtime.DynamicMethodNameCounter.1
My page that causes the NRE:
{
private ObservableCollection<Adventures> adventures;
private List<Character> charactersList;
string played = "No";
string gmed = "No";
public AdventuresPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
try
{
var adventureList = await App.client.GetTable<Adventures>().Take(200).ToListAsync();
adventures = new ObservableCollection<Adventures>(adventureList);
AdventuresCollectionView.ItemsSource = adventures;
AdventuresCollectionView.SelectedItem = null;
}
catch (NullReferenceException ex)
{
Console.WriteLine(ex.Message);
}
base.OnAppearing();
}
private async void AdventuresCollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var adventure = AdventuresCollectionView.SelectedItem as Adventures;
string advName = (from a in adventures
where a.Id == adventure.Id
select a.AdventureName).First();
await DetermineCredit(advName);
if(adventure != null)
{
await Navigation.PushModalAsync(new AdventureDetailsPage(adventure, played, gmed));
}
else
{
AdventuresCollectionView.SelectedItem = null;
}
}
private void AdvSearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
var normalizedQuery = e.NewTextValue.ToString().ToLower() ?? "";
AdventuresCollectionView.ItemsSource = adventures.Where(a => a.AdventureName.ToLowerInvariant().Contains(normalizedQuery)).ToList();
}
private async Task DetermineCredit(string name)
{
string advName = name;
charactersList = new List<Character>(await App.client.GetTable<Character>().Where(a => a.AccountId == App.account.Id).ToListAsync());
try
{
for (int c = 0; c < charactersList.Count(); c++)
{
var chara = await App.client.GetTable<Character>().Where(ch => ch.Id == charactersList[c].Id).ToListAsync();
string charId = (from ch in chara
select ch.Id).First().ToString();
var charAdv = await App.client.GetTable<CharAdventures>().Where(ca => ca.AdventureName == advName && ca.CharacterId == charId).ToListAsync();
string creditType = (from ch in charAdv
where advName == ch.AdventureName
select ch.CreditType).FirstOrDefault();
if (string.IsNullOrEmpty(creditType))
{
break;
}
else if (creditType == "Player" && played == "No")
{
played = "Yes";
}
else if (creditType == "GM" && gmed == "No")
{
gmed = "Yes";
}
else
{
break;
}
}
}
catch (NullReferenceException ex)
{
Console.WriteLine(ex.Message);
}
}
}
The stack trace doesn't give me enough information to figure out what is causing the error, and it didn't appear when I stepped through until after the overridden OnAppearing() method completed. I do not see anything that is similar to this in a search, and I don't understand why this doesn't appear when the page is loaded for the first time, but only when popping the modal page. Any suggestions would be appreciated.
I stepped through the load a couple more times, and noticed that after closing the modal, the SelectionChanged event handler was firing.
Commenting out the two AdventuresCollectionView.SelectedItem = null; lines allowed the page to reload without an item selected, and without the NullReferenceException.

Different behaviors when instantiated from button or timer in c#

I have a function called getMessages that can be called by a Button click (using the RelayCommand trigger) or that is called in a timer every 15s.
The desired behavior is:
webservice > deserialize answer > system notification > updatelistview > insert localDB
But when the function is called by the timer the updatelistview is not done. Why does this happen if the function is the same and works perfectly in the button command?
CODE:
// Get messages for the logged in user
public async void getMessages()
{
try
{
List<FriendGetMessage> msg = new List<FriendGetMessage>();
var response = await CommunicationWebServices.GetCHAT("users/" + au.idUser + "/get", au.token);
if (response.StatusCode == HttpStatusCode.OK) // If there are messages for me.
{
var aux = await response.Content.ReadAsStringAsync();
IEnumerable<FriendGetMessage> result = JsonConvert.DeserializeObject<IEnumerable<FriendGetMessage>>(aux);
if (result != null)
{
foreach (var m in result)
{
msg.Add(m);
}
//MsgList=msg;
foreach (var f in Friends)
{
if (f.msg == null || f.msg.Count() == 0)
{
f.msg = new ObservableCollection<Messages>();
}
foreach (var mess in msg)
{
if (mess.idUser == f.idUser)
{
Messages mm = new Messages();
mm.received = mess.message;
mm.timestamp = "Received " + mess.serverTimestamp;
mm.align = "Right";
// Add to the friend list.
f.msg.Add(mm);
// Add to Local DB
InsertMessage(null, au.idUser.ToString(), f.idUser, mess.message, mess.serverTimestamp);
var notification = new System.Windows.Forms.NotifyIcon()
{
Visible = true,
Icon = System.Drawing.SystemIcons.Information,
BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info,
BalloonTipTitle = "New Message from " + f.name,
BalloonTipText = "Message: " + mess.message,
};
// Display for 5 seconds.
notification.ShowBalloonTip(5);
// The notification should be disposed when you don't need it anymore,
// but doing so will immediately close the balloon if it's visible.
notification.Dispose();
}
}
}
counterChat = 1; // resets the counter
}
}
else {
counterChat = counterChat * 2;
}
//var sql = "select * from chat";
//var respo = GetFromDatabase(sql);
OnPropertyChanged("Friends");
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
Debug.WriteLine("{0} Exception caught.", e);
}
}
CODE TIMER:
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
ADDED - I've also tried this and didn't worked (it seems that is some kind of concurrency problem to the ObservableCollection called Friends (is a friendslist) each friend has an ObservableCollection of messages (is a chat))
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
Application.Current.Dispatcher.Invoke((Action)async delegate { await getMessages(); });
incChat = 0;
}
}
Best regards,
I think you need to make the timer handler be an async method as follows:
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
await getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
This way OnPropertyChanged("Friends") is guaranteed to fire after the work in getMessages is done.
The methods need to change to:
DispatcherTimer _timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
public async void timerchat_Tick(object sender, EventArgs e)
{
//...
await getMessages();
//...
}
public async Task getMessages()
{
try
{
// ... your code here
string result = await response.Content.ReadAsStringAsync();
// .... rest of your code
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
}
}
It is solved. The problem was in my ViewModels I was opening multiple threads and sometimes the right one would update the UI and sometimes no.
Thanks for all the answers.

SearchBox result suggestion Windows 8.1

I got a strange issue with my searchBox in Windows 8.1 App.
I got an unhandler exception (and a crush) if in my Suggestion i do not append the querySuggestion and append only the ResultSuggestion.
the problem occurs when i change the queryText.
This is my function
public async void OnSuggest(Windows.UI.Xaml.Controls.SearchBox e, SearchBoxSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
var queryText = args.QueryText != null ? args.QueryText.Trim() : null;
if (string.IsNullOrEmpty(queryText)) return;
TransporterExt tr_search = new TransporterExt();
tr_search.name = queryText;
try
{
var suggestionCollection = args.Request.SearchSuggestionCollection;
ObservableCollection<TransporterExt> querySuggestions = await TransporterService.Search(tr_search);
if (querySuggestions != null && querySuggestions.Count > 0)
{
foreach (TransporterExt tr in querySuggestions)
{
//if (tr.name.ToUpperInvariant().Contains(e.QueryText.ToUpperInvariant()))
//{
// //suggestionCollection.AppendQuerySuggestion(tr.name);
// suggestionCollection.AppendResultSuggestion(tr.name,
// tr.trId.ToString(),
// tr.trId.ToString(),
// imgRef, "imgDesc");
//}
suggestionCollection.AppendQuerySuggestion(tr.name);
}
}
}
catch (Exception)
{
//Ignore any exceptions that occur trying to find search suggestions.
}
deferral.Complete();
}
I got the searchBox inside an UserControl
My controller code
public delegate void SuggestionsRequested(Windows.UI.Xaml.Controls.SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args);
public event Windows.Foundation.TypedEventHandler<Windows.UI.Xaml.Controls.SearchBox, SearchBoxSuggestionsRequestedEventArgs> SearchBoxSuggestionsRequested;
private void SearchBoxSuggestions(Windows.UI.Xaml.Controls.SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
if (SearchBoxSuggestionsRequested != null)
SearchBoxSuggestionsRequested(sender, args);
}
I got this exception
WinRT: A method was called at an unexpected time.
exception: System.InvalidOperationException - type (string)
Edited Solution - Working function
First of all i remove from the constructor of the page the registration of event
public TruckCrudPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
this.navigationHelper.SaveState += navigationHelper_SaveState;
//this.truckForm.SearchBoxSuggestionsRequested += OnSuggest;
}
public async void OnSuggest(Windows.UI.Xaml.Controls.SearchBox e, SearchBoxSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
TransporterExt tr_search = new TransporterExt();
ObservableCollection<TransporterExt> querySuggestions = new ObservableCollection<TransporterExt>();
var queryText = args.QueryText != null ? args.QueryText.Trim() : null;
if (string.IsNullOrEmpty(queryText)) return;
suggested.Clear();
tr_search.name = queryText;
try
{
var suggestionCollection = args.Request.SearchSuggestionCollection;
querySuggestions = await TransporterService.Search(tr_search);
if (querySuggestions != null && querySuggestions.Count > 0 )
{
int i = 0;
foreach (TransporterExt tr in querySuggestions)
{
if (tr.name.StartsWith(e.QueryText, StringComparison.CurrentCultureIgnoreCase))
//if (tr.name.ToLower().Contains(e.QueryText))
{
string name = tr.name;
string detail = tr.trId.ToString();
string tag = i.ToString();
string imageAlternate = "imgDesc";
suggestionCollection.AppendResultSuggestion(name, detail, tag, imgRef, imageAlternate);
suggested.Add(tr);
//Debug.WriteLine("dentro" + suggested.Count);
i++;
}
}
}
}
catch (Exception exc)
{
//Ignore any exceptions that occur trying to find search suggestions.
Debug.WriteLine("Exception generata " + exc.Message);
Debug.WriteLine(exc.StackTrace);
}
deferral.Complete();
}
But it works only with condition StartsWith and i would like to use Contains
You can use SearchBox and SuggestionRequested event to fire the event when type on the SearchBox. I will show an Example
<SearchBox x:Name="SearchBoxSuggestions" SuggestionsRequested="SearchBoxEventsSuggestionsRequested"/>
and write the SearchBoxEventsSuggestionsRequested handler in the code behind
private void SearchBoxEventsSuggestionsRequested(object sender, SearchBoxSuggestionsRequestedEventArgs e)
{
string queryText = e.QueryText;
if (!string.IsNullOrEmpty(queryText))
{
Windows.ApplicationModel.Search.SearchSuggestionCollection suggestionCollection = e.Request.SearchSuggestionCollection;
foreach (string suggestion in SuggestionList)
{
if (suggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))
{
suggestionCollection.AppendQuerySuggestion(suggestion);
}
}
}
}
You can add the keyword to SuggestioList, and it will show in the dropdown when you type on the Searchbox.
Create the SuggestionList
public List<string> SuggestionList { get; set; }
initialize the list
SuggestionList = new List<string>();
and add keywords to the list
SuggestionList.Add("suggestion1");
SuggestionList.Add("suggestion2");
SuggestionList.Add("suggestion3");
SuggestionList.Add("suggestion4");
SuggestionList.Add("Fruits");
When you type s on the Searchbox it will show all the keyword starts with s.
Thanks.

Async method does not return control flow to form

I have a problem with my async method in WPF application. I want to implement asynch await pattern in my WPF application to retain form responsiveness when querying database with EntityFramework. I did everything as described in microsoft examples but it doesn't return control flow to the application as example applications do.
Here is my button click code:
private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
var loggedIn = await _gluUserRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Text);
Switcher.Switch(new Loader());
if (loggedIn)
{
UserName = LoginTextBox.Text;
Switcher.Switch(new Blank());
}
else
{
UserName = String.Empty;
MessageBox.Show("Błędny login lub hasło. Spróbuj ponownie.");
Switcher.Switch(new Login());
}
}
Here is my LoginAsync method:
public async Task<bool> LoginAsync(string login, string password)
{
string hashedPassword = PasswordHasher.Hash(password);
var user = await _dbContext.Users.FirstOrDefaultAsync(x => x.UserName == login);
if (user != null && user.Password == hashedPassword)
return true;
return false;
}
I've seen the same usage of async/await in Microsoft example applications, however, their WPF apps return control to Window handle, i.e. I can move window around when in my app that is impossible.
What I want to achieve is using async/await pattern with WPF application to retain responsiveness of application. I want to display loading circle when querying database and return to method when querying has been completed.
Anyone has an idea what am I doing wrong?
If I understand correctly
Switcher.Switch(new Loader());
is what shows the 'loading circle'.
You need to call it before you await the login. As it is now, your handler does everything else after LoginAsync returns.
You may also want to look at ReactiveUI. It provides a useful framework solving your problem.
It's hard to make guesses, but try changing the beginning of your LoginButton_Click code to start the task and await for its result later, like this:
private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
var loggedInTask = _gluUserRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Text);
MessageBox.Show("before await");
var loggedIn = await loggedInTask;
MessageBox.Show("after await");
// ...
}
You should be seeing two message boxes, one after another, both of them should be responsive, i.e. moveable and click-able. If you do see such behaviour, then the problem is most likely with your Switcher.Switch and has nothing to do with asynchronous methods of EntityFramework.
I've found solution. Though I don't know why it actually works. But it doesn't block UI thread. I don't think it's thread safe too, you should we weary.
private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
var loginTask = userRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Password);
controller.DisplayPageLoader();
DbUser loginResult = await loginTask;
if (loginResult != null)
{
controller.DisplayPageNewMeal();
controller.SetLoggedUser(loginResult);
}
else
{
MessageBox.Show("Błędny login lub hasło. Spróbuj ponownie.");
controller.DisplayPageLogin();
}
}
and then in repository
public Task<DbUser> LoginAsync(string login, string password)
{
return Task.Run<DbUser>( () => Login(login, password));
}
private DbUser Login(string login, string password)
{
try
{
string hashedPassword = PasswordHasher.Hash(password);
var user = _dbContext.Users.FirstOrDefaultAsync(x => x.UserName == login);
if (user.Result != null && user.Result.Password == hashedPassword)
return user.Result;
return null;
}
catch(Exception ex)
{
_logger.Error("Blad logowania uzytkownika", ex);
return null;
}
}
i have main form with async load method :
private async void MainMenu_Load(object sender, EventArgs e)
{
await _connection.Start();
await _myHub.Invoke("Join");
_myHub.On("recieved", data =>
{
var obj = JsonConvert.DeserializeObject(data);
messagewindows = new SendMessage(User, obj.Sender);
if ((obj.Reciever == User ) )
{
messagewindows. txtHistory.Text += obj.Sender + " :" + obj.text + Environment.NewLine;
messagewindows.Show();
}
});
}
i show second form for show message but form hang crash app :
private async void SendMessage_Load(object sender, EventArgs e)
{
Text = Reciever;
await _connection.Start();
await _myHub.Invoke("Join");
_myHub.On("recieved", data =>
{
var obj = JsonConvert.DeserializeObject<MessageSent>(data);
if ((obj.Reciever == Sender || obj.Sender == Sender) && (obj.Sender == Reciever || obj.Reciever == Reciever))
{
txtHistory.Text += obj.Sender + " :" + obj.text + Environment.NewLine;
txtHistory.SelectionStart = txtHistory.Text.Length;
txtHistory.ScrollToCaret();
}
});
}

Categories

Resources