I am currently making a WPF App as a school project. I chose a Media player topic.
I have a slight problem with returning track length of a song and assigning it as a maximum for a slider (I used a slider for song seeking).
/*List<string> pathPesme = new List<string>();
path = od.FileNames;
for (int i = 0; i < path.Length; i++)
{
lista.Add(path[i]);
m.CreateControl();
media = this.m.newMedia(path[i]);*/
//When i want to instance a new class Pesma(song) and put the track length(duzina), it works and returns the value in a mm:ss format.
string ime = System.IO.Path.GetFileName(path[i]);
string duzina = media.durationString;
Pesma pesma = new Pesma(ime, path[i], duzina);
listaPesmi.Add(pesma);
}
However, when i try to do a similar thing and put it as Slider.Maximum, it doesn't work.
foreach (var pesma in dispesma)/**/
{
string aa=iseciString(lbxListaPesama.Items[lbxListaPesama.SelectedIndex].ToString(), ':');
//Method for cutting string to get song name
if (pesma.ImePesme == aa)
{
m.URL = pesma.Path;
premotajSlider.Maximum = Math.Ceiling(m.currentMedia.duration); //Slider: Here i try to put the max value. In the debugger, it returns a 0
m.Ctlcontrols.play();
jacinaZvukaSlider.Value = m.settings.volume;
}
}
It's my first time asking on StackOverflow, so I hope I didn't do something bad. If I did, sorry, still in the learning phase.
Related
I’m trying to show pins on the map but only the ones that can fit the screen around your current location depending on the zoom level I’ve set on when the map appears, because I have 10’s of pins and when I open the map it loads every pin and takes a long time to load.
Any idea on how to do it ?
Method to load pins:
async Task ExecuteLoadPinsCommand()
{
IsBusy = true;
try
{
Map.Pins.Clear();
Map.MapElements.Clear();
Map.CustomPins.Clear();
var contents = await placeRepository.GetAllPlacesWithoutRelatedDataAsync();
if (contents == null || contents.Count < 1)
{
await App.Current.MainPage.DisplayAlert("No places found", "No places have been found for that category, please try again later", "Ok");
await ExecuteLoadPinsCommand();
}
if (contents != null)
{
places.Clear();
var customPins = this.Map.CustomPins;
places = contents;
foreach (var item in places)
{
CustomPin devicePin = new CustomPin
{
Type = PinType.Place,
PlaceId = item.PlaceId.ToString(),
Position = new Position(item.Latitude, item.Longitude),
Label = $"{item.Name}",
Address = $"{item.Name}"
};
Map.CustomPins.Add(devicePin);
Map.Pins.Add(devicePin);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
CustomMapRenderer:
protected override MarkerOptions CreateMarker(Pin pin)
{
CustomPin pin = (CustomPin)pin;
var thePlace = Task.Run(async () => await placeRepository.GetPlaceByIdWithMoodAndEventsAsync(Guid.Parse(pin.PlaceId)));
var place = thePlace.ConfigureAwait(true)
.GetAwaiter()
.GetResult();
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(place.Position.Latitude, place.Position.Longitude));
if (place.Category == "" && place.SomethingCount == 0)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.icon));
}
//else if ...
return marker;
}
A major part of programming is learning to debug well.
When facing a performance problem, it is important to isolate the time delay to the smallest bit of code that you can.
Here's the thought process I would go through:
YOUR OBSERVATION: When there is one pin, it takes less than a second. When there are 20 pins, it takes maybe 10 seconds. (9 second difference.)
YOUR HYPOTHESIS (Given the question you posted): Maybe adding 20 pins to map takes much or most of the 9 seconds.
TESTS: How can we test EXACTLY the code that "adds pins to map"?
A: "Divide and conquer":
Let all the other code run 20 times. That is, have 20 pins as data. BUT suppress the code that adds those pins.
Test #1: Have 20 pins returned by GetAllPlacesWithoutRelatedDataAsync. So all that work is done 20 times.
Comment out JUST the code that ADDS the pins. Make this change:
//Map.CustomPins.Add(devicePin);
//Map.Pins.Add(devicePin);
Result #1: _____ seconds
Its possible that having NO pins allows the map to skip loading some pin-related code. Lets find out how quick it is when we only ADD ONE of the 20 pins.
Test #2: Have 20 pins in the data. BUT only ADD one of them. Make this ONE-LINE change:
foreach (var item in places)
{
CustomPin devicePin = new CustomPin
{
Type = PinType.Place,
PlaceId = item.PlaceId.ToString(),
Position = new Position(item.Latitude, item.Longitude),
Label = $"{item.Name}",
Address = $"{item.Name}"
};
Map.CustomPins.Add(devicePin);
Map.Pins.Add(devicePin);
break; // <--- ADD THIS LINE.
}
Result #2: _____ seconds
(Test #2 is what I was trying to ask you to do, in one of my comments on the question. I've deleted those comments.)
Now we have enough information to determine how much of the ~9 extra seconds are due to going through that foreach loop 20 times, to ADD 20 pins.
This will determine whether there is any point in trying to speed up the ADDS, or whether there is a problem elsewhere.
If most time is spent elsewhere, then you need to add the suspect code to the question. Then do similar tests there. Until you can report exactly what code takes most of the time.
IF 20x map.Pins.Add(..) takes a significant amount of time, THEN here are two techniques, either of which should be faster, imho.
FASTER ADD #1:
Use Map.ItemsSource.
FASTER ADD #2:
Create the map WITH its pins, BEFORE displaying it.
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace XFSOAnswers
{
// Based on https://github.com/xamarin/xamarin-forms-samples/blob/main/WorkingWithMaps/WorkingWithMaps/WorkingWithMaps/PinPageCode.cs
public class PinPageCode : ContentPage
{
public PinPageCode()
{
Title = "Pins demo";
Position position = new Position(36.9628066, -122.0194722);
MapSpan mapSpan = new MapSpan(position, 0.01, 0.01);
Map map = new Map(mapSpan);
Pin pin = new Pin
{
Label = "Santa Cruz",
Address = "The city with a boardwalk",
Type = PinType.Place,
Position = position
};
map.Pins.Add(pin);
// ... more pins.
Content = new StackLayout
{
Margin = new Thickness(10),
Children =
{
map
}
};
}
}
}
So, I want to store the inventory of the player in Firebase Realtime Database.
I set up a basic schema, and I want to add the items (their names, actually) under the inventory "branch".
I am trying to do it with the push method, but it ain't working for me right now.
I got the following script which updates the player's inventory client-side and should update the database to, with appending the inventory "branch".
What am I doing wrong?
public void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(items[i].item_name).Push();
lastIndex = i;
}
}
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
I'm guessing that there's slight confusion around what Push() does in this context. If you read the documentation, it says:
Add to a list of data. Every time you call Push(), Firebase generates a unique key that can also be used as a unique identifier, such as user-scores/<user-id>/<unique-score-id>.
Basically, it's giving you a place you can safely write into rather than actually adding something to the Database. You should check out the sample, but I think I can illustrate both what you intend to do and a cool way to use Push() for your use case.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").SetValueAsync(itemNames);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
What I'm doing here is that I build a list of the items before hand. Then I set the inventory entry to this list. If I depended on the previous state, I'd probably prefer to do this as a transaction instead.
I also use async/await. I'm not sure how important it is that this logic runs before you update your game's UI, but setting anything in Firebase is an asynchronous operation (it runs in the background). You can omit this if it's not important that you stay in sync.
If you wanted to have uniquely identified weapon slots. You could instead do something like:
var childReference = reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory");
var key = childReference.Key;
// ... more logic here ...
childReference.Child(key).SetValueAsync(item_name);
A few more hints:
I'd recommend that you use the ValueChanged listener whenever possible to keep your game state in sync with your server state. Realtime Database will always be running asynchronously in the background, and this way you can help stay up to date as more data becomes available.
If asynchronous programming in Unity is new to you, check out my post on the subject.
Okay, so I've finally figured it out.
With the help of Push() method's unique id generation, I am able to add childs with the same item name to the inventory node.
Firstly, I create that unique id, then, I add that unique id to the database and set it's value to the item name.
Edit: I mixed my original solution with Pux0r3's solution, so that it uses async/await.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if (free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
string key = reference.Child(auth.CurrentUser.UserId).Child("inventory").Push().Key;
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(key).SetValueAsync(currentPickable.item_name);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
I'm having a difficult time figuring out how to properly get distances between two Locations in Xamarin. At least I'm consistently getting the wrong results, according to almighty Google.
This code explains the problem.
public void OnLocationChanged(Location location)
{
// Longitude/Latitude of Tower Bridge.
location.Longitude = 51.5053446;
location.Latitude = -0.0765396;
foreach (var store in this.stores)
{
if (store.DistanceView != null)
{
Location store_loc = new Location(location.Provider);
//store_loc.Longitude = double.Parse(store.GPSN);
//store_loc.Latitude = double.Parse(store.GPSW);
// Longitude/Latitude of Big Ben.
store_loc.Longitude = 51.5005747;
store_loc.Latitude = -0.1247025;
var distance = location.DistanceTo(store_loc);
// Google Maps ("measure distance") says 3.44km.
// Xamarin (variable "distance") says 5351.983 meters.
store.DistanceView.Text = distance.ToString();
}
}
}
The actual locations I'm working with are different (for reasons of privacy), but the measurement error is similar, in that I'm getting a result that's not quite twice as high as measured by Google Maps, but somewhere in the vicinity. At any rate, the above measurements should match, and they don't.
So I need to get the duration of each Video in a Playlist, the problem is that there is no duration included, theoretically I could calculate it with the EndAt and StartAt values but they are no longer filled with data and always will be null (see documentation).
So all of this is done so far with the Google.Apis.YouTube.v3. The code I currently have:
var PlaylistItemRequest = YouTubeService.PlaylistItems.List("snippet,contentDetails");
PlaylistItemRequest.PlaylistId = Find;
PlaylistItemRequest.MaxResults = 50;
List<Song> PlaylistItems = new List<Song>();
while (true)
{
var PlaylistItemResponse = await PlaylistItemRequest.ExecuteAsync();
foreach (var playlistitem in PlaylistItemResponse.Items)
PlaylistItems.Add(new Song(playlistitem.Snippet.Title, playlistitem.ContentDetails.EndAt, playlistitem.ContentDetails.StartAt, $"https://www.youtube.com/watch?v={playlistitem.Id}", playlistitem.Snippet.Thumbnails.Default__.Url, DateTime.UtcNow, null, user.Id));
if (PlaylistItemResponse.NextPageToken == null) break;
PlaylistItemRequest.PageToken = PlaylistItemResponse.NextPageToken;
}
Then I thought I could just do something like that, because this would give me the duration of the video:
var request = YouTubeService.Videos.List("snippet,contentDetails,statistics");
The problem is that I would need to send this for each song in the playlist and I do not want to risk to break the Google API ratelimits. Since 200 requests are a lot.
Any help is appreciated!
Note: I would need a solution with no manual delay if possible.
I've got a very strange issue while parsing an external XAML file. The pre-history is that I want to load an external XAML file with content to process. But I want to load as many different files as I want. That happens by unloading the old and loading the new one.
My issue is:
When I load a xaml the first time, everything is good, all as it should be.
But when I load the same xaml the second time, every entry of the object im Loading is there twice. If I run this again, every object is there three times and so on...
To debug the project yourself, download it here. The function starts at line 137 in the file "Control Panel.xaml.cs". I realy don't know what this is. Is it my fault or simply a bug? If yes, is there a workaround?
/// <summary>
/// Load a xaml file and parse it
/// </summary>
public void LoadPresentation()
{
this.Title = "Control Panel - " + System.IO.Path.GetFileName(global.file);
System.IO.FileStream XAML_file = new System.IO.FileStream(global.file, System.IO.FileMode.Open);
try
{
System.IO.StreamReader reader = new System.IO.StreamReader(XAML_file);
string dump = reader.ReadToEnd(); //This is only for debugging purposes because of the strange issue...
XAML_file.Seek(0, System.IO.SeekOrigin.Begin);
presentation = (ResourceDictionary)XamlReader.Load(XAML_file);
//Keys the resourceDictionary must have to be valid
if (presentation["INDEX"] == null || presentation["MAIN_GRID"] == null || presentation["CONTAINER"] == null || presentation["LAYOUTLIST"] == null)
{
throw new Exception();
}
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
for (int i = 0; i < topics.Count; )
{
topics.RemoveAt(i);
}
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
}
catch
{
System.Windows.Forms.MessageBox.Show("Failed to load XAML file \"" + global.file + "\"", "Parsing Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
presentation = null;
}
finally
{
XAML_file.Close();
}
}
Edit:
I have tried to serialize the object that was read from the XamlReader and in the output was nowhere any childelement... But if I pull the object out of the dictionary, the children are all there (duplicated and triplicated, but there).
I have already tried to clear the list over
topics.Clear();
and
topics=new ObservableCollection<TopicListItem>();
lv_topics.ItemsSource=topics;
Try Index.Topics.Clear() after loading the Topics into your topics object. That appears to get rid of the duplication.
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
topics.Clear();
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
Index.Topics.Clear(); //Adding this will prevent the duplication
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
In the code post topics is not declared in LoadPresentation() so naturally it will have any prior values.
I know you said you tried topics=new ObservableCollection(); but please try again. And put that IN LoadPresentation()
public void LoadPresentation()
{
ObservableCollection<TopicListItem> topics = new ObservableCollection<TopicListItem>()
I would pass filename
public void LoadPresentation(string fileName)
I get you may need to use topics outside LoadPresentation but this is debugging. If you need topics outside the return it.
public ObservableCollection<TopicListItem> LoadPresentation(string fileName)
If that does not fix it I would put a try catch block on the XAML_file.Close(); to see if something weird is not going on.