C# Event fires a lot of times - c#

I have a problem. I am using a CollectionView that receives data in a custom ViewModel from my webpage as long as it returns a JSON with the data. Once the Offset in the call >= num_of_rows the webpage prints "Nothing". If that happens I set a boolean HitBottomOfList = true;. Now everytime when it wants to do a webcall it checks if the HitBottomOfList == false.
Full Code
ViewModel:
public class TemplateListViewModel
{
public double WidthHeight { get; set; }
public ICommand LoadTemplates => new Command(LoadTemplateList);
public int CurrentTemplateCountReceived;
public bool HitBottomOfList = false;
public ObservableCollection<TemplateSource> sourceList { get; set; }
public TemplateListViewModel()
{
CurrentTemplateCountReceived = 0;
sourceList = new ObservableCollection<TemplateSource>();
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
var width = mainDisplayInfo.Width;
var density = mainDisplayInfo.Density;
var ScaledWidth = width / density;
WidthHeight = (ScaledWidth / 2);
loadingTemplates += onLoadingTemplates;
LoadTemplateList();
}
private event EventHandler loadingTemplates = delegate { };
private void LoadTemplateList()
{
loadingTemplates(this, EventArgs.Empty);
}
private async void onLoadingTemplates(object sender, EventArgs args)
{
if (HitBottomOfList == false)
{
List<Template> templateList = await App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived);
if (templateList != null)
{
foreach (var template in templateList)
{
ImageSource source = ImageSource.FromUri(new Uri("mysite.org/myapp/" + template.FileName));
TemplateSource templateSource = new TemplateSource { Id = template.Id, Source = source, WidthHeight = WidthHeight, FileName = template.FileName };
sourceList.Add(templateSource);
}
CurrentTemplateCountReceived = sourceList.Count;
}
else
{
HitBottomOfList = true;
}
}
}
}
The XAML:
<CollectionView ItemsSource="{Binding sourceList}" RemainingItemsThreshold="6"
RemainingItemsThresholdReachedCommand="{Binding LoadTemplates}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ff:CachedImage
Source="{Binding Source}"
VerticalOptions="Center"
HorizontalOptions="Center"
WidthRequest="{Binding WidthHeight}"
HeightRequest="{Binding WidthHeight}">
<ff:CachedImage.GestureRecognizers>
<TapGestureRecognizer Tapped="imgTemplate_Clicked" />
</ff:CachedImage.GestureRecognizers>
</ff:CachedImage>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And finally the WebCall that I do:
public async Task<List<Template>> GetTemplates(User user, int offset)
{
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("un", user.Username));
postData.Add(new KeyValuePair<string, string>("pw", user.Password));
postData.Add(new KeyValuePair<string, string>("offset", offset.ToString()));
var content = new FormUrlEncodedContent(postData);
var weburl = "mysite.org/myapp/get_templates.php";
List<Template> response = await PostResponseTemplates(weburl, content);
return response;
}
public async Task<List<Template>> PostResponseTemplates(string weburl, FormUrlEncodedContent content)
{
var response = await client.PostAsync(weburl, content);
var json = await response.Content.ReadAsStringAsync();
if (json != "Nothing")
{
var jObject = JObject.Parse(json);
var templatePropery = jObject["Templates"] as JArray;
List<Template> templateList = new List<Template>();
foreach (var property in templatePropery)
{
List<Template> propertyList = new List<Template>();
propertyList = JsonConvert.DeserializeObject<List<Template>>(property.ToString());
templateList.AddRange(propertyList);
}
var sourcePropery = (JObject)jObject["Source"];
foreach (var property in sourcePropery)
{
string tempplateSource = property.Value.Value<string>();
App.TemplateSource = tempplateSource;
}
return templateList;
}
else
{
ErrorMessage = json;
return default(List<Template>);
}
}
Now the problem is that when it does trigger the RemainingItemsThresholdReachedCommand="{Binding LoadTemplates}"
it executes the command a lot of times after each other, thinking it needs more data, while there is already a command to get new data. This causes the app to get new data with the same offset a few times, so the app will the same data in the CollectionView a lot of times.
I want the app to call the webpage 1 time to receive more images and just let it load, without asking again for new data, so the duplicates in the list will disappear.
So how can I make sure it only asks the data once, when almost hit the bottom?
Update
Using #Jason his code the following is going wrong:
When the code goes through the MyHandler, it fires the LoadTemplateList(); But jumps to the handling = false; before it finished, so the next command is allowed to start, without finishing the other. Any idea how to wait for the method to finish?

