I'm working with a oAuthTwitterTimeline example that Andy created but I'm kind of having a hard time making sure the default constructor is called(Parameterless Constructor) to pull the data from my web.config file instead of having me hardcode the app settings in.
I am calling the method "GetMyTimeline()" but AuthenticateSettings.
I get a bunch of null reference exceptions when I finally jump into AuthenticateMe method.
AuthResponse twitAuthResponse = authenticate.AuthenticateMe(AuthenticateSettings);
This is the parameterless constructor
public OAuthTwitterWrapper()
{
string oAuthConsumerKey = ConfigurationManager.AppSettings["oAuthConsumerKey"];
string oAuthConsumerSecret = ConfigurationManager.AppSettings["oAuthConsumerSecret"];
string oAuthUrl = ConfigurationManager.AppSettings["oAuthUrl"];
AuthenticateSettings = new AuthenticateSettings { OAuthConsumerKey = oAuthConsumerKey, OAuthConsumerSecret = oAuthConsumerSecret, OAuthUrl = oAuthUrl };
string screenname = ConfigurationManager.AppSettings["screenname"];
string include_rts = ConfigurationManager.AppSettings["include_rts"];
string exclude_replies = ConfigurationManager.AppSettings["exclude_replies"];
int count = Convert.ToInt16(ConfigurationManager.AppSettings["count"]);
string timelineFormat = ConfigurationManager.AppSettings["timelineFormat"];
TimeLineSettings = new TimeLineSettings
{
ScreenName = screenname,
IncludeRts = include_rts,
ExcludeReplies = exclude_replies,
Count = count,
TimelineFormat = timelineFormat
};
string searchFormat = ConfigurationManager.AppSettings["searchFormat"];
string searchQuery = ConfigurationManager.AppSettings["searchQuery"];
SearchSettings = new SearchSettings
{
SearchFormat = searchFormat,
SearchQuery = searchQuery
};
}
Nothing special in the controller
public class HomeController : Controller
{
private readonly OAuthTwitterWrapper.OAuthTwitterWrapper _oAuthTwitterWrapper;
public HomeController(OAuthTwitterWrapper.OAuthTwitterWrapper oAuthTwitterWrapper)
{
_oAuthTwitterWrapper = oAuthTwitterWrapper;
}
public ActionResult Index()
{
return View();
}
public JsonResult GetTwitterFeed()
{
return Json(_oAuthTwitterWrapper.GetMyTimeline(), JsonRequestBehavior.AllowGet);
}
//Some more stuff
}
The whole class "OAuthTwitterWrapper"
namespace OAuthTwitterWrapper
{
public class OAuthTwitterWrapper : IOAuthTwitterWrapper
{
public IAuthenticateSettings AuthenticateSettings { get; set; }
public ITimeLineSettings TimeLineSettings { get; set; }
public ISearchSettings SearchSettings { get; set; }
/// <summary>
/// The default constructor takes all the settings from the appsettings file
/// </summary>
public OAuthTwitterWrapper()
{
string oAuthConsumerKey = ConfigurationManager.AppSettings["oAuthConsumerKey"];
string oAuthConsumerSecret = ConfigurationManager.AppSettings["oAuthConsumerSecret"];
string oAuthUrl = ConfigurationManager.AppSettings["oAuthUrl"];
AuthenticateSettings = new AuthenticateSettings { OAuthConsumerKey = oAuthConsumerKey, OAuthConsumerSecret = oAuthConsumerSecret, OAuthUrl = oAuthUrl };
string screenname = ConfigurationManager.AppSettings["screenname"];
string include_rts = ConfigurationManager.AppSettings["include_rts"];
string exclude_replies = ConfigurationManager.AppSettings["exclude_replies"];
int count = Convert.ToInt16(ConfigurationManager.AppSettings["count"]);
string timelineFormat = ConfigurationManager.AppSettings["timelineFormat"];
TimeLineSettings = new TimeLineSettings
{
ScreenName = screenname,
IncludeRts = include_rts,
ExcludeReplies = exclude_replies,
Count = count,
TimelineFormat = timelineFormat
};
string searchFormat = ConfigurationManager.AppSettings["searchFormat"];
string searchQuery = ConfigurationManager.AppSettings["searchQuery"];
SearchSettings = new SearchSettings
{
SearchFormat = searchFormat,
SearchQuery = searchQuery
};
}
/// <summary>
/// This allows the authentications settings to be passed in
/// </summary>
/// <param name="authenticateSettings"></param>
public OAuthTwitterWrapper(IAuthenticateSettings authenticateSettings)
{
AuthenticateSettings = authenticateSettings;
}
/// <summary>
/// This allows the authentications and timeline settings to be passed in
/// </summary>
/// <param name="authenticateSettings"></param>
/// <param name="timeLineSettings"></param>
public OAuthTwitterWrapper(IAuthenticateSettings authenticateSettings, ITimeLineSettings timeLineSettings)
{
AuthenticateSettings = authenticateSettings;
TimeLineSettings = timeLineSettings;
}
/// <summary>
/// This allows the authentications, timeline and search settings to be passed in
/// </summary>
/// <param name="authenticateSettings"></param>
/// <param name="timeLineSettings"></param>
/// <param name="searchSettings"></param>
public OAuthTwitterWrapper(IAuthenticateSettings authenticateSettings, ITimeLineSettings timeLineSettings, ISearchSettings searchSettings)
{
AuthenticateSettings = authenticateSettings;
TimeLineSettings = timeLineSettings;
SearchSettings = searchSettings;
}
public string GetMyTimeline()
{
var timeLineJson = string.Empty;
IAuthenticate authenticate = new Authenticate();
AuthResponse twitAuthResponse = authenticate.AuthenticateMe(AuthenticateSettings);
// Do the timeline
var utility = new Utility();
timeLineJson = utility.RequstJson(TimeLineSettings.TimelineUrl, twitAuthResponse.TokenType, twitAuthResponse.AccessToken);
return timeLineJson;
}
public string GetSearch()
{
var searchJson = string.Empty;
IAuthenticate authenticate = new Authenticate();
AuthResponse twitAuthResponse = authenticate.AuthenticateMe(AuthenticateSettings);
// Do the timeline
var utility = new Utility();
searchJson = utility.RequstJson(SearchSettings.SearchUrl, twitAuthResponse.TokenType, twitAuthResponse.AccessToken);
return searchJson;
}
}
}
If anyone is having the same issue...
I labeled the default parameterless constructor with
namespace OAuthTwitterWrapper
{
public class OAuthTwitterWrapper : IOAuthTwitterWrapper
{
public IAuthenticateSettings AuthenticateSettings { get; set; }
public ITimeLineSettings TimeLineSettings { get; set; }
public ISearchSettings SearchSettings { get; set; }
[InjectConstructor]
public OAuthTwitterWrapper()
{
string oAuthConsumerKey = ConfigurationManager.AppSettings["oAuthConsumerKey"];
string oAuthConsumerSecret = ConfigurationManager.AppSettings["oAuthConsumerSecret"];
string oAuthUrl = ConfigurationManager.AppSettings["oAuthUrl"];
AuthenticateSettings = new AuthenticateSettings { OAuthConsumerKey = oAuthConsumerKey, OAuthConsumerSecret = oAuthConsumerSecret, OAuthUrl = oAuthUrl };
string screenname = ConfigurationManager.AppSettings["screenname"];
string include_rts = ConfigurationManager.AppSettings["include_rts"];
string exclude_replies = ConfigurationManager.AppSettings["exclude_replies"];
int count = Convert.ToInt16(ConfigurationManager.AppSettings["count"]);
string timelineFormat = ConfigurationManager.AppSettings["timelineFormat"];
TimeLineSettings = new TimeLineSettings
{
ScreenName = screenname,
IncludeRts = include_rts,
ExcludeReplies = exclude_replies,
Count = count,
TimelineFormat = timelineFormat
};
string searchFormat = ConfigurationManager.AppSettings["searchFormat"];
string searchQuery = ConfigurationManager.AppSettings["searchQuery"];
SearchSettings = new SearchSettings
{
SearchFormat = searchFormat,
SearchQuery = searchQuery
};
after making a reference to Unity(my IoC container).
Related
I've created an ASP.NET Core API solution that uses RestSharp to send data to a third party API and return results back to my API and database. On my API, I want to return the results of the RestSharp call as part of my controller class so that my DNN site can consume the RestSharp Responses. How do I return the responses obtained from the third party API in my controller class?
My RestSharp logic is as follows:
/// <summary>
/// Executes a particular http request to a resource.
/// </summary>
/// <typeparam name="T">The response type.</typeparam>
/// <param name="request">The REST request.</param>
/// <param name="baseUrl">The base URL.</param>
/// <returns>Returns a response of the type parameter.</returns>
private static T Execute<T>(IRestRequest request, string baseUrl) where T : class, new()
{
baseUrl = "https://xmltest.propay.com/api/propayapi/PropayMerchantService.svc/";
var client = new RestClient(baseUrl);
var response = client.Execute<T>(request);
if (response.ErrorException != null)
{
Console.WriteLine(
"Error: Exception: {0}, Headers: {1}, Content: {2}, Status Code: {3}",
response.ErrorException,
response.Headers,
response.Content,
response.StatusCode);
}
return response.Data;
}
public static ProPayResponse MerchantSignUpForProPay()
{
var baseUrl = "https://xmltest.propay.com/api/propayapi/PropayMerchantService.svc/";
var request = BuildMerchantTestData();
var restRequest = CreateRestRequest("/Signup", Method.PUT);
restRequest.AddJsonBody(request);
return Execute<ProPayResponse>(restRequest, baseUrl);
}
/// <summary>
/// Builds the merchant request data.
/// </summary>
/// <returns>The request data.</returns>
private static SignUpRequest BuildMerchantTestData()
{
var onboarding = new Onboarding();
var signUpRequest = new SignUpRequest();
var userid = "userId";
var email = userid + "#test.com";
using (SOBOContext context = new SOBOContext())
{
var signupRequest = new SignUpRequest
{
SignupAccountData = new SignupAccountData
{
ExternalId = "12345",
Tier = onboarding.AverageTicket.ToString(),
CurrencyCode = "USD",
PhonePIN = onboarding.PhonePin,
UserId = onboarding.UserName
},
Address =
new Address
{
Address1 = onboarding.Address1Line1,
Address2 = onboarding.Address1Line1,
ApartmentNumber = " ",
City = onboarding.Address1City,
State = onboarding.Address1State,
Country = onboarding.Address1Country,
Zip = onboarding.Address1ZipCode
},
BusinessAddress =
new Address
{
Address1 = onboarding.BusinessAddressLine1,
Address2 = onboarding.BusinessAddressLine2,
ApartmentNumber = " ",
City = onboarding.BusinessCity,
State = onboarding.BusinessState,
Country = onboarding.BusinessCountry,
Zip = onboarding.BusinessZipCode
},
MailAddress = new Address { Address1 = onboarding.OwnerAddress, City = onboarding.OwnerCity, State = onboarding.OwnerRegion, Country = onboarding.OwnerCountry, Zip = onboarding.OwnerZipCode },
BankAccount =
new BankAccount
{
AccountCountryCode = onboarding.BankAccount1CountryCode,
AccountType = onboarding.BankAccount1Type,
AccountOwnershipType = onboarding.BankAccount1OwnershipType,
BankAccountNumber = onboarding.BankAccount1Number,
BankName = onboarding.BankAccount1BankName,
RoutingNumber = onboarding.BankAccount1RoutingNumber
},
SecondaryBankAccount =
new BankAccount
{
AccountCountryCode = onboarding.BankAccount2CountryCode,
AccountType = onboarding.BankAccount2Type,
AccountOwnershipType = onboarding.BankAccount2OwnershipType,
BankAccountNumber = onboarding.BankAccount2Number,
BankName = onboarding.BankAccount2BankName,
RoutingNumber = onboarding.BankAccount2RoutingNumber
},
BusinessData =
new BusinessData
{
BusinessLegalName = onboarding.BusinessLegalName,
DoingBusinessAs = onboarding.DoingBusinessAs,
EIN = onboarding.Ein,
},
CreditCardData = new CreditCardData
{
CreditCardNumber = onboarding.CreditCardNumber, // test card number
ExpirationDate = onboarding.ExpirationDate
},
PersonalData =
new PersonalData
{
DateOfBirth = onboarding.DateOfBirth.ToString(),
SourceEmail = onboarding.Email,
SocialSecurityNumber = onboarding.Ssn,
FirstName = onboarding.FirstName,
LastName = onboarding.Lastname,
MiddleInitial = onboarding.MiddleInitial,
PhoneInformation =
new PhoneInformation { DayPhone = onboarding.DayPhone, EveningPhone = onboarding.EveningPhone }
}
};
context.SaveChangesAsync();
return signupRequest;
}
}
/// <summary>
/// Request factory to ensure API key is always first parameter added.
/// </summary>
/// <param name="resource">The resource name.</param>
/// <param name="method">The HTTP method.</param>
/// <returns>Returns a new <see cref="RestRequest"/>.</returns>
private static RestRequest CreateRestRequest(string resource, Method method)
{
var credentials = GetCredentials();
var restRequest = new RestRequest { Resource = resource, Method = method, RequestFormat = DataFormat.Json, };
restRequest.AddHeader("accept", "application/json");
restRequest.AddHeader("Authorization", credentials);
return restRequest;
}
My controller class thus far is as follows:
public async Task<IActionResult> GetMerchantSignUp()
{
await _context.ProPayResponse.ToListAsync();
return Ok();
}
Are there any other steps needed to retrieve the RestSharp responses and expose them in my controller class?
I am calling a 3rd party API which provided me with a PHP code example on how to call their API. I do not know PHP but it looks pretty straight forward and readable for me to try and build a similar .NET solution. The PHP example below is doing a Post but in my C# example I will be doing a Get. I have mocked up the PHP example with some dummy values.
$params = array();
$params['assertion_id'] = "b57936e4-6cea-46f4-a897-c7b3a6f01147"; // Example Assertion ID.
$params['product_id'] = "product001";
$params['product_name'] = "Useless Product";
$params['price'] = "1.99";
// Generate Signature Base String.
$params['api_key'] = "526587410g44p9kk8f7h2bb2zf3365va"; // Example Api Key.
$params['timestamp'] = "1510760624"; // Example Timestamp.
$params['nonce'] = "uilp37xh"; // Example Nonce.
ksort($params);
$url = "https://someurl.com/api/product";
$signature_base_string = 'POST&'.urlencode($url).'&'.http_build_query($params);
// Encrypt signature base string for signature.
$secret_key = "DlKST1adTpoWELS8TjjBc1pFATdlGA8qHUNEaq9MOSAUT648AlAvzK4EEC7="; // Example Secret Key.
$signature = base64_encode(hash_hmac('sha1', $signature_base_string, base64_decode($secret_key), true));
$params['signature'] = $signature;
// Send Request.
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, count($params));
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
Here is the C# code example that I have constructed thus far utilizing RestSharp. The nonce, timestamp, and signature adhere to the OAuth 1.0 specification but I am not sure that I have handled that part correctly? I cannot provide the real values to actually test the Api call but know the code does work in the sense that it does get a response back from the 3rd party Api. However, the response states that the parameters are missing...
public class ProductApiRequest
{
private string _api;
private string _apiKey;
private string _apiSecretKey;
public ProductApiRequest()
{
_api = "https://someurl.com/api/";
_apiKey = "526587410g44p9kk8f7h2bb2zf3365va";
_apiSecretKey = "DlKST1adTpoWELS8TjjBc1pFATdlGA8qHUNEaq9MOSAUT648AlAvzK4EEC7=";
}
public class Product
{
public Guid assertion_id { get; set; }
public String api_key { get; set; }
public string product_id { get; set; }
public string product_name { get; set; }
public string price { get; set; }
public string timestamp { get; set; }
public string nonce { get; set; }
public string signature { get; set; }
}
public string getProduct()
{
try
{
Product oProduct = new Product();
oProduct.assertion_id = Guid.NewGuid();
oProduct.api_key = _apiKey;
oProduct.product_id = "product001";
oProduct.product_name = "Useless Product";
oProduct.price = "1.99";
oProduct.timestamp = GenerateTimeStamp();
oProduct.nonce = GenerateNonce();
oProduct.signature = GenerateSignature(oProduct.nonce, oProduct.timestamp, _apiSecretKey);
var path = "product";
dynamic request = new RestSharp.RestRequest(path, RestSharp.Method.GET);
request.RequestFormat = RestSharp.DataFormat.Json;
request.JsonSerializer = new RestSharp.Serializers.JsonSerializer();
request.AddJsonBody(oProduct);
var client = new RestSharp.RestClient(_api);
var response = client.Execute(request);
return "Return something.";
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
public string GenerateTimeStamp()
{
TimeSpan ts = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0));
string timeStamp = ts.TotalSeconds.ToString();
timeStamp = timeStamp.Substring(0, timeStamp.IndexOf("."));
return timeStamp;
}
public string GenerateNonce()
{
var _random = new Random();
var sb = new StringBuilder();
for (int i = 0; i < 8; i++)
{
int g = _random.Next(3);
switch (g)
{
case 0:
// lowercase alpha
sb.Append((char)(_random.Next(26) + 97), 1);
break;
default:
// numeric digits
sb.Append((char)(_random.Next(10) + 48), 1);
break;
}
}
return sb.ToString();
}
public string GenerateSignature(string nonce, string timestamp, string apisecretkey)
{
var encoder = new UTF8Encoding();
var hasher = new System.Security.Cryptography.HMACSHA1();
var vals = string.Concat(nonce, timestamp, apisecretkey);
byte[] hashedDataBytes = hasher.ComputeHash(encoder.GetBytes(vals));
return Convert.ToBase64String(hashedDataBytes);
}
}
Any help converting the PHP solution to a C# solution would be appreciated!
I did not know PHP but have managed to figured out the solution! \m/ Metal \m/
public class ProductApiRequest
{
private string _api;
private string _apiKey;
private string _apiSecretKey;
private string _resource;
public ProductApiRequest()
{
_api = "https://someurl.com/api/";
_apiKey = "526587410g44p9kk8f7h2bb2zf3365va";
_apiSecretKey = "DlKST1adTpoWELS8TjjBc1pFATdlGA8qHUNEaq9MOSAUT648AlAvzK4EEC7=";
_resource = "products";
}
public class Product
{
public Guid assertion_id { get; set; }
public String api_key { get; set; }
public string product_id { get; set; }
public string product_name { get; set; }
public string price { get; set; }
public string timestamp { get; set; }
public string nonce { get; set; }
public string signature { get; set; }
}
public string getProduct()
{
Product oProduct = new Product();
oProduct.assertion_id = Guid.NewGuid();
oProduct.api_key = _apiKey;
oProduct.product_id = "product001";
oProduct.product_name = "Useless Product";
oProduct.price = "1.99";
oProduct.timestamp = GenerateTimeStamp();
oProduct.nonce = GenerateNonce();
// Create Signature Base String.
string apiEncoded = Uri.EscapeDataString(_api + _resource);
string strSignatureBase = "GET&" + apiEncoded + "&api_key=" + oProduct.api_key + "&product_id=" + oProduct.product_id + "&product_name=" + oProduct.product_name + "&price=" + oProduct.price + "&nonce=" + oProduct.nonce + "×tamp=" + oProduct.timestamp;
// Create Signature for OAuth 1.0
oProduct.signature = GenerateSignature(strSignatureBase, _apiSecretKey);
var client = new RestClient(_api + _resource + "?assertion_id=" + oProduct.assertion_id + "&api_key=" + oProduct.api_key + "&product_id=" + oProduct.product_id + "&product_name=" + oProduct.product_name + "&price=" + oProduct.price + "×tamp=" + oProduct.timestamp + "&nonce=" + oProduct.nonce + "&signature=" + HttpUtility.UrlEncode(oProduct.signature));
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
return "Return whatever you want.";
}
public string GenerateTimeStamp()
{
TimeSpan ts = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0));
string timeStamp = ts.TotalSeconds.ToString();
timeStamp = timeStamp.Substring(0, timeStamp.IndexOf("."));
return timeStamp;
}
public string GenerateNonce()
{
var _random = new Random();
var sb = new StringBuilder();
for (int i = 0; i < 8; i++)
{
int g = _random.Next(3);
switch (g)
{
case 0:
// lowercase alpha
sb.Append((char)(_random.Next(26) + 97), 1);
break;
default:
// numeric digits
sb.Append((char)(_random.Next(10) + 48), 1);
break;
}
}
return sb.ToString();
}
public string GenerateSignature(string strSignatureBase, string strSecretKey)
{
byte[] signatureBaseBytes = Encoding.UTF8.GetBytes(strSignatureBase);
byte[] secretKeyDecodedBytes = Convert.FromBase64String(strSecretKey); // Decode Secret Key.
var encoder = new UTF8Encoding();
var hasher = new HMACSHA1(secretKeyDecodedBytes);
byte[] hashedDataBytes = hasher.ComputeHash(signatureBaseBytes);
return Convert.ToBase64String(hashedDataBytes);
}
}
I'm working on xamarin solution for my Github repository. For the Pins project, I'm facing a problem. There is the thing, if you create a pin (aka CustomPin()), and then, you want to edit the location. Then the address name will change and same for the location if you change the address, the location will be create based on the address name. So from here, it's easy.
However, when the Pin address/location is changing, I would like my Map update itself. But because the List<> property doesn't changes, it doesn't update the map.
So, get a pointer to your parent list or to the map?
But this solution doesn't seems properly good for my use, I think that maybe, another best solution exist, but I don't know how to do..
You can compile the project, but there is three updates.
CustomPin
public class CustomPin : BindableObject
{
public static readonly BindableProperty AddressProperty =
BindableProperty.Create(nameof(Address), typeof(string), typeof(CustomPin), "",
propertyChanged: OnAddressPropertyChanged);
public string Address
{
get { return (string)GetValue(AddressProperty); }
set { SetValue(AddressProperty, value); }
}
private static async void OnAddressPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as CustomPin).SetValue(LocationProperty, await CustomMap.GetAddressPosition(newValue as string));
}
public static readonly BindableProperty LocationProperty =
BindableProperty.Create(nameof(Location), typeof(Position), typeof(CustomPin), new Position(),
propertyChanged: OnLocationPropertyChanged);
public Position Location
{
get { return (Position)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static async void OnLocationPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as CustomPin).SetValue(AddressProperty, await CustomMap.GetAddressName((Position)newValue));
Debug.WriteLine("private static async void OnLocationPropertyChanged(BindableObject bindable, object oldValue, object newValue)");
}
public string Name { get; set; }
public string Details { get; set; }
public string ImagePath { get; set; }
public uint PinSize { get; set; }
public uint PinZoomVisibilityMinimumLimit { get; set; }
public uint PinZoomVisibilityMaximumLimit { get; set; }
public Point AnchorPoint { get; set; }
public Action<CustomPin> PinClickedCallback { get; set; }
public CustomPin(Position location)
{
Location = location;
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
public CustomPin(string address)
{
Address = address;
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
public CustomPin()
{
Address = "";
Location = new Position();
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
}
CustomMap | PS: Just add these three methods
#region
public async static Task<string> GetAddressName(Position position)
{
string url = "https://maps.googleapis.com/maps/api/geocode/json";
string additionnal_URL = "?latlng=" + position.Latitude + "," + position.Longitude
+ "&key=" + App.GOOGLE_MAP_API_KEY;
JObject obj = await CustomMap.GoogleAPIHttpRequest(url, additionnal_URL);
string address_name;
try
{
address_name = (obj["results"][0]["formatted_address"]).ToString();
}
catch (Exception)
{
return ("");
}
return (address_name);
}
public async static Task<Position> GetAddressPosition(string name)
{
string url = "https://maps.googleapis.com/maps/api/geocode/json";
string additionnal_URL = "?address=" + name
+ "&key=" + App.GOOGLE_MAP_API_KEY;
JObject obj = await CustomMap.GoogleAPIHttpRequest(url, additionnal_URL);
Position position;
try
{
position = new Position(Double.Parse((obj["results"][0]["geometry"]["location"]["lat"]).ToString()),
Double.Parse((obj["results"][0]["geometry"]["location"]["lng"]).ToString()));
}
catch (Exception)
{
position = new Position();
}
return (position);
}
private static async Task<JObject> GoogleAPIHttpRequest(string url, string additionnal_URL)
{
try
{
var client = new HttpClient();
client.BaseAddress = new Uri(url);
var content = new StringContent("{}", Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
try
{
response = await client.PostAsync(additionnal_URL, content);
}
catch (Exception)
{
return (null);
}
string result = await response.Content.ReadAsStringAsync();
if (result != null)
{
try
{
return JObject.Parse(result);
}
catch (Exception)
{
return (null);
}
}
else
{
return (null);
}
}
catch (Exception)
{
return (null);
}
}
#endregion
MainPage.xaml.cs | PS: PCL part, just change the Constructor
public MainPage()
{
base.BindingContext = this;
CustomPins = new List<CustomPin>()
{
new CustomPin("Long Beach") { Name = "Le Mans", Details = "Famous city for race driver !", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 0, PinZoomVisibilityMaximumLimit = 150, PinSize = 75},
new CustomPin() { Name = "Ruaudin", Details = "Where I'm coming from.", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 75, PinSize = 65 },
new CustomPin() { Name = "Chelles", Details = "Someone there.", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 50, PinSize = 70 },
new CustomPin() { Name = "Lille", Details = "Le nord..", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 44, PinSize = 40 },
new CustomPin() { Name = "Limoges", Details = "I have been there ! :o", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 65, PinSize = 20 },
new CustomPin() { Name = "Douarnenez", Details = "A trip..", ImagePath = "CustomIconImage.png", PinZoomVisibilityMinimumLimit = 110, PinSize = 50 }
};
Debug.WriteLine("Initialization done.");
PinActionClicked = PinClickedCallback;
PinsSize = Convert.ToUInt32(100);
MinValue = 50;
MaxValue = 100;
InitializeComponent();
Debug.WriteLine("Components done.");
}
Maybe it's easy or maybe the way I said it's the only one, but I don't know how to update the pin on the map if a pin got edited, because finaly, it's still the same object, so the list doesn't change...
Thank for help !
Edit 1
Ok so, I made some changes however, it still doesn't work.. I mean my code work as I want, but calling PropertyChanged doesn't change anything...
I changed some things like List<CustomPin> is now ObservableCollection<CustomPin>.. Also I changed a bit the xaml part to that:
<control:CustomMap x:Name="MapTest" CustomPins="{Binding CustomPins}" CameraFocusParameter="OnPins"
PinSize="{Binding PinsSize, Converter={StaticResource Uint}}"
PinClickedCallback="{Binding PinActionClicked}"
VerticalOptions="Fill" HorizontalOptions="Fill"/>
And my CustomPin is now like that:
public class CustomPin : BindableObject, INotifyPropertyChanged
{
/// <summary>
/// Handler for event of updating or changing the
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public static readonly BindableProperty AddressProperty =
BindableProperty.Create(nameof(Address), typeof(string), typeof(CustomPin), "",
propertyChanged: OnAddressPropertyChanged);
public string Address
{
get { return (string)GetValue(AddressProperty); }
set { SetValue(AddressProperty, value); }
}
private static void OnAddressPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as CustomPin).SetAddress(newValue as string);
Debug.WriteLine("Address property changed");
}
private async void SetAddress(string address)
{
if (setter == SetFrom.None)
{
setter = SetFrom.Address;
SetLocation(await CustomMap.GetAddressPosition(address));
setter = SetFrom.None;
NotifyChanges();
}
else if (setter == SetFrom.Location)
{
setter = SetFrom.Done;
SetValue(AddressProperty, address);
}
}
private enum SetFrom
{
Address,
Done,
Location,
None,
}
private SetFrom setter;
private async void SetLocation(Position location)
{
if (setter == SetFrom.None)
{
setter = SetFrom.Location;
SetAddress(await CustomMap.GetAddressName(location));
setter = SetFrom.None;
NotifyChanges();
}
else if (setter == SetFrom.Address)
{
setter = SetFrom.Done;
SetValue(LocationProperty, location);
}
}
public static readonly BindableProperty LocationProperty =
BindableProperty.Create(nameof(Location), typeof(Position), typeof(CustomPin), new Position(),
propertyChanged: OnLocationPropertyChanged);
public Position Location
{
get { return (Position)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static async void OnLocationPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as CustomPin).SetLocation((Position)newValue);
Debug.WriteLine("Location property changed");
}
private void NotifyChanges()
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Address)));
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Location)));
}
public string Name { get; set; }
public string Details { get; set; }
public string ImagePath { get; set; }
public uint PinSize { get; set; }
public uint PinZoomVisibilityMinimumLimit { get; set; }
public uint PinZoomVisibilityMaximumLimit { get; set; }
public Point AnchorPoint { get; set; }
public Action<CustomPin> PinClickedCallback { get; set; }
public CustomPin(Position location)
{
setter = SetFrom.None;
Location = location;
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
public CustomPin(string address)
{
setter = SetFrom.None;
Address = address;
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
public CustomPin()
{
setter = SetFrom.None;
Address = "";
Location = new Position();
Name = "";
Details = "";
ImagePath = "";
PinSize = 50;
PinZoomVisibilityMinimumLimit = uint.MinValue;
PinZoomVisibilityMaximumLimit = uint.MaxValue;
AnchorPoint = new Point(0.5, 1);
PinClickedCallback = null;
}
}
Finally, a call of PropertyChanged doesn't do anything.. Any idea?
Thank !
PS: Do not forget that the solution is available on my github repository
You should use INotifyPropertyChanged in your Pin implementation. This way, when you update some parameters, you notify the changes and can update the map.
Faced a similar issue a while ago, what solved it for me was changing the binding in the xaml from this:
CustomPins="{Binding CustomPins}"
to this:
CustomPins="{Binding CustomPins, Mode=TwoWay}"
I finally had an idea ! In Xamarin Forms, App can be acceeded from anywhere so, what I did is:
Create a method in your MainPage.xaml.cs which invoke a PropertyChanged event. You can do something like that so:
public void PinsCollectionChanged()
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomPins)));
Debug.WriteLine("Updated !!!");
}
Then, from your item (For me it's a CustomPin object) of your list, call this method by getting the current instance of the application. Take a look at the code to understand:
private void NotifyChanges()
{
(App.Current.MainPage as MainPage).PinsCollectionChanged();
}
PS: Do not forget to add using MapPinsProject.Page; in your object.
Hope it helps !
I want to have a class with several nested classes, so that when I create a new parent class, an object of each nested class is created and I can reference the variables within each nested class globally.
Here is my current code:
public class StockChecklist
{
public class qty1p1 { public string tag = "uniqueval23456"; public string value = ""; public string reference = ""; }
public class qty1p2 { public string tag = "uniqueval3736"; public string value = ""; public string reference = ""; }
public class qty2 { public string tag = "uniqueval97357"; public string value = ""; public string reference = ""; }
public class qty3p1 { public string tag = "uniqueval88356"; public string value = ""; public string reference = ""; }
public class qty3p2 { public string tag = "uniqueval62346"; public string value = ""; public string reference = ""; }
public class qty3p3 { public string tag = "uniqueval09876"; public string value = ""; public string reference = ""; }
public class qty3p4 { public string tag = "uniqueval62156"; public string value = ""; public string reference = ""; }
public class qty4 { public string tag = "uniqueval25326"; public string value = ""; public string reference = ""; }
}
then I create a new parent object with:
StockChecklist theCurrentList = new StockChecklist();
but how do access the nested objects 'tag', 'value' and 'reference'? I was hoping for something simple like StockChecklist.qty1p1.tag = 'changedval999999999';
Is something like this possible with C#?
You mixed up definition and declaration. Defining a nested class doesn't create an instance. Also the classes you define looks like they all use the same properties. So, you should define one class and declare multiple instances.
You can fix this with:
C# 6.0
public class Info
{
public string tag { get; set; }
public string value { get; set; }
public string reference { get; set; }
}
public class StockChecklist
{
public Info qty1p1 { get; } = new Info { tag = "uniqueval23456", value = "", reference = "" };
public Info qty1p2 { get; } = new Info { tag = "uniqueval3736", value = "", reference = "" };
public Info qty2 { get; } = new Info { tag = "uniqueval97357", value = "", reference = "" };
public Info qty3p1 { get; } = new Info { tag = "uniqueval88356", value = "", reference = "" };
public Info qty3p2 { get; } = new Info { tag = "uniqueval62346", value = "", reference = "" };
public Info qty3p3 { get; } = new Info { tag = "uniqueval09876", value = "", reference = "" };
public Info qty3p4 { get; } = new Info { tag = "uniqueval62156", value = "", reference = "" };
public Info qty4 { get; } = new Info { tag = "uniqueval25326", value = "", reference = "" };
}
Pre C# 6.0 you have to create the instances in the constructor.
public class StockChecklist
{
public StockChecklist()
{
qty1p1 = new Info { tag = "uniqueval23456", value = "", reference = "" };
qty1p2 = new Info { tag = "uniqueval3736", value = "", reference = "" };
qty2 = new Info { tag = "uniqueval97357", value = "", reference = "" };
qty3p1 = new Info { tag = "uniqueval88356", value = "", reference = "" };
qty3p2 = new Info { tag = "uniqueval62346", value = "", reference = "" };
qty3p3 = new Info { tag = "uniqueval09876", value = "", reference = "" };
qty3p4 = new Info { tag = "uniqueval62156", value = "", reference = "" };
qty4 = new Info { tag = "uniqueval25326", value = "", reference = "" };
}
public Info qty1p1 { get; private set; }
public Info qty1p2 { get; private set; }
public Info qty2 { get; private set; }
public Info qty3p1 { get; private set; }
public Info qty3p2 { get; private set; }
public Info qty3p3 { get; private set; }
public Info qty3p4 { get; private set; }
public Info qty4 { get; private set; }
}
note: Like some comments already noted, declaring 8 instances of the same class within a class could point on 'poor' design. You could create a Dictionary<> for it.
Here is a dictionary version: (bonus)
public class Info
{
public string tag { get; set; }
public string value { get; set; }
public string reference { get; set; }
}
public class StockChecklist
{
private Dictionary<string, Info> _infoDict = new Dictionary<string, Info>();
private void AddToDict(Info info)
{
_infoDict.Add(info.tag, info);
}
public StockChecklist2()
{
AddToDict(new Info { tag = "uniqueval23456", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval3736", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval97357", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval88356", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval62346", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval09876", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval62156", value = "", reference = "" });
AddToDict(new Info { tag = "uniqueval25326", value = "", reference = "" });
}
public bool TryGetByTag(string tag, out Info info)
{
return _infoDict.TryGetValue(tag, out info);
}
public Info this[string tag]
{
get
{
Info info;
if (!_infoDict.TryGetValue(tag, out info))
return null;
return info;
}
}
}
Use it like: (C# 6.0)
StockChecklist stock = new StockChecklist();
Info info;
if (stock.TryGetByTag("uniqueval23456", out info))
{
Trace.WriteLine($"{info.tag} = {info.value}");
}
Or (C# 6.0)
Trace.WriteLine(stock["uniqueval88356"]?.value);
You should do something like this:
public class qty1p1 { public string tag = "uniqueval23456"; public string value = ""; public string reference = ""; }
public class qty1p2 { public string tag = "uniqueval3736"; public string value = ""; public string reference = ""; }
public class qty2 { public string tag = "uniqueval97357"; public string value = ""; public string reference = ""; }
public class qty3p1 { public string tag = "uniqueval88356"; public string value = ""; public string reference = ""; }
public class qty3p2 { public string tag = "uniqueval62346"; public string value = ""; public string reference = ""; }
public class qty3p3 { public string tag = "uniqueval09876"; public string value = ""; public string reference = ""; }
public class qty3p4 { public string tag = "uniqueval62156"; public string value = ""; public string reference = ""; }
public class qty4 { public string tag = "uniqueval25326"; public string value = ""; public string reference = ""; }
public class StockChecklist
{
public qty1p1 _qty1p1;
public qty1p2 _qty1p2;
.
.
.
}
and Then you can use it like:
StockChecklist theCurrentList = new StockChecklist();
theCurrentList._qty1p1.tag = 'changedval999999999';
You can do like this:
public class Parent
{
public class child1 { public string name = "a"; public int Value = 1;}
public class child2 { public string name = "b"; public int Value = 2;}
public class child3 { public string name = "c"; public int Value = 3;}
public class child4 { public string name = "d"; public int Value = 4;}
public class child5 { public string name = "e"; public int Value = 5;}
public child1 c1;
public child2 c2;
public child3 c3;
public child4 c4;
public child5 c5;
public Parent() {
this.c1 = new child1();
this.c2 = new child2();
this.c3 = new child3();
this.c4 = new child4();
this.c5 = new child5();
}
}
class Program
{
static void Main(string[] args)
{
Parent p1 = new Parent();
Console.WriteLine(p1.c1.name);
Console.WriteLine(p1.c2.name);
Console.WriteLine(p1.c3.name);
Console.WriteLine(p1.c4.name);
Console.WriteLine(p1.c5.name);
Console.ReadLine();
}
I have the following function which is accessible from one of my asp.net page:
/* QUERY TO RUN FROM ANY FUNCTION */
public void runQuery()
{
strMainSql = #"SELECT
CT.OBJECTID 'Object ID'
FROM HSI.RMOBJECTINSTANCE1224 CT
WHERE CT.ACTIVESTATUS = 0 AND CT.OBJECTID = '" + s + "'";
using (SqlConnection scConn = new SqlConnection(strConn))
{
scConn.Open();
using (SqlCommand scComm = new SqlCommand(strMainSql, scConn))
{
sdrRead = scComm.ExecuteReader();
while (sdrRead.Read())
{
/* CAN BE USED IN OTHER PAGES */
strTaskName = sdrRead[1].ToString();
strTaskDetail = sdrRead[2].ToString();
strTaskStartDate = sdrRead[3].ToString();
strIdentifier = sdrRead[4].ToString();
strStatus = sdrRead[5].ToString();
strDueDate = sdrRead[6].ToString();
strIssueDate = sdrRead[7].ToString();
strCompleted = sdrRead[8].ToString();
strNotes = sdrRead[9].ToString();
strProvider = sdrRead[10].ToString();
strService = sdrRead[11].ToString();
strCheckedDate = sdrRead[12].ToString();
strCheckedStatus = sdrRead[13].ToString();
strCheckedUser = sdrRead[14].ToString();
strClient = sdrRead[15].ToString();
hfMemoID.Value = sdrRead[16].ToString();
hfObjectID.Value = sdrRead[0].ToString();
break;
}
}
}
/* SPECIFIC TO THE PAGE ONLY */
lblUser.Text = strCheckedUser;
lblDateTime.Text = strCheckedDate;
lblTaskName.Text = strTaskName;
lblClient.Text = strClient;
lblID.Text = strIdentifier;
lblSvc.Text = strService;
lblProvider.Text = strProvider;
lblStat.Text = strStatus;
lblDueDate.Text = strDueDate;
lblDetail.Text = strTaskDetail;
lblTaskIssue.Text = strIssueDate;
lblStartDate.Text = strTaskStartDate;
lblCompleted.Text = strCompleted;
}
The question I have is, if I have to use the above function in multiple pages, instead of having a multiple copies of the same function which might lead to issue later on when updating, how do I make it into a class by itself so I can call it from any page and get the value from the SQL query?
What you can do is expose the results from the query as properties and then use the properties in the ASPX page.
using System.Data.SqlClient;
namespace MyNamespace
{
public class Task
{
public string strTaskName { get; set; }
public string strTaskDetail { get; set; }
public string strTaskStartDate { get; set; }
public string strIdentifier { get; set; }
public string strStatus { get; set; }
public string strDueDate { get; set; }
public string strIssueDate { get; set; }
public string strCompleted { get; set; }
public string strNotes { get; set; }
public string strProvider { get; set; }
public string strService { get; set; }
public string strCheckedDate { get; set; }
public string strCheckedStatus { get; set; }
public string strCheckedUser { get; set; }
public string strClient { get; set; }
// you need to define properties for the appropriate datatype on these
//hfMemoID
//hfObjectID
public string strConn { get; set; }
public void Load(string objectid)
{
var strMainSql = #"SELECT
CT.OBJECTID 'Object ID'
FROM HSI.RMOBJECTINSTANCE1224 CT
WHERE CT.ACTIVESTATUS = 0 AND CT.OBJECTID = '" + objectid + "'";
using (SqlConnection scConn = new SqlConnection(strConn))
{
scConn.Open();
using (SqlCommand scComm = new SqlCommand(strMainSql, scConn))
{
var sdrRead = scComm.ExecuteReader();
while (sdrRead.Read())
{
/* CAN BE USED IN OTHER PAGES */
this.strTaskName = sdrRead[1].ToString();
this.strTaskDetail = sdrRead[2].ToString();
this.strTaskStartDate = sdrRead[3].ToString();
this.strIdentifier = sdrRead[4].ToString();
this.strStatus = sdrRead[5].ToString();
this.strDueDate = sdrRead[6].ToString();
this.strIssueDate = sdrRead[7].ToString();
this.strCompleted = sdrRead[8].ToString();
this.strNotes = sdrRead[9].ToString();
this.strProvider = sdrRead[10].ToString();
this.strService = sdrRead[11].ToString();
this.strCheckedDate = sdrRead[12].ToString();
this.strCheckedStatus = sdrRead[13].ToString();
this.strCheckedUser = sdrRead[14].ToString();
this.strClient = sdrRead[15].ToString();
//
//hfMemoID.Value = sdrRead[16].ToString();
//hfObjectID.Value = sdrRead[0].ToString();
break;
}
}
}
}
}
}
In the code behind use the class to load the data and then set the controls using the properties
private MyNamespace.Task Task = new MyNamespace.Task();
protected void Page_Load(object sender, EventArgs e)
{
Task.strConn = "my connection string.";
Task.Load("task id to load");
// set the value into the controls.
lblUser.Text = Task.strCheckedUser;
lblDateTime.Text = Task.strCheckedDate;
lblTaskName.Text = Task.strTaskName;
lblClient.Text = Task.strClient;
lblID.Text = Task.strIdentifier;
lblSvc.Text = Task.strService;
lblProvider.Text = Task.strProvider;
lblStat.Text = Task.strStatus;
lblDueDate.Text = Task.strDueDate;
lblDetail.Text = Task.strTaskDetail;
lblTaskIssue.Text = Task.strIssueDate;
lblStartDate.Text = Task.strTaskStartDate;
lblCompleted.Text = Task.strCompleted;
}