C# API in Winforms - c#

I have this code to generate random jokes from an API and I want it to output a joke to a listbox after pressing a button. But I cant put it working... Im kinda new so any hints to help me pls? Thank you! :)
My code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Windows;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JokeGEN
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
static readonly HttpClient client = new HttpClient();
public class Value
{
public int id { get; set; }
public string joke { get; set; }
public List<string> categories { get; set; }
}
public class Root
{
public string type { get; set; }
public Value value { get; set; }
}
private void button2_Click(object sender, EventArgs e)
{
Task<string> responseBody = client.GetStringAsync("http://api.icndb.com/jokes/random");
listBox1.Items.Add(responseBody.Result);
Root jokes = JsonConvert.DeserializeObject<Root>(responseBody.Result);
string joke = jokes.value.joke;
}
}
}

First of all: separate your model from how you display your model (your view). Apart from that your code will be better to reuse, it will also be easier to unit test and thus to debug. If later you decide that you want to view your jokes differently, you won't have to change the model.
private async Task<string> FetchJokeAsync()
{
using (var client = new HttpClient())
{
const string requestUri = "http://api.icndb.com/jokes/random";
string responseBody = await client.GetStringAsync(uri);
return responseBody;
}
}
About async-await: Every method that uses async-await, has to be declared async. Return Task instead of void, and Task<TResult> instead of TResults. There is only one exception: event handlers return void instead of Task.
await inside the async method. The return value of await is the TResult. It is convention to append the identifier of the method with async.
By the way, HttpClient implements IDisposable. This means that the designer of the class thought that it holds scarce resources. It is convention not to keep IDisposable object longer alive then needed. You have to weigh the costs of construction against the cost of holding a scarce resource, but since you are doing this as a result to a button click, I guess you won't do this ten times per second. So reconstructing the Client is no problem.
Apparently you want to display the fetched jokes in a ListBox. You need a method for this:
private void DisplayJoke(string joke)
{
this.listBoxJokes.Items.Add(joke);
}
Now to fetch the Joke and Display the fetched joke whenever the button is clicked:
private async void OnButtonCreateJoke_ClickedAsync(object sender, ...)
{
string joke = await this.FetchJokeAsync();
this.DisplayJoke(joke);
}
Remember: OnButtonCreateJoke is an async event handler, the return value should be void, not Task.
If this does not work, it will be easy to debug it. First replace the JokeFetcher:
private async Task<string> FetchJokeAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)); // simulate some wait time
return "This is a Joke";
}
What happens? Is the joke displayed correctly, then apparently the problem is in this method. If not displayed correctly: Try to debug DisplayJoke: what happens if you display it in a Text Box?
private void DisplayJoke(string joke)
{
this.textBox1.Text = joke;
}
By now you should know whether the problem is in your joke fetching or joke displaying.
Conclusion
By separating the model (= how to get a joke) from the view (=how to display the joke) it is way easier to understand what happens, to test your code, and thus to debug your code, it is way easier to change the requirements: if you need to display the joke in a TextBox, changes are minimal.

Related

How to put as input parameter of method ID from database instead of a number from a field usin SQLite - Xamarin