use a bool to track if you are already handling the event and ignore any new ones
bool handling = false;
public void MyHandler()
{
// already handling an event, ignore the new one
if (handling) return;
handling = true;
// process event here
handling = false;
}

Related

Label doesn't properly update on defered event handler [Bug?]

I noted something wrong, either it's from me or a bug when experimenting with .Net MAUI.
I have an ObservableCollection property :
public ObservableCollection<LotoModel> Lotteries { get; set; } = new();
and an ObservableProperty (using community mvvm toolkit) :
[ObservableProperty]
public string _lotteriesCount = "A";
When ever I click/touch a Button I load my Lotteries collection. (tested with an hard coded 4 static LotoModel items) :
static GridModel _testGM1 = new GridModel { Name = "TEST Grid #1", Start = 0, End = 10, NumberOfDraw = 2 };
static GridModel _testGM2 = new GridModel { Name = "TEST Grid #2", Start = 0, End = 20, NumberOfDraw = 5 };
static GridModel _testGM3 = new GridModel { Name = "TEST Grid #3", Start = 0, End = 30, NumberOfDraw = 8 };
static GridModel _testGM4 = new GridModel { Name = "TEST Grid #4", Start = 0, End = 50, NumberOfDraw = 10 };
static LotoModel _testLM1 = new LotoModel { Name = "TEST Lottery #1", IsFavorite = true, Grids = new ObservableCollection<GridModel> { _testGM1, _testGM2 } };
static LotoModel _testLM2 = new LotoModel { Name = "TEST Lottery #2", IsFavorite = false, Grids = new ObservableCollection<GridModel> { _testGM3, _testGM4 } };
And the Button command Task :
async Task GetLotteriesAsync()
{
if (IsBusy)
return;
try
{
IsBusy = true;
_buttonCount++;
LotteriesCount = _buttonCount.ToString();
if (Lotteries.Count != 0)
Lotteries.Clear();
Lotteries.Add(_testLM1);
Lotteries.Add(_testLM2);
Lotteries.Add(_testLM1);
Lotteries.Add(_testLM2);
}
catch (Exception e)
{
Log.Error(e, "Error while trying to get our lotteries");
await Application.Current.MainPage.DisplayAlert("Error!", e.Message, "OK");
}
finally
{
IsBusy = false;
}
}
So each time I touch/click my Button, LotteriesCount string property get's updated with a static int counter field value :
static int _buttonCount = 0;
That is OK.
Now I also update this property via this CollectionChangedEventHandler :
public LotteriesVM(LotteryService lotteryService)
{
GetLotteriesCommand = new Command(async () => await GetLotteriesAsync());
Lotteries.CollectionChanged += LotteriesChangedMethod;
}
private void LotteriesChangedMethod(object sender, NotifyCollectionChangedEventArgs e)
{
LotteriesCount = _lotteriesCount + "_" + Lotteries.Count.ToString();
}
And now here the unexpected behavior : The label only update the counter part of it string property, the remaining "_1_2_3_4" added in the handler doesn't get updated in UI.
Note that I'm using a Android Pixel 5 emulator from VS 17.3 preview.
And I also noted that if I force orientation of the emulated android device, then the Label is updated !
Even more, if I force a XAML MinimalWidthRequest="200" for my Label, then it is correctly updated now !
<!-- without MinimumWidthRequest, the label update only after forcing a screen orientation-->
<Label Text="{Binding LotteriesCount}" MinimumWidthRequest="200" HorizontalOptions="Center" FontAttributes="Bold" FontSize="22" TextColor="OrangeRed"/>
So am I doing something wrong or it is a bug ? Thank you.

Lazy Loading gridview from json

