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..
Related
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.
Ad[a,o]pting Brundritt's example here, which he referenced in responding to my earlier BingMaps question, I have been able to display certain data in an "infobox" like so:
...but am not getting the BitmapImage that is part of the data I intend to display.
My question is why are the images not displaying, and what do I need to go in order to display them?
It's not the data - I've saved the thumbnail image to a SQLite database, where it is stored as an array of bytes, but which can be seen as images in SQLiteToolbox:
:
So as you can see, the String data is displaying fine, but not the image. Here is the pertinent code I've got for retrieving the data and displaying it:
// The class used to create the SQLite table
public class PhotraxBaseData
{
[SQLite.PrimaryKey]
[SQLite.AutoIncrement]
public int Id { get; set; }
. . .
public DateTime dateTimeTaken { get; set; }
public String filePath { get; set; }
public Byte[] thumbnail { get; set; }
}
private async void Gener8MapMarkers(List<PhotraxBaseData> _pbd)
{
foreach (PhotraxBaseData pbd in _pbd)
{
String descAndFilename = Path.GetFileName(pbd.filePath);
if (null != pbd.imgDescription)
{
descAndFilename = String.Format("{0} - {1}", pbd.imgDescription, descAndFilename);
}
BitmapImage bmi = await PhotraxUtils.ByteArrayToBitmapImage(pbd.thumbnail);
if (PhotraxUtils.ValidLatAndLong(pbd.latitude, pbd.longitude))
{
Location loc = new Location(pbd.latitude, pbd.longitude);
AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, null, DataLayer);
}
}
}
public static async Task<BitmapImage> ByteArrayToBitmapImage(this byte[] byteArray)
{
// from http://dotnetspeak.com/2013/04/convert-byte-array-to-an-image-in-winrt
if (byteArray != null)
{
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(byteArray.AsBuffer());
var image = new BitmapImage();
stream.Seek(0);
image.SetSource(stream);
return image;
}
}
return null;
}
public class PushpinMetadata
{
public string DescAndFileName { get; set; }
public DateTime DateTimeTaken { get; set; }
public BitmapImage Thumbnail { get; set; }
}
public void AddPushpin(Location latlong, string descAndFileName, DateTime dateTimeTaken, BitmapImage thumbnail, MapLayer layer)
{
Pushpin p = new Pushpin()
{
Tag = new PushpinMetadata()
{
DescAndFileName = descAndFileName,
DateTimeTaken = dateTimeTaken,
Thumbnail = thumbnail
}
};
MapLayer.SetPosition(p, latlong);
p.Tapped += PinTapped;
layer.Children.Add(p);
}
private void PinTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
Pushpin p = sender as Pushpin;
PushpinMetadata m = (PushpinMetadata)p.Tag;
//Ensure there is content to be displayed
if (!String.IsNullOrEmpty(m.DescAndFileName))
{
Infobox.DataContext = m;
Infobox.Visibility = Visibility.Visible;
MapLayer.SetPosition(Infobox, MapLayer.GetPosition(p));
}
else
{
Infobox.Visibility = Visibility.Collapsed;
}
}
The XAML:
<bm:Map.Children>
<!-- Data Layer-->
<bm:MapLayer Name="DataLayer"/>
<!--Common Infobox-->
<bm:MapLayer>
<Grid x:Name="Infobox" Visibility="Collapsed" Margin="0,-115,-15,0">
<Border Width="300" Height="110" Background="Black" Opacity="0.8" BorderBrush="White" BorderThickness="2" CornerRadius="5"/>
<StackPanel Height="100" Margin="5">
<Grid Height="40">
<TextBlock Text="{Binding DescAndFileName}" FontSize="20" Width="250" TextWrapping="Wrap" HorizontalAlignment="Left" />
<Button Content="X" Tapped="CloseInfobox_Tapped" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
<Grid Height="40">
<Viewbox Height="200" Stretch="Uniform" StretchDirection="Both">
<Image Source="{Binding Thumbnail}" Width="Auto" Height="Auto" Margin="2"/>
</Viewbox>
</Grid>
<Grid Height="40">
<TextBlock Text="{Binding DateTimeTaken}" FontSize="16" Width="290" TextWrapping="Wrap" Height="Auto"/>
</Grid>
</StackPanel>
</Grid>
</bm:MapLayer>
<callisto:CustomDialog Content="CustomDialog" Height="100" Width="100"/>
</bm:Map.Children>
My guess is that the Image's Source property doesn't quite know what to do with the BitmapImage it's bound to via "Thumbnail"; but I don't know how to unbabelize the mismatch (presuming that's where the problem lies).
UPDATE
To answer Faye's brother Chris, here is the "database" (SQLite class) declaration:
public Byte[] thumbnail { get; set; }
Here is where I was calling the converter method, passing the appropriate member of the class instance, and then adding a pushpin:
BitmapImage bmi = await PhotraxUtils.ByteArrayToBitmapImage(pbd.thumbnail);
if (PhotraxUtils.ValidLatAndLong(pbd.latitude, pbd.longitude))
{
Location loc = new Location(pbd.latitude, pbd.longitude);
//AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, null, DataLayer);
AddPushpin(loc, descAndFilename, pbd.dateTimeTaken, bmi, DataLayer);
}
However: MY BAD, PEOPLES!!!
I had neglected to replace my placeholder "null" arg with "bmi" (you can see my previous code commented out above, and my new working call to AddPushpin() below it). The image's size/scale is all wrong, but that can be fixed easily enough, I reckon.
I don't know whether to jump for joy or kick myself in the keister, so I think I'll do both simultaneously.
Thanks to Mr. Dunaway for making me look closer at my code - something I obviously should have done before posting.
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.
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.
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!