I have a button method that expects me to give it a value from the field that serves as the ID.
async void Tapped(System.Object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
var person = await App.SQLiteDb.GetItemAsync(Convert.ToInt32(txtPersonId.Text));
if (person != null)
{
await DisplayAlert("Success", "Person Name: " + person.Name + Environment.NewLine + "Person ID: " + person.PersonID, "OK");
}
}
How to replace:
Convert.ToInt32(txtPersonId.Text)
With:
person.PersonID
So not to expect an input parameter but to get an ID from the database for the respective name ?
My SQLiteHelper.cs look like this:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
namespace WeatherLocationInfo.Views
{
public class SQLiteHelper
{
SQLiteAsyncConnection db;
public SQLiteHelper(string dbPath)
{
db = new SQLiteAsyncConnection(dbPath);
db.CreateTableAsync<Person>().Wait();
}
//Insert and Update new record
public Task<int> SaveItemAsync(Person person)
{
if (person.PersonID != 0)
{
return db.UpdateAsync(person);
}
else
{
return db.InsertAsync(person);
}
}
//Delete
public Task<int> DeleteItemAsync(Person person)
{
return db.DeleteAsync(person);
}
//Read All Items
public Task<List<Person>> GetItemsAsync()
{
return db.Table<Person>().ToListAsync();
}
//Read Item
public Task<Person> GetItemAsync(int personId)
{
return db.Table<Person>().Where(i => i.PersonID == personId).FirstOrDefaultAsync();
}
}
}
My Person object look like this:
using System;
using SQLite;
namespace WeatherLocationInfo.Views
{
public class Person
{
[PrimaryKey, AutoIncrement]
public int PersonID { get; set; }
public string Name { get; set; }
}
}
My App Xaml.cs look like this:
public static SQLiteHelper SQLiteDb
{
get
{
if (db == null)
{
db = new SQLiteHelper(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "XamarinSQLite.db3"));
}
return db;
}
}
I used tabbed forms on my xamarin project and want to pass as an external parameter id from the database.
I want to completely delete the field for filling in the ID and everything happens automatically.
If I understand your question, first you have to bind your text field into a Person property in your ViewModel then use the Person Object in your Event handler.
var bindingContext = this.BindingContext as YourViewModelName ;
var person = await App.SQLiteDb.GetItemAsync(bindingContext.person.PersonID);
The way you have written your code suggests that you are currently calling your database (SQLLite) methods from your view (mobile app or 'Tabbed forms').
While this is doable, it does not permit binding which is exactly what you are asking for when you mentioned that you would like to use 'person.PersonID' instead of 'Convert.ToInt32(txtPersonId.Text)'
In order to do this, you need to follow a layered architecture of a mobile app where calls are made as follows:
View: This is the face of your mobile app, the screen you see on the phone when you open the app.
ViewModel: The ViewModel communicates with the View and the database and is responsible for fetching data from SQLLite and connecting or 'binding' it do the View.
Model: In your example, this would be the 'Person' object/class and is also used by SQLLite do create tables in the database.
DataLayer: To start off with, I would suggest adding this layer between ViewModel and Model. This layer will have calls like 'App.SQLiteDb.GetItemAsync()' or 'App.SQLiteDb.SaveItemAsync()', just so that you do not have to write this code in the ViewModel, there by keeping the ViewModel clean.
Lastly, I would recommend you familiarizing yourself with the concepts of:
Xamarin and MVVM
Dependency Injection
Try googling out for 'Xamarin mobile app sqlite mvvm tutorials'. There are loads out there that would explain these concepts.
All the best.

C# I'm trying to create a web user control with custom classes, and I am getting a Object not set to an instance of an Object Error

I hope you can help me with this. I am creating an internal webforms asp.net site to display a list of internally used documents in different categories.
I decided to create a custom document class to put in a list to hold the documents, and then a custom web user control to display the documents wherever they want them on the site.
The documents class is in a general class file in my App_Code folder.
cabinet.cs
public class Document
{
private string _Url;
private string _Title;
public Document(string URL, string Title)
{
_Url = URL;
_Title = Title;
}
public string URL
{
get { return _Url; }
set { _Url = value; }
}
public string Title
{
get { return _Title; }
set { _Title = value; }
}
}
This code works just fine. Then in my user control I create a list of type document and initiate it in Page_Load(). Then I created a public method to add new documents to the list.
DocDisplay.ascx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class DocDisplay : System.Web.UI.UserControl
{
private List<Document> _DocList;
protected void Page_Load(object sender, EventArgs e)
{
_DocList = new List<Document>();
}
public void Add(string URL, string Title)
{
_DocList.Add(new Document(URL, Title));
}
public void WriteDocuments()
{
foreach (Document doc in _DocList)
{
Response.Write($"<span class='document'><a href='{doc.URL}'>{doc.Title}</a></span>");
}
}
}
I am getting the error in the add method. It says that my object is not to an instance of an object. But I do that in Page_Load.
index.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
pageDocs.Add("index.aspx", "Hello World!");
pageDocs.Add("index.aspx", "Red Rum");
pageDocs.Add("index.aspx", "Lorum Ipsum");
}
I have registered my user control in my index page.
<%# Register Src="~/DocDisplay.ascx" TagPrefix="uc" TagName="DocDisplay" %>
<uc:DocDisplay ID="pageDocs" runat="server" />
So I am not exactly sure why I am getting that error. As far as I can tell, there is nothing wrong with my code. If you could help I would greatly appreciate it.
Events get fired starting from the root of control hierarchy and end at the leaf nodes. Index.Page_Load is being called before DocDisplay.Page_Load has an opportunity to instantiate the list.
The _DocList field needs a value before it can be used by anything, so initialization needs to happen as early as possible. This is accomplished very easily with a field initializer. Declare and assign it all at once:
private List<Document> _DocList = new List<Document>();
When the Index class instantiates its child controls early in the page life cycle, _DocList will immediately have an object reference.
It's tempting to say, "Page_Init will be called sooner; I'll do it there." This may work at first, but if you do any dynamic control loading, you'll soon find out that it's a balancing act. A dynamically loaded control has to play event catch-up, so its Init event can be fired after statically loaded controls have started firing Load events. It's important to use each event for its purpose, and not for its timing, and use constructors (and field initializers) to initialize non-control class state.