I have a lazy loading in gridview whose data is taken from JSON.
Sample of JSON:
{
"error": false,
"total_data": 32,
"data_per_page": "16",
"current_page": 1,
"total_page": 2,
"current_total": 16,
"data": [
{
"id": "2613",
"judul": "Kamus ID EN",
"slug": "kamus-id-en",
"cover": "https://mhnkp2.com/src/umum/cover/kamus_ID_EN-thumb.jpg",
"path": "https://mhnkp2.com/school/dl/dodl/2613",
"ukuran": "3504835",
"formated_size": "3.34 MB",
"fname": "kamus_ID_EN.pdf",
"publish": "1",
"urgent": "400",
"kelas": "0",
"nama_kelas": "Umum"
},
XAML:
<Grid x:Name="content" Grid.Row="1" Loaded="MainGrid_Loaded">
<GridView
x:Name="itemGridView"
Loaded="itemGridView_Loaded">
<GridView.ItemTemplate>
<DataTemplate>
<Grid
Width="135"
Height="280"
Margin="5,5,5,5"
Background="White">
<TextBlock
x:Name="title"
Margin="0,0,10,10"
FontSize="14"
FontWeight="SemiBold"
Foreground="Black"
Style="{StaticResource TitleTextBlockStyle}"
Text="{Binding Judul}"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Code:
ObservableCollection<Buku> datasource = new ObservableCollection<Buku>();
int offset = 0;
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
itemGridView.ItemsSource = datasource;
Umum(1);
}
public class Buku
{
public string Judul { get; set; }
}
private async void Umum(int offset)
{
urlPath = "https://mhnkp2.com/school/api-v3/fetch/umum";
var httpClient = new HttpClient(new HttpClientHandler());
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("halaman", offset.ToString()),
new KeyValuePair<string, string>("limit", "16"),
new KeyValuePair<string, string>("SCH-API-KEY", "SCH_KEnaBiDeplebt")
};
var response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
string jsonText = await response.Content.ReadAsStringAsync();
try
{
double total = groupObject1["total_data"].GetNumber();
double pages = groupObject1["total_page"].GetNumber();
double page = groupObject1["current_page"].GetNumber();
Buku file = new Buku();
file.PageNo = Convert.ToInt32(page);
file.Pages = Convert.ToInt32(pages);
file.Total = Convert.ToInt32(total);
JsonArray jsonData1 = jsonObject["data"].GetArray();
foreach (JsonValue groupValue1 in jsonData1)
{
JsonObject groupObject2 = groupValue1.GetObject();
string title = groupObject2["judul"].GetString();
Buku file1 = new Buku();
file1.Judul = title;
datasource.Add(file1);
}
itemGridView.ItemsSource = datasource;
}
}
private void itemGridView_Loaded(object sender, RoutedEventArgs e)
{
ScrollViewer viewer = GetScrollViewer(this.itemGridView);
viewer.ViewChanged += Viewer_ViewChanged;
}
private void Viewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer view = (ScrollViewer)sender;
double progress = view.VerticalOffset / view.ScrollableHeight;
//Debug.WriteLine(progress);
if (progress > 0.7 && !incall && !endoflist)
{
incall = true;
busyindicator.IsActive = true;
Umum(++offset);
}
}
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
I am having a problem, that the gridview should display data on pages 1 and 2, but in the gridview the data displayed on page 2 is a repetition of data on page 1, as shown below:
How to fix it?
Note:
The page uses the "offset" parameter
The page limit is "total_page" in JSON
you just need to pass the offset value to MainGrid_Loaded and set offset zero to one
ObservableCollection<Buku> datasource = new ObservableCollection<Buku>();
int offset = 1; // set offset zero to one
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
itemGridView.ItemsSource = datasource;
Umum(offset); // just change 1 to offset
}
public class Buku
{
public string Judul { get; set; }
}
private async void Umum(int offset)
{
urlPath = "mhnkp2.com/school/api-v3/fetch/ktsp2006/kelas/1";
var httpClient = new HttpClient(new HttpClientHandler());
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("halaman", offset.ToString()),
new KeyValuePair<string, string>("limit", "16"),
new KeyValuePair<string, string>("SCH-API-KEY", "SCH_KEnaBiDeplebt")
};
var response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
string jsonText = await response.Content.ReadAsStringAsync();
try
{
double total = groupObject1["total_data"].GetNumber();
double pages = groupObject1["total_page"].GetNumber();
double page = groupObject1["current_page"].GetNumber();
Buku file = new Buku();
file.PageNo = Convert.ToInt32(page);
file.Pages = Convert.ToInt32(pages);
file.Total = Convert.ToInt32(total);
JsonArray jsonData1 = jsonObject["data"].GetArray();
foreach (JsonValue groupValue1 in jsonData1)
{
JsonObject groupObject2 = groupValue1.GetObject();
string title = groupObject2["judul"].GetString();
Buku file1 = new Buku();
file1.Judul = title;
datasource.Add(file1);
}
itemGridView.ItemsSource = datasource;
}
}
private void itemGridView_Loaded(object sender, RoutedEventArgs e)
{
ScrollViewer viewer = GetScrollViewer(this.itemGridView);
viewer.ViewChanged += Viewer_ViewChanged;
}
private void Viewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer view = (ScrollViewer)sender;
double progress = view.VerticalOffset / view.ScrollableHeight;
//Debug.WriteLine(progress);
if (progress > 0.7 && !incall && !endoflist)
{
incall = true;
busyindicator.IsActive = true;
Umum(offset++);
}
}
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}

