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.
Related
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;
}
I have two methods in my WCF, one to populate my ListBox and the other one to delete my selected ListBox items.
My issue is when I run my Delete method the Data shows null, there is also no errors or exceptions.
The coding runs smoothly but nothing changes and the Selected Item stays in the list box.
WPF
private void bnFeedDel_Click(object sender, RoutedEventArgs e)
{
using (TruckServiceClient service = new TruckServiceClient())
{
service.DelFeedAsync(new FeedView
{
Id = lbFeed.SelectedIndex
});
}
}
public async Task LoadFeeds()
{
TruckServiceClient TSC = new TruckServiceClient();
try
{
List<ClientItems> feeditems = new List<ClientItems>();
foreach (var item in await TSC.GetFeedAsync())
{
feeditems.Add(new ClientItems
{
FId = item.Id,
FTitle = item.Title,
FContent = item.Content
});
}
lbFeed.ItemsSource = (feeditems.ToArray());
lbFeed.DisplayMemberPath = "FTitle";
}
catch (Exception)
{
throw;
}
}
WCF
public void DelFeed(FeedView feedview)
{
using (var result = new TruckDb())
{
var t = new Feed
{
Id = feedview.Id,
Title = feedview.Title,
Content = feedview.Content
};
result.Feed.Remove(t);
result.SaveChanges();
}
}
This is all kinda still new to me so any comments/suggestions for my coding would be appreciated.
In the call to your WCF service, you're passing in a new instance of FeedView, with only the Id property set:
service.DelFeedAsync(new FeedView { Id = lbFeed.SelectedIndex });
So when the the service method runs, both Title and Content do not have values to be set (assuming they're both strings, they'll be null or empty strings):
var t = new Feed {
Id = feedview.Id,
Title = feedview.Title,
Content = feedview.Content};
When you call result.Feed.Remove(t);, nothing is removed because there's no matching item to remove.
Additional note: using using with WCF Service Clients is against best practices: https://msdn.microsoft.com/en-us/library/aa355056(v=vs.110).aspx
Hi I am using the Dynamic Data Display library that I got from codeplex.com/dynamicdatadisplay. I saved my copy in the root directory and added the reference to the DLL of the DynamicDataDisplay.dll file to my project. However the xaml does not recognize the ChartPlotter and is saying "The name "ChartPlotter" does not exist in the namespace "http://research.microsoft.com/DynamicDataDisplay/1.0". Does anyone know what's the problem?
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
Title="MainWindow" Height="350" Width="525">
<Grid>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:ChartPlotter.HorizontalAxis>
<d3:HorizontalDateTimeAxis Name="dateAxis"/>
</d3:ChartPlotter.HorizontalAxis>
<d3:ChartPlotter.VerticalAxis>
<d3:VerticalIntegerAxis Name="countAxis"/>
</d3:ChartPlotter.VerticalAxis>
<d3:Header FontFamily="Arial" Content="Bug Information"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="Count"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="Date"/>
</d3:ChartPlotter>
</Grid>
</Window>
Code-behind
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media; // Pen
using System.IO;
using Microsoft.Research.DynamicDataDisplay; // Core functionality
using Microsoft.Research.DynamicDataDisplay.DataSources; // EnumerableDataSource
using Microsoft.Research.DynamicDataDisplay.PointMarkers; // CirclePointMarker
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Window1_Loaded);
}
private void Window1_Loaded(object sender, RoutedEventArgs e)
{
List<BugInfo> bugInfoList = LoadBugInfo("..\\..\\BugInfo.txt");
DateTime[] dates = new DateTime[bugInfoList.Count];
int[] numberOpen = new int[bugInfoList.Count];
int[] numberClosed = new int[bugInfoList.Count];
for (int i = 0; i < bugInfoList.Count; ++i)
{
dates[i] = bugInfoList[i].date;
numberOpen[i] = bugInfoList[i].numberOpen;
numberClosed[i] = bugInfoList[i].numberClosed;
}
var datesDataSource = new EnumerableDataSource<DateTime>(dates);
datesDataSource.SetXMapping(x => dateAxis.ConvertToDouble(x));
var numberOpenDataSource = new EnumerableDataSource<int>(numberOpen);
numberOpenDataSource.SetYMapping(y => y);
var numberClosedDataSource = new EnumerableDataSource<int>(numberClosed);
numberClosedDataSource.SetYMapping(y => y);
CompositeDataSource compositeDataSource1 = new
CompositeDataSource(datesDataSource, numberOpenDataSource);
CompositeDataSource compositeDataSource2 = new
CompositeDataSource(datesDataSource, numberClosedDataSource);
plotter.AddLineGraph(compositeDataSource1,
new Pen(Brushes.Blue, 2),
new CirclePointMarker { Size = 10.0, Fill = Brushes.Red },
new PenDescription("Number bugs open"));
plotter.AddLineGraph(compositeDataSource2,
new Pen(Brushes.Green, 2),
new TrianglePointMarker
{
Size = 10.0,
Pen = new Pen(Brushes.Black, 2.0),
Fill = Brushes.GreenYellow
},
new PenDescription("Number bugs closed"));
plotter.Viewport.FitToView();
} // Window1_Loaded()
private static List<BugInfo> LoadBugInfo(string fileName)
{
var result = new List<BugInfo>();
FileStream fs = new FileStream(fileName, FileMode.Open);
StreamReader sr = new StreamReader(fs);
string line = "";
while ((line = sr.ReadLine()) != null)
{
string[] pieces = line.Split(':');
DateTime d = DateTime.Parse(pieces[0]);
int numopen = int.Parse(pieces[1]);
int numclosed = int.Parse(pieces[2]);
BugInfo bi = new BugInfo(d, numopen, numclosed);
result.Add(bi);
}
sr.Close();
fs.Close();
return result;
}
} // class Window1
public class BugInfo
{
public DateTime date;
public int numberOpen;
public int numberClosed;
public BugInfo(DateTime date, int numberOpen, int numberClosed)
{
this.date = date;
this.numberOpen = numberOpen;
this.numberClosed = numberClosed;
}
}
} // ns
Likely to be the "locked" dll issue. DynamicDataDisplay.dll has to be unlocked (check its properties).
related topic : WPF assembly reference missing - project still building
What I have:
I currently have this class:
class Storico
{
private string rigaStorico;
public string Name
{
get { return rigaStorico; }
set { rigaStorico = value; }
}
public Storico(string storic)
{
rigaStorico = storic;
}
}
This ObservableCollection:
ObservableCollection<Storico> rigaStorico = new ObservableCollection<Storico>();
And this DataTemplate, located into a LongListSelector:
<DataTemplate>
<Grid>
<TextBlock Text="●" Margin="0,8,0,0" Foreground="#EE7B00"/>
<TextBlock Text="{Binding Name}" ManipulationStarted="TextBlock_ManipulationStarted" ManipulationCompleted="TextBlock_ManipulationCompleted">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Name="ContextMenu" >
<toolkit:MenuItem Name="DeleteItem" Header="Delete Item" Click="DeleteItem_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
</Grid>
</DataTemplate>
What I need to do:
I actually need take all the strings contained into the LongListSelector, that may be like:
String1
String2
String3
and write them to a file into IsolatedStorage, in inversed order. Something like this:
String3
String2
String1
Obviously enough, the ItemsSource of the Storico is rigaStorico itself:
Storico.ItemsSource = rigaStorico;
I hope my goal is clear and I gave all the stuff that's needed to solve it.
Try this:
IsolatedStorageFile savegameStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fs = null;
using (fs = savegameStorage.CreateFile("FILENAME"))
{
if (fs != null)
{
var index = 0;
foreach(var rigaStor in rigaStorico.Reverse())
{
byte[] bytes = Encoding.UTF8.GetBytes(rigaStor.Name);
fs.Write(bytes, index, bytes.Length);
index = bytes.Length;
}
}
}
Not sure why you need them in reverse order, but you could use this FileStorage to write the data out.
FileStorage.WriteSharedData("MyItems.js", rigaStorico.Reverse());
The FileStorage uses json.net under the hood, if you want you could just do:
var storage = IsolatedStorageFile.GetUserStoreForApplication();
using (var fileStream = storage.CreateFile(fileName))
{
//Write the data
using (var isoFileWriter = new StreamWriter(fileStream))
{
var json = JsonConvert.SerializeObject(rigaStorico.Reverse());
isoFileWriter.WriteLine(json);
}
}
Thanks for the help. I solved my issue with the following code:
string[] contenutoStorico = rigaStorico.Select(x => x.Name).ToArray();
IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream("history.txt", FileMode.Create, FileAccess.Write, myIsolatedStorage)))
{
for (int i = 0; i < contenutoStorico.Length; i++)
{
writeFile.WriteLine(contenutoStorico[contenutoStorico.Length-i-1]);
}
writeFile.Close();
}
Hi everybody i have a little problem and i hope that somebody could help me
I have a json url that gives me the data like this :
[
{
"nom": "fofo",
"appGuid": "79fa058b-395a-438d-b66f-d751faea82e0"
},
{
"nom": "fifi",
"appGuid": "8b6bfcdb-d286-46e2-889e-0168a782323f"
},
{
"nom": "toto",
"appGuid": "65DE39E7-0130-4836-BBD3-7051574018B6"
},
{
"nom": "titi",
"appGuid": "66DE39E7-0130-4836-BBD3-7051574018B6"
}
]
My class :
public class ListApplication
{
public string nom { get; set; }
public string appGuid { get; set; }
}
I have a listpicker :
I want to bind just the element “nom” in the listpicker, I’ve tried this methods but nothing works:
The first method:
WebClient visio = new WebClient();
visio.DownloadStringCompleted += new DownloadStringCompletedEventHandler(vision_DownloadStringCompleted);
visio.DownloadStringAsync(new Uri("https://......... "));
void vision_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
return;
JArray jArray = JArray.Parse(e.Result);
List<ListApplication> apps = new List<ListApplication>();
for (int i = 0; i < jArray.Count; i++)
{
JObject app = (JObject)jArray[i];
apps.Add(new ListApplication { nom = (string)app["nom"], appGuid = (string)app["appGuid"] });
this.Application.ItemsSource = apps;
//
}
The second method:
public Appli()
{
InitializeComponent();
this.Type_info.ItemsSource = Action;
this.Periode.ItemsSource = Per;
var w = new WebClient();
Observable.FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted").Subscribe(r =>
{
var deserialized =
JsonConvert.DeserializeObject<List<ListApplication>>(r.EventArgs.Result);
Application.ItemsSource = deserialized;
});
w.DownloadStringAsync(
new Uri("https://........"));
}
And then i added Itemsource= {Binding nom} in the listpicker in XAML
Any help I’ll be so appreciative ,and sorry for my English
I believe you are asking that the binding is not taking effect. That is, you cannot see the data in your list.
If that is the case, are you setting the DataContext of the list picker to the List? Also, it seems that you would expect the content of the list picker to change when you receive more JSON data. So, if that is the case i would advise you to use an ObservableCollection instead of a List.
It's ok i found the answer ,i've used this with the second method
<toolkit:ListPicker x:Name="listPicker" Header="Application" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding nom}" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>