Calling a method from a different ViewModel

I'm writing a program for managing a tool inventory and have run into a problem when I have the users mark a tool as 'fixed'.
The program should work as follows:
Using TIView, TIViewModel, TIModel:
Employee checks tool out.
Tool happens to get damaged during use.
Employee return's the tool marking it as damaged and reporting the problem.
The tool is marked as returned and locked from being check out until fixed.
Using VPRView, VPRViewModel, and VPRModel:
An inspector goes into a data grid showing all tools with problems.
The inspector corrects the problem, marks the tool as fixed, then submits the data.
The program updates the SQLite database with the inspectors ID number, their solution, marks the problem as fixed and logs the date/time of completion.
THE PROBLEM STEP:
8. The program then runs the PopulateToolInventory method from the TIViewModel to update the inventory list so that the tool is no longer locked.
Summarized:
When the inspector marks the tool as fixed the database is updated using the VPRView, VPRViewModel, and VPRModel. The method to pull the data for the tool inventory is found in the TIViewModel. How do I get the application to execute the 'PopulateToolInventory' method from the VPRViewModel after uploading the data to the database via the VPRViewModel?
Code Sample:
VPRViewModel:
public void SubmitSolution()
{
VPRModel vprm = new VPRModel();
vprm.SubmitProblemSolution(ProblemSolved, ProblemSolution, InspectorID, SelectedReport[0].ToString());
ProblemReports = vprm.RetrieveProblemReports();
InspectorID = null;
ProblemSolution = null;
ProblemSolved = false;
MessageBox.Show("Solution successfully recorded!", "Success!", MessageBoxButton.OK);
// This is where I would want to call the method from the TIViewModel to update the data grid on the TIView.
}
TIViewModel:
private DataTable _toolInventory;
public DataTable ToolInventory
{
get { return _toolInventory; }
set
{
_toolInventory = value;
NotifyOfPropertyChange(() => ToolInventory);
}
}
public void PopulateToolInventory()
{
TIModel tim = new TIModel();
ToolInventory = tim.RetrieveToolInventory();
}
ShellViewModel:
class ShellViewModel : Conductor<object>
{
public void Open_ToolInventory()
{
ActivateItem(new TIViewModel());
}
public void ViewProblemReport()
{
WindowManager wm = new WindowManager();
VPRViewModel vprvm = new VPRViewModel();
wm.ShowDialog(vprvm);
}
}
FYI: I'm using Caliburn.Micro if this helps with any solution.
Hopefully this is enough information. If not, just ask for what you need! Also, please don't eat my code alive. I'm self taught and know that I'm far from a professional developer but this is a passion of mine and I'm really enjoying it. Constructive criticism is appreciated, just please don't make me feel stupid.
Using Ed's idea in the question comments I did the following.
class ShellViewModel : Conductor<object>
{
public void Open_ToolInventory()
{
ActivateItem(new TIViewModel());
}
public void ViewProblemReport()
{
WindowManager wm = new WindowManager();
VPRViewModel vprvm = new VPRViewModel();
wm.ShowDialog(vprvm);
}
}
Was changed to:
class ShellViewModel : Conductor<object>
{
TIViewModel tivm = new TIViewModel();
VPRViewModel vprvm = new VPRViewModel();
public void OpenToolInventory()
{
ActivateItem(tivm);
}
public void ViewProblemReport()
{
WindowManager wm = new WindowManager();
wm.ShowDialog(vprvm);
tivm.PopulateToolInventory();
}
}
This runs the targeted method after the dialog is closed updating the tool inventory to reflect all the solved problems at once. You're the best, Ed!