Navigate to another ContentPage from async method before the async method returns a value

I need to start Another ContentPage before this async method returns value:
public class GettingCountry : ContentPage
{
public static List<string> CountriesList = new List<string>();
MainPage mainPage = new MainPage();
public async Task<List<RootObject>> FetchAsync(string url)
{
string jsonString;
using (var httpClient = new System.Net.Http.HttpClient())
{
var stream = await httpClient.GetStreamAsync(url);
StreamReader reader = new StreamReader(stream);
jsonString = reader.ReadToEnd();
}
var listOfCountries = new List<RootObject>();
var responseCountries = JArray.Parse(JObject.Parse(jsonString)["response"]["items"].ToString());
foreach (var countryInResponse in responseCountries)
{
var rootObject = new RootObject((int)countryInResponse["id"], (string)countryInResponse["title"]);
CountriesList.Add(rootObject.Title);
}
//I NEED TO NAVIGATE TO FillingPage() FROM HERE:
await Navigation.PushAsync(new FillingPage());
//await Navigation.PushModalAsync(new NavigationPage(new FillingPage()));
return listOfCountries;
}
The page that is need to be started is:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FillingPage : ContentPage
{
public FillingPage ()
{
GettingCountry gettingCountry = new GettingCountry();
Label header = new Label
{
Text = "Заполните бланк",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
TextColor = Color.Blue
};
Entry nameEntry = new Entry()
{
Placeholder = "Имя",
};
Entry surnameEntry = new Entry()
{
Placeholder = "Фамилия"
};
Picker countryPicker = new Picker()
{
Title = "Страна",
VerticalOptions = LayoutOptions.CenterAndExpand
};
foreach (string country in GettingCountry.CountriesList)
{
countryPicker.Items.Add(country);
}
SearchBar townSearchBar = new SearchBar()
{
Placeholder = "Город",
SearchCommand = new Command(() =>
{
})
};
SearchBar universitySearchBar = new SearchBar()
{
Placeholder = "Университет",
SearchCommand = new Command(() =>
{
})
};
Button myButton = new Button()
{
TextColor = Color.Green,
Text = "Выполнить",
FontSize = 22
};
// Accomodate iPhone status bar.
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
// Build the page.
this.Content = new StackLayout
{
Children =
{
header,
nameEntry,
surnameEntry,
countryPicker,
townSearchBar,
universitySearchBar,
myButton
}
};
}
}
}
But this code await Navigation.PushAsync(new FillingPage()); works well only when I press a button. When I press a button the needed page starts well. But the same code inside a method does not work. I have debagged it. It goes to a FillingPage() but doesn`t launches it when I try to launch it from inside the async method.
This is likely a result of the operation not being performed on the main thread. Try wrapping your code like this:
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PushAsync(new FillingPage());
}
Edit: After a private message, I learned that there was not enough information in the question to know the real issue. The application is calling FetchAsync in Application.OnStart and it's not part of the view hiearchy at all so navigation methods would not work. The following was provided:
protected override void OnStart ()
{
getCountry();
}
private async void getCountry()
{
var url = "...";
GettingCountry gettingCountry = new GettingCountry();
await gettingCountry.FetchAsync(url);
}
GettingCountry is a ContentPage being used like some kind of data access class and it's not currently part of the UI as MainPage is set to something else. A quick hack would be something more like:
private async void getCountry()
{
var url = "...";
GettingCountry gettingCountry = new GettingCountry();
var data = await gettingCountry.FetchAsync(url);
await MainPage.Navigation.PushAsync(new FillingPage(data));
}
I would suggest two further areas to look at improving.
Consider refactoring GettingCountry as it does not need to be a ContentPage.
Investigate an alternative calling so that async void is not used.

Tapped Slideshow Image From JSON

I have a picture slideshow that when the picture was tapped by the user and if the url in json no "#", it will go to the url address.
JSON:
XAML:
<Image x:Name="topBanner" Source="images/new (3.0)/banner/MI-W10-banner-1366-01.png" Tapped="topBanner_Tapped" />
Code:
DispatcherTimer playlistTimer1a = null;
List<string> Images1a = new List<string>();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ImageSource1a();
}
private async void ImageSource1a()
{
try
{
var httpClientHandler = new HttpClientHandler();
httpClientHandler.Credentials = new System.Net.NetworkCredential("username", "password");
var httpClient = new HttpClient(httpClientHandler);
string urlPath = "http://";
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("platform","win"),
};
HttpResponseMessage response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
string jsonText = await response.Content.ReadAsStringAsync();
JsonObject jsonObject = JsonObject.Parse(jsonText);
//JsonObject jsonData1 = jsonObject["data"].GetObject();
JsonArray jsonData1 = jsonObject["data"].GetArray();
foreach (JsonValue groupValue1 in jsonData1)
{
JsonObject groupObject1 = groupValue1.GetObject();
string image = groupObject1["image"].GetString();
string url = groupObject1["url"].GetString();
Images1a.Add(image);
}
playlistTimer1a = new DispatcherTimer();
playlistTimer1a.Interval = new TimeSpan(0, 0, 6);
playlistTimer1a.Tick += playlistTimer_Tick1a;
topBanner.Source = new BitmapImage(new Uri(Images1a[0]));
playlistTimer1a.Start();
}
}
catch (HttpRequestException ex)
{
RequestException();
}
}
int count1a = 0;
void playlistTimer_Tick1a(object sender, object e)
{
if (Images1a != null)
{
if (count1a < Images1a.Count)
count1a++;
if (count1a >= Images1a.Count)
count1a = 0;
ImageRotation1a();
}
}
private async void ImageRotation1a()
{
OpacityTrans1.Begin();
}
private void topBanner_Tapped(object sender, TappedRoutedEventArgs e)
{
//I have to confused to add this code
//Can anyone help me to add this code
}
How, when the slideshow image tapped by the user, it will go to url address on JSON (when the address url not '#')?
There are a lot of improvements to be made to the code. I recommend you read a bit more about DataBinding and MVVM.
However, I will try to help you with the code, as-is:
First, you need to ensure you maintain all the data from JSON so you can use it later. Instead of having a List for the image URLs you need to have a structure to hold both urls:
public struct DataItem
{
public string ImageUrl {get;set;}
public string Url {get;set;}
}
Then declare your list as:
List<DataItem> Images1a = new List<DataItem>();
When you build you list, create DataItem instances and add them to the list
foreach (JsonValue groupValue1 in jsonData1)
{
JsonObject groupObject1 = groupValue1.GetObject();
var dataItem = new DataItem();
dataItem.ImageUrl = groupObject1["image"].GetString();
dataItem.Url = groupObject1["url"].GetString();
Images1a.Add(dataItem);
}
Finally, when you tap an image, find the url based on the index:
private async void topBanner_Tapped(object sender, TappedRoutedEventArgs e)
{
var tappedItem = Images1a[count1a];
if (tappedItem.Url != "#")
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(tappedItem.Url));
}
}
You can read more about how to launch URIs from the documentation

Temporarily Sort WP8 Listbox

I have an WP C# app built on the Local Database Sample (http://code.msdn.microsoft.com/wpapps/Local-Database-Sample-57b1614c).
The main page displays a list of items from the xml database, which by default shows items in the order created. I would like to be able to offer at least one other sort order - either reversed or sorted by "Subject". Unfortunately Listbox.Sort is not supported in WP.
I have tried various answers found on here, including attempting to sort the xml file itself, but for reasons beyond my level of coding they do not change the order of the list (see Templatestorage) however i suspect it is due to improper implementation.
The code for the listbox is:
<ListBox x:Name="Templates" SelectionChanged="OnSelectionChanged" Background="Transparent" Style="{StaticResource ListBoxStyle1}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Grid.Row="2" Text="{Binding Subject}" Style="{StaticResource PhoneTextLargeStyle}" Margin="12,2" />
<TextBlock Grid.Row="2" Text="{Binding DT}" Style="{StaticResource PhoneTextSmallStyle}" Margin="12,5" />
<Rectangle Height="1" Margin="23,7,50,7" Fill="{StaticResource PhoneAccentBrush}" MinWidth="400" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The c# is:
public partial class MainPage
{
private readonly TemplateStorage storage = new TemplateStorage();
public MainPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Templates.ItemsSource = storage.GetItems();
this.NavigationService.RemoveBackEntry();
}
private void PhoneApplicationPage_GotFocus(object sender, RoutedEventArgs e)
{
Templates.ItemsSource = storage.GetItems();
}
}
The Templatestorage, which shows the various attempts at sorting (commented out) is:
public class TemplateStorage
{
private IList<NanoMemoTemplate> templates;
private const string Filename = "template-list.xml";
protected IList<NanoMemoTemplate> Templates
{
get
{
return templates ?? (templates = LoadTemplates().ToList());
}
set
{
templates = value;
}
}
protected IEnumerable<NanoMemoTemplate> LoadTemplates()
{
using(var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if(!applicationStorage.FileExists(Filename))
return Enumerable.Empty<NanoMemoTemplate>();
using(var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.Read))
{
var document = XDocument.Load(speedListFile);
return from t in document.Root.Elements("template")
select new NanoMemoTemplate
{
Id = new Guid(t.Attribute("id").Value),
Subject = t.Attribute("subject").Value,
Body = t.Attribute("body").Value,
DT = t.Attribute("dateCreated").Value,
};
}
}
}
//public IEnumerable<NanoMemoTemplate> SortTemplates()
//{
// using (var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
// {
// if (!applicationStorage.FileExists(Filename))
// return Enumerable.Empty<NanoMemoTemplate>();
// using (var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.ReadWrite))
// {
// var documentSort = XDocument.Load(speedListFile);
// XDocument datatemp = new XDocument(documentSort);
// var subjectSort = from p in datatemp.Descendants("template")
// orderby (string)p.Attribute("subject")
// select p;
// //var subjectSort = datatemp.Elements("template").OrderBy(p => (string)p.Attribute("subject")).ToArray();
// string cleanDataDump = subjectSort.ToString();
// MessageBox.Show(cleanDataDump);
// documentSort.Descendants("template").Remove();
// documentSort.Element("template").Add(subjectSort);
// return Templates;
// }
// }
//}
//public IEnumerable<NanoMemoTemplate> SortItems()
//{
// //Sort XML order so order is saved
// using (var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
// {
// if (!applicationStorage.FileExists(Filename))
// return Enumerable.Empty<NanoMemoTemplate>();
// using (var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.ReadWrite))
// {
// var documentSort = XDocument.Load(speedListFile);
// IEnumerable<string> codes = from code in documentSort.Elements("template")
// let subs = (string)code.Element("subject")
// orderby subs
// select subs;
// //return Templates as per usual as sorting is done at DB level
// return from t in documentSort.Root.Elements("template")
// select new NanoMemoTemplate
// {
// Id = new Guid(t.Attribute("id").Value),
// Subject = t.Attribute("subject").Value,
// Body = t.Attribute("body").Value,
// DT = t.Attribute("dateCreated").Value,
// };
// }
// }
//}
public IEnumerable<NanoMemoTemplate> GetItems()
{
return Templates;
}
public void Save(NanoMemoTemplate template)
{
Templates.Add(template);
}
public void Delete(NanoMemoTemplate template)
{
Templates.Remove(template);
}
//public void Sort(NanoMemoTemplate template)
//{
// IList<NanoMemoTemplate> list = new List<NanoMemoTemplate>();
// IEnumerable<NanoMemoTemplate> sortedEnum = list.OrderBy(Templates => Templates.Subject);
// IList<NanoMemoTemplate> sortedList = sortedEnum.ToList();
//}
public void SaveChanges()
{
using(var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
using(var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Create, FileAccess.Write))
{
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("templates",
from t in Templates
select new XElement("template",
new XAttribute("id", t.Id),
new XAttribute("subject", t.Subject),
new XAttribute("body", t.Body),
new XAttribute("dateCreated", t.DT))));
document.Save(speedListFile);
}
}
}
Instead of having to set Templates.ItemsSource = storage.GetItems(); in your code, you can keep an ObservableCollection (or other enumerable type) as a class-level variable:
//StorageTemplates should be a class-level variable
ObservableCollection<NanoMemoTemplate> StorageTemplates;
//You can assign the value to StorageTemplates when the page loads
StorageTemplates = storage.GetItems();
You would then apply an ItemsSource="{Binding StorageTemplates}" data binding to your ListBox in XAML. (See this for more info on binding)
<ListBox x:Name="Templates" ItemsSource="{Binding StorageTemplates, UpdateSourceTrigger="PropertyChanged"}" SelectionChanged="OnSelectionChanged" Background="Transparent" Style="{StaticResource ListBoxStyle1}" >
<ListBox.ItemTemplate>
....
</ListBox.ItemTemplate>
</ListBox>
Then you can use the built-in Sort methods of the ObservableCollection to set your sort order for the items. You may need to implement a Property Changed handler, you can check this tutorial for more information.

Categories

Resources