remove items from longlist selector if image dont load - c#

Hello in my WP8 app I get data from a web API and then set the Itemsource of my longlistselector to the instance of the Class.
The problem is that the API sometimes sends out image links before the Images are actually created, so when look at the longlist selectors Picture that is bound to a url in the Class it will turn out black and not load...
Now my question is, is there some way to filter those posts out so they don't show, the Images are not loaded in code behind but only in the XAML when the app runs..
EDIT:
Will add some code and better explain:
I use a webclient to download a Json file from the server and then use Json.net to deserialize it into my class like this:
string jsonstring = e.Result.ToString();
Latest lastdownloaded = JsonConvert.DeserializeObject<Latest>(jsonstring);
my class looks something like this:
public class Latest
{
public string ThumbLink{ get; set; }
public int Id { get; set; }
public string Title { get; set;
}
I then set the the item source of my longlist selector to the instance
latestlonglist.ItemsSource = lastdownloaded;
And then my xaml simply looks like this:
<phone:LongListSelector x:Name="latestlonglist" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="latestlonglist_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<Grid Height="160">
<TextBlock x:Name="titleBlock" Text="{Binding Title}" TextWrapping="Wrap" Margin="145,0,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="26" Height="105" VerticalAlignment="Top"/>
<Image x:Name="latestListImage" Source="{Binding ThumbLink}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="140" />
</Grid>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
the json containas 40 image links
Thanks

OK, there is no code so I can only guess. I guess that you have ObservableCollection<> of objects which contains picture objects which is in ViewModel.
You should make and async method for downloading pictures and storing it in BitmapImage.
In ViewModel you make a method to load data where you for looping through you image links and launch download with await and add them to your ObservableCollection<>. That way items to your ObservableCollection<> will be added only after downloading them and you solve problem with black squares.
Here is your class:
class Latest
{
public string ThumbLink { get; set; }
public BitmapImage Thumb { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public async Task<bool> LoadThumbAsync()
{
WebClient client = new WebClient();
client.OpenReadAsync(new Uri(this.ThumbLink));
client.OpenReadCompleted += (s, e) => //Wait for completion
{
var tempBitmap = new BitmapImage(); //Create temp bitmap container
tempBitmap.SetSource(e.Result); //Copy stream to bitmap container
this.Thumb = tempBitmap;
e.Result.Close();
return;
};
return true; // return bool only to be able to await this method.
}
}
class ViewModel
{
ObservableCollection<Latest> lastdownloaded = new ObservableCollection<Latest>();
ObservableCollection<Latest> allItems = new ObservableCollection<Latest>();
public async void LoadData()
{
//Here you load all your thumbs in list allItems. This is only temporary container.
for (var i = 0; i < allItems.Count; i++) // Now here for every item in that container you load thumb and add it to lastdownloaded list which you bind to Long List Selector.
{
await allItems[i].LoadThumbAsync();
lastdownloaded.Add(allItems[i]);
}
}
}
Maybe this not the best code but should give you idea.

Related

UWP thumbnail binding with image with x:bind

I am trying to bind thumbnail of my video StorageFile to Image XAML element through data template with x:Bind. I am using MVVM approach and I've used same method to achieve this in past but I don't know why it isn't working now.
I use live property explorer and source of Image is 0. other properties like video title are just working fine but image is not working. But problem occurs even with duration, sometimes duration shows up and sometimes it doesn't, which is odd.
I am providing my code below.
Model
public class VideoItem : LibraryItem
{
#region Props
public string Views { get; set; }
public string Duration { get; set; }
public BitmapImage Display { get; set; }
public VideoProperties MyVideoProperties { get; set; }
public StorageFile MyVideoFile { get; set; }
#endregion
public VideoItem(StorageFile File)
{
MyVideoFile = File;
Initialize();
}
#region PrivateMethods
private async void Initialize()
{
Title = MyVideoFile.DisplayName;
MyVideoProperties = await MyVideoFile.Properties.GetVideoPropertiesAsync();
var dur = MyVideoProperties.Duration;
Duration = $"{dur.Hours.ToString()} : {dur.Minutes.ToString()} : {dur.Seconds.ToString()}";
Display = await GetDisplay();
Views = MyVideoProperties.Rating.ToString();
}
private async Task<BitmapImage> GetDisplay()
{
var bitm = new BitmapImage();
using (var imgSource = await MyVideoFile.GetScaledImageAsThumbnailAsync(ThumbnailMode.VideosView))
{
if (imgSource != null) { bitm.SetSource(imgSource); }
else
{
var storelogoFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
var storageLogoFile = await storelogoFolder.GetFileAsync("StoreLogo.png");
bitm.UriSource = new Uri(storageLogoFile.Path);
}
}
return bitm;
}
#endregion
}
public class LibraryItem
{
public string Title { get; set; }
}
ViewModel
public class VideoLibraryViewModel
{
#region Constructor
public VideoLibraryViewModel(StorageFolder mainFolder)
{
VideoItems = new ObservableCollection<VideoItem>();
MainFolder = mainFolder;
Initialize();
}
#endregion
#region Props
public ObservableCollection<VideoItem> VideoItems { get; set; }
#endregion
#region PrivateFields
private StorageFolder MainFolder;
private IEnumerable<StorageFile> Videos;
private char[] sep = new char[] { '/' };
#endregion
#region PrivateMethods
private async void Initialize()
{
Videos = await MainFolder.GetFilesAsync();
Videos = Videos.Where(a => a.ContentType.Split(sep)[0] == "video");
FillUp();
}
private void FillUp()
{
foreach (var file in Videos)
{
VideoItems.Add(new VideoItem(file));
}
}
#endregion
}
View
<controls:AdaptiveGridView Name="VideosLibraryGridView" Grid.Row="1"
Header="Videos"
Style="{StaticResource MainGridView}"
ItemClick="VideosLibraryGridView_ItemClicked"
ItemsSource="{x:Bind VideoLibraryVM.VideoItems, Mode=OneWay}">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate x:DataType="data:VideoItem">
<StackPanel Margin="4" >
<Grid>
<Image Source="{x:Bind Display, Mode=OneWay}" Style="{StaticResource GridViewImage}"/>
<Border Style="{StaticResource TimeBorder}">
<TextBlock Text="{x:Bind Duration, Mode=OneWay}" Foreground="White"/>
</Border>
</Grid>
<TextBlock Text="{x:Bind Title,Mode=OneWay}" Style="{StaticResource GridViewVideoName}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{x:Bind Views,Mode=OneWay}" Style="{StaticResource GridViewViews}"/>
<TextBlock Text="Views" HorizontalAlignment="Right"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
Style For Image
<Style TargetType="Image" x:Key="GridViewImage">
<Setter Property="Stretch" Value="UniformToFill"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Output in the app, you can see gridview items show no image and no duration, sometimes duration show and image never shows up:
Why no image?
Why no duration?
UPDATE
I have checked with breakpoints, all properties of items are appearing null, apart from title, all properties apart from title are retrieved asyncronosly, maybe that is the reason?
all properties of items are appearing null, apart from title, all properties apart from title are retrieved asyncronosly
It's because the UI just render grid items with all available properties without waiting any results from asynchronous methods, hence why sometimes you'll get some items with a proper duration text shows up and sometimes It doesn't.
So, the logical solution is to run the asynchronous method after the gridview load those items, right?
But how? placing it under loaded event inside datatemplate doesn't change anything since It'll just do the same problem again.
well, you can do it by abusing ContainerContentChanging event inside Gridview control due to how that event itself works.
Page.xaml
<controls:AdaptiveGridView
Name="VideosLibraryGridView" Grid.Row="1
ContainerContentChanging="VideosLibraryGridView_ContainerContentChanging"
Header="Videos"
Style="{StaticResource MainGridView}"
ItemClick="VideosLibraryGridView_ItemClicked"
ItemsSource="{x:Bind VideoLibraryVM.VideoItems, Mode=OneWay}">
<!--something something-->
</controls:AdaptiveGridView>
Page.xaml.cs
private void VideosLibraryGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args
{
args.RegisterUpdateCallback(LoadImage);
}
private async void LoadImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
var imageurl = (args.Item as model).ThumbnailUri;
var cache = await getimagefromfileasync(imageurl);
//check your image location based on your template first.
var image = templateRoot.Children[0] as Image;
image.Source = new BitmapImage()
{
UriSource = new Uri(cache.Path)
};
image.Opacity = 1;
}
The code above is what I did in order to load the cached thumbnail asynchronously.
source:
Dramatically Increase Performance when Users Interact with Large Amounts of Data in GridView and ListView
ContainerContentChanging Event
Update ListView and GridView items progressively

binding events to wpf list for realtime update

I am building a WPF app that will populate filtered headlines from a variety of news services. Each headline triggers an event, which in a console app I can display on the console. I want to use WPF here but have bot used it prior to this endeavor. My mainwindow xaml is as shown below. My original thought was to have an ObservableCollection populate list items in a listview in the xaml. If that is not the right approach, I'm open to expert opinion on a better way as speed of receipt to display is vital. If what I am doing is proper then how do I bind a new entry to the ObservableCollection to a new list item to display?
<StackPanel Orientation="Vertical" Margin="5,150 5 50" Name="HeadlinePanel">
<TextBlock Text="Filtered Headlines From Monitoring List"
HorizontalAlignment="Left" Margin="0,0 5 5" Name="ScrollingHeadlineLabel" FontWeight="Bold" FontSize="14" Background="LightSkyBlue" />
<ListBox>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="a property on the headline" />
<TextBlock><Run Text="headline is from a website"/></TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="a property on the headline" />
<TextBlock><Run Text="headline is from TWTR"/></TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="a property on the headline" />
<TextBlock><Run Text="headline from a different website"/></TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="a property on the headline" />
<TextBlock><Run Text="text from a different tweet"/></TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
</StackPanel>
In the console app the streaming begins (code shown below) in the filteredStream.Start() but the handler needs to register prior. In the console app I can write to the console (commented out) but here I add the headline object to the collection when the event fires. My question is how to bind that to my xaml list items. I will initiate the stream from mainwindow method? or some method I create to run within that?
var config = new TwitterOAuthConfig()
{
ConsumerKey = customerKey,
ConsumerSecret = customerSecret,
AccessToken = accessToken,
AccessTokenSecret = accessTokenSecret,
GeoOnly = false,
KeywordsToMonitor = keywords,
UsersToFollow = followers
};
var filteredStream = new TwitterClient(config);
var headlineCollection = new ObservableCollection<Headline>();
// subscribe to the event handler
filteredStream.HeadlineReceivedEvent +=
(sender, arguments) => headlineCollection.Add(arguments.Headline);
//Console.WriteLine("ID: {0} said {1}", arguments.Headline.Username, arguments.Headline.HeadlineText);
filteredStream.ExceptionReceived += (sender, exception) => Console.WriteLine(exception.HeadlineException.ResponseMessage);
filteredStream.Start();
Here is my Original HeadlineViewModel
public class HeadlineViewModel : ObservableItem
{
private string _headlineText;
public string Source { get; set; }
public string Username { get; set; }
public string Text
{
get { return _headlineText; }
set
{
_headlineText = value;
RaisePropertyChangedEvent("HeadlineText");
}
}
public List<string> UrlsParsedFromText { get; set; }
public string TimeStamp { get; set; }
}
I've updated it to the following:
public class HeadlineViewModel
{
public class HeadlineDisplayItems: ObservableItem
{
private string _headlineText;
public string HeadlineIconPath { get; set; }
public string TimeStamp { get; set; }
public string Username { get; set; }
public string Text
{
get { return _headlineText; }
set
{
_headlineText = value;
RaisePropertyChangedEvent("HeadlineText");
}
}
}
public List<string> UrlsParsedFromText { get; set; }
public ObservableCollection<HeadlineDisplayItems> HeadlineCollection { get; set; }
}
I don't know about your architecture, but wpf is mostly used with what they call MVVM (Model-View-ViewModel) where you have your View (you already posted the code), the ViewModel (I believe you don't have one) and the model (that is the Headline you are using). The objective of the ViewModel is to simplify the life of the view and make available all the information and actions it needs to display.
For example, you should hava a ViewModel for the whole view you are building, let's say "HeadlinePanelViewModel" (I don't recommend panel in the name because the idea of using a ViewModel is to abstract the controls or technologies being used). The HeadlinePanelViewModel needs to make the headlines available, so it must have a collection of a ViewModel representing all the information concerned to the headline (icons, titles, links, ...). In the end, you have an HeadlinePanelViewModel which contains an ObservableCollection. Set this as DataContext of your View and you must be ready to go to display your info.
Now comes the part of actually loading the info. Again, I don't know about your architecture. But in VERY simple terms, you could instantiate the filteredStream inside of your HeadlinePanelViewModel and everytime an HeadlineReceivedEvent is fired, you create an HeadlineViewModel corresponding to it and add to your collection.
"Complete" code based in the code in your answer:
The ViewModel:
public class HeadlineViewModel
{
public HeadlineViewModel()
{
// This is here only for simplicity. Put elsewhere
var config = new TwitterOAuthConfig()
{
ConsumerKey = customerKey,
ConsumerSecret = customerSecret,
AccessToken = accessToken,
AccessTokenSecret = accessTokenSecret,
GeoOnly = false,
KeywordsToMonitor = keywords,
UsersToFollow = followers
};
var filteredStream = new TwitterClient(config);
HeadlineCollection = new ObservableCollection<HeadlineDisplayItems>();
// subscribe to the event handler
filteredStream.HeadlineReceivedEvent +=
(sender, arguments) => HeadlineCollection.Add(ConvertToViewModel(arguments.Headline));
//Console.WriteLine("ID: {0} said {1}", arguments.Headline.Username, arguments.Headline.HeadlineText);
filteredStream.ExceptionReceived += (sender, exception) => Console.WriteLine(exception.HeadlineException.ResponseMessage);
filteredStream.Start();
}
private HeadlineDisplayItems ConvertToViewModel(Headline headline)
{
// Conversion code here
}
public class HeadlineDisplayItems: ObservableItem
{
private string _headlineText;
public string HeadlineIconPath { get; set; }
public string TimeStamp { get; set; }
public string Username { get; set; }
public string Text
{
get { return _headlineText; }
set
{
_headlineText = value;
RaisePropertyChangedEvent("HeadlineText");
}
}
}
public List<string> UrlsParsedFromText { get; set; }
public ObservableCollection<HeadlineDisplayItems> HeadlineCollection { get; set; }
}
The View:
<StackPanel Orientation="Vertical" Margin="5,150 5 50" Name="HeadlinePanel">
<TextBlock Text="Filtered Headlines From Monitoring List"
HorizontalAlignment="Left" Margin="0,0 5 5" Name="ScrollingHeadlineLabel" FontWeight="Bold" FontSize="14" Background="LightSkyBlue" />
<ListBox ItemsSource="{Binding HeadlineCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding HeadlineIconPath}" />
<TextBlock><Run Text="{Binding Text}"/></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
The code missing is where you do the this.DataContext = new HeadlineViewModel(); to the View.
EDIT: You may experience some problems with cross-thread operations if you try to update the observableCollection from a thread different of the view thread. A workaround is to use the solution in this link, but I don't think it's the best approach.
Create your ObservableCollection as a Property that you can Reference in XAML. Either create it directly in your MainWindow-Class or instantiate your collection as a StaticResource.
Bind your ObservableCollection as ItemsSource to your Listbox
<ListBox ItemsSource="{Binding Path=HeadlineCollection}"></ListBox>
and use an DataTemplate to bind your data to it
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image ... />
<TextBlock Text="{Binding Path=Text}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
For the Headline, create a data class that manages what you need to display (headline, icons, etc.). Something like this:
class Headline
{
bool isTwitter {get; set;}
string Text {get; set;}
}
Then in your client object you can simply add a new object to the ObservableCollection by calling the Add()-Method and the Application will automatically render the new object.
You can start your query client on the main UI thread but for a responsive UI you should let the query routine run in it's own thread (e.g. by using a BackgroundWorker) so that the UI isn't cluttered by it.