WPF: Execute some code asynchronously in .NET 3.5

I have a MVVM WPF app. I have a window, let's say "LvWindow", with a listview that is loaded from data comming from a database. From main window "MainWindow", I have a menu, which has some options. When I select the option to access "LvWindow", it is open. Then from ViewModel, in the constructor I have a call to a database from which I request some data that then I load into the listview.
My goal is to make the process to request data from database and then load it in the listview asynchronous. I want this in order to not block the whole app, I mean, during this window is loaded, user can go to the main window menu and select to open another type of window. Windows are open in tabs.
While the process of requesting data from database and being loaded into listview in window "LvWindow", I show a splash saying "Loading" on it(in fact this is a rectangle with zindex set to a higher number to avoid user can interact with listview until it is completely loaded). This splash will be closed when the listview is loaded with the data comming from database.
So to make the process asynchronous, I know in winforms it can be done with delegates by using beginInvoke, endInvoke and callbacks methods, see here.
Also, another possibility is to use a background worker, like posted here.
So in WPF which is the best way to do it? using delegates as winforms or background workers?
ATTEMPT #1:
I have tried XANIMAX solution as this:
public class TestViewModel : BaseViewModel
{
private static Dispatcher _dispatcher;
public ObservableCollection<UserData> lstUsers
public ObservableCollection<UserData> LstUsers
{
get
{
return this.lstUsers;
}
private set
{
this.lstUsers= value;
OnPropertyChanged("LstUsers");
}
}
public TestViewModel()
{
ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
{
var result = getDataFromDatabase();
UIThread((p) => LstUsers = result);
}));
}
ObservableCollection<UserData> getDataFromDatabase()
{
return this.RequestDataToDatabase();
}
static void UIThread(Action<object> a)
{
if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
//this is to make sure that the event is raised on the correct Thread
_dispatcher.Invoke(a); <---- HERE EXCEPTION IS THROWN
}
}
but in line _dispatcher.Invoke(a) an exception is thrown:
TargetParameterCountException: the parameter count mismatch
UserData is my data model, it is a class with some public properties. Something like:
public class UserData
{
public string ID{ get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// Other properties
}
so the problem is that the call to database is returning "RequestDataToDatabase" is returning a collection of UserData (ObservableCollection) so the exception is thrown.
I do not know how to solve it. Could you help me, please?
Final solution:
As XAMIMAX said in the comments:
Change the signature from static void UIThread(Action a) to static void UIThread(Action a)
modify UIThread((p) => LstUsers = result); by UIThread(() => LstUsers
= result);
As you can't await asynchronous methods in a constructor in C# 7.0 (but async Main is coming in 7.1) you can extract your async function calls to a separate function in your ViewModel and synchronously call this within your View's code-behind constructor, after you have created your ViewModel and assigned it to the View's DataContext:
public MainWindow()
{
this.vm = new MyViewModel();
this.DataContext = this.vm;
this.InitializeComponent();
this.vm.AsychronousFunctionToCallDatabase();
}
As XAMIMAX says, you want to implement a ViewModel to handle business logic between your View and your models. Then if your ViewModel implements INotifyPropertyChanged and and you set up Binding in your XAML to your properties in the ViewModel - then the display will refresh after the database call without blocking the UI thread. Note, if you have any collections populated from the database call then they should be of type ObservableCollection.
But as Kundan says, in your AsychronousFunctionToCallDatabase() function you should include an await statement or a create a Task on the line that calls the database - this will return control to the calling function (in this case, to the MainWindow constructor).
Here is one of the possible solutions for you.
In your View Model you would have something like this:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace VM
{
public class TestViewModel : BaseViewModel
{
private static Dispatcher _dispatcher;
List<object> ListToDisplay { get; set; }//INPC omitted for brevity
public TestViewModel()
{
ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
{
var result = getDataFromDatabase();
UIThread(() => ListToDisplay = result);
}));
}
List<object> getDataFromDatabase()
{
//your logic here
return new List<object>();
}
static void UIThread(Action a)
{
if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
//this is to make sure that the event is raised on the correct Thread
_dispatcher.Invoke(a);
}
}
}
There are couple of options to run the method asynchronously.
async await - https://msdn.microsoft.com/library/hh191443(vs.110).aspx
Task Parallel Library :https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming

Rebus: advice for adding a usercontext to each message

First let's define 'UserContext' as being a number of properties required to execute the receiving message in the correct context of the user, so it is a bit more than just a string. In my case this also includes data on which application 'instance' the user was working.
As I see it there are 2 main options to provide a 'UserContext' for a message:
As a Header
As a base class for the message
When using a Header, I need to provide my own serialization, when using a base class, Rebus will solve the serialization for me.
So I spiked using a base class using a little sample program:
public class UserContext
{
public string Name { get; set; }
public int UserId { get; set; }
public Guid AppId { get; set; }
}
public class UserContextMessageBase
{
public UserContext UserContext { get; set; }
}
public class SimpleMessage : UserContextMessageBase
{
public string Data { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
using (var adapter = new BuiltinContainerAdapter())
using (var timer = new Timer())
{
//adapter.Register(typeof(UserContextHandler));
adapter.Register(typeof(SimpleMessageHandler));
var bus = Configure.With(adapter)
.Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())
.MessageOwnership(d => d.FromRebusConfigurationSection())
//.SpecifyOrderOfHandlers(o => o.First<UserContextHandler>())
.CreateBus()
.Start();
timer.Elapsed += delegate
{
bus.Send(new Messages.SimpleMessage { Data = Guid.NewGuid().ToString() });
};
timer.Interval = 10000;
timer.Start();
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
}
internal class UserContextHandler : IHandleMessages<UserContextMessageBase>
{
protected UserContext _context;
public void Handle(UserContextMessageBase message)
{
var old = Console.ForegroundColor;
if (_context != null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Context is already populated");
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Processing UserContextMessageBase");
// create the correct Context to process the message
_context = message.UserContext;
Console.ForegroundColor = old;
}
}
internal class SimpleMessageHandler : **UserContextHandler**, IHandleMessages<SimpleMessage>
{
public void Handle(SimpleMessage message)
{
// allow to use the _context to process this message
Console.WriteLine("Received SimpleMessage {0}", message.Data);
}
}
But when I run the program, I see that the SimpleMessage is getting processed twice. Is this 'by design' or perhaps a bug?
On the other hand, I can uncomment the registration for the UserContextHandler, and not inherit the SimpleMessageHandler from the UserContextHandler, but then I would have to stuff the UserContext into the MessageContext, and use it as such from the SimpleMessageHandler.
In my opinion, both approaches are valid - personally, I'd lean towards using headers because they're less noisy, and because that's really what they're there for :) but, as you correctly state, that requires that you somehow take care of "serializing" the user context into one or more headers, deserializing it again upon receiving each message.
The header approach could be done pretty elegantly, though, in the MessageSent and MessageContextEstablished events for sending and receiving respectively, staying out of your message handlers, and then the user context could be made available in the message context.
The other approach with using a message base class is definitely valid too, and I can see that you're hit by the fact that the lookup for the incoming message will get a new handler instance for each lookup - therefore, the pipeline will contain two handler instances, and the message will then be dispatched "as much as possible" (i.e. once for each compatible type/supertype) to each handler instance, thus resulting in effectively handling the message twice.
In your case, I suggest you do as you hint at towards the end: Make the UserContextHandler a separate handler that you ensure gets to be first in the pipeline, thus allowing it to stash the user context in MessageContext.GetCurrent().Items for all subsequent handlers to extract.
I'd love to cook an example, though, showing a way to do exactly what you need, but by using headers (possibly in the form of simply a ;-separated list of key-value pairs, or something similar), but I'm afraid I cannot promise that such an example would be available within the next few days.
Let me know if it works out for you :)
Update: I've added a sample to Rebus' sample repo that demonstrates how an ambient user context can be picked up and passed around in a message header, including a few nifties around configuration and DI - it's called UserContextHeaders - check it out :)

Categories

Resources