how to retrieve multiple images from web service in windows phone 7Thi

I am building an application in windows phone 7 where i need to retrieve multiple images from the web service in a single image view and the images should changes when the user swipes it. I tried it in the following way:
My xaml:
<Image Source="{Binding ImageBind }" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="79,61,72,503" Height="187" />
This is my image view where i want to display the images.
The cs code:
public class Rest
{
public string restaurant_image { get; set; }
public BitmapImage ImageBind { get; set; }
}
public const string RestXml = "Rest.xml";
public Restaura()
{
InitializeComponent();
LoadData();
}
private void LoadData()
{
bool isSuccess;
//try to load data from iso store
var doc = ReadXml(out isSuccess);
if (isSuccess) PopulateList(doc);
//if failed (data doesn't exists in iso store), download data from web service
else
{
RahmService.RahmSoapClient client = new RahmService.RahmSoapClient();
client.getRestaurantLocationAllCompleted += new EventHandler<RahmService.getRestaurantLocationAllCompletedEventArgs>(client_getRestaurantLocationAllCompleted);
client.getRestaurantLocationAllAsync();
}
}
void client_getRestaurantLocationAllCompleted(object sender, RahmService.getRestaurantLocationAllCompletedEventArgs e)
{
var doc = XDocument.Parse(e.Result);
PopulateList(doc);
WriteXml(doc);
}
Here i am not getting any result. Please help me with code
Your xaml should be this.
<ListBox Name="ListBoxProduct" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding ImageBind }" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Height="187" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In code behind
private void PopulateList(XDocument doc)
{
List<Rest> restList = new List<Rest>();
foreach (var location in doc.Descendants("UserDetails"))
{
Rest data = new Rest();
data.restaurant_image = location.Element("restaurant_image").Value;
data.ImageBind = new BitmapImage(new Uri(#" http://........"
+ data.restaurant_image, UriKind.Absolute));
restList.Add(data);
}
ListBoxProduct.ItemsSource= restList;
}
Try this.

How to display images in ListView in Win8

The problem:
There is no images being displayed.Please help to correct my mistake. Thanks
1) I have stored the photo in a folder called Images and marked the photo as content
2) I have created a class and added it in the project
class ModelImage
{
public string Image_Name { get; set; }
public string Image { get; set; }
public string Description { get; set; }
}
3) I have added the ListView and a Button in MainPage
<ListView Name="LV" ItemsSource="{Binding}" HorizontalAlignment="Left" Height="552" Margin="693,27,0,0" Grid.Row="1" VerticalAlignment="Top" Width="582">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="30" Text="Hello">
<Image Source="{Binding Image}" Height="300" Width="300">
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
--- Button
private void Button_Click_1(object sender, RoutedEventArgs e)
{
List <ModelImage> list = new List <ModelImage>();
{
new ModelImage { Image_Name = "Meat", Image = "/Images/Meat_ProkChop.jpg", Description = "Pork Chop" };
new ModelImage { Image_Name = "Meat", Image = "/Images/Meat_Beef.jpg", Description = "Beef" };
};
LV.DataContext = list;
}
Do I need to use the hardcode Path for the photo inside the folder called Images?
Try "ms-appx:///Images/Image.jpg" instead of "/Images/Image.jpg". Getting the path right is important. Since it is default, this is likely correct already, but ensure your images are marked as "Content" in properties.
Best of luck!

Data Binding Bing Maps pushpin

I'm having a bit of trouble extracting some information I need when I pass a data binding in Xaml for a windows 8 app.
I have a default template designed in the app.xaml here:
<DataTemplate x:Key="PinTemplate">
<bm:Pushpin Name="{Binding img}" IsTapEnabled="True" >
<bm:MapLayer.Position>
<bm:Location Latitude="{Binding Latitude}" Longitude="{Binding Longitude}" />
</bm:MapLayer.Position>
</bm:Pushpin>
</DataTemplate>
I want to access the individual "img" strings from the binding above. On my map I have an itemcontrol embedded like so:
<Maps:Map Name="london" HorizontalAlignment="Left" Height="546" Margin="78,34,0,0" Grid.Row="1" VerticalAlignment="Top" Width="806" Credentials="{StaticResource BingCredentials}" RenderTransformOrigin="0.439,0.282">
<Maps:Pushpin Name="myPin" Height="100" Width="100"/>
<Maps:MapItemsControl ItemTemplate="{StaticResource PinTemplate}" ItemsSource="{Binding PushpinCollection}" Height="100" Width="100" Tapped="pushPin_Tapped"/>
<Popup VerticalOffset="200" HorizontalOffset="300" x:Name="Image" IsLightDismissEnabled="True" Height="2000" Width="2000" >
<Image Name="myImage" Height="300" Width="300" Source="Assets/Logo.png"/>
</Popup>
</Maps:Map>
And the c# behind it is:
public class PushpinModel
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public string img { get; set; }
}
public ObservableCollection<PushpinModel> PushpinCollection { get; set; }
PushpinCollection = new ObservableCollection<PushpinModel>();
PushpinCollection.Add(new PushpinModel() { Latitude = templat[0], Longitude = templng[0], img = names[0] });
PushpinCollection.Add(new PushpinModel() { Latitude = templat[1], Longitude = templng[1], img = names[1] });
DataContext = this;
As it stands now I have an action control "Tapped" on the MapsItemcontrol which works properly, it creates a popup and displays a picture. However I would like to pass the information in img to the action controller so that I can specify which image to display in specific pushpins. I tried to do it like below but I think it the data context it returns is for the itemscontrol as a whole, how do I access the properties within the data template for each individual instance of pushpincollection? Thanks
private void pushPin_Tapped(object sender, TappedRoutedEventArgs e)
{
if (!Image.IsOpen) { Image.IsOpen = true; }
var pushpinData = (sender as Pushpin).DataContext as PushpinModel;
String file = pushpinData.ToString();
// use image in popup here
}
Try
var pushpinData = (sender as FrameworkElement).DataContext as PushpinModel;
That's how I usually do it, cause you don't always know what the sender type is but to me it seems it is the Pushpin..

Categories

Resources