Windows Phone Custom Live Tiles not working as Expected - windows-phone-7

I created a Windows Phone application, In that i need to create the custom tile and update it from program
I created a User control,
<TextBlock x:Name="Count" Foreground="White" FontFamily="Segoe WP Bold" FontSize="80" Text="{Binding Count}" HorizontalAlignment="Right" Margin="0,0,10,0"/>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0,0,0,0" Grid.Row="1" Text="Tile Demo" Foreground="White" FontFamily="Segoe WP Semibold" FontSize="35" />
from my MainPage.xaml.cs file i am binding the count then i am converting the User control into a .jpg file and storing in the ISO Store. Like below
public void CreateOrUpdateTile(int count)
{
CustomNotificationTile frontTile = new CustomNotificationTile();
TileData tileData = new TileData() { Count = count };
frontTile.DataContext = tileData;
//frontTile.Count.Text = count.ToString();
frontTile.Measure(new Size(173, 173));
frontTile.Arrange(new Rect(0, 0, 173, 173));
var bmp = new WriteableBitmap(173, 173);
bmp.Render(frontTile, null);
bmp.Invalidate();
var isf = IsolatedStorageFile.GetUserStoreForApplication();
var filename = "/Shared/ShellContent/Tile.jpg";
if (!isf.DirectoryExists("/Shared/ShellContent"))
{
isf.CreateDirectory("/Shared/ShellContent");
}
using (var stream = isf.OpenFile(filename, System.IO.FileMode.OpenOrCreate))
{
bmp.SaveJpeg(stream, 173, 173, 0, 100);
}
ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
//test if Tile was created
if (TileToFind == null)
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("isostore:" + filename, UriKind.Absolute),
BackBackgroundImage = new Uri("Application_TileImage_173x173.png", UriKind.Relative)
};
ShellTile.Create(new Uri("/MainPage.xaml?TileID=2", UriKind.Relative), NewTileData);
}
#region Update the Tile Not Working as expected
else
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("isostore:" + filename, UriKind.Absolute)
};
TileToFind.Update(NewTileData);
}
#endregion
}
Problem:
My problem is first i am calling from constructor to create the tile with some dummy data, Its Working as expected
But when i try to update the Tile from other methods its not working, those methods i am calling from page_load event
Those methods will fetch data from a WCF service, i am updating the Tile from the completed event of the WCF service method like below
Service.getProductsCountAsync();
Service.getProductsCountCompleted += (o,e) => {
int count = e.Result;
Dispatcher.BeginInvoke(() =>
{
CreateOrUpdateTile(count);
});
};
When the controller hit the above code, I only seeing the number with black background, Tile count is updating but the background color and logo are changing, I don't know why its happening but need to solve as early as possible.
I think the problem might be updating the UI from the background thread, but i don't know how to overcome that
Following are the images which will taken before method calls and after method calls
Before
After

Its working
We have to Bind the Background color, Logo also dynamically like count even though they are static
After that it is working as expected

Related

Xamarin Android emulator using grid inside scrollview, only first page of grid is drawn

I am developing a Xamarin Forms app in Visual Studio 2019 using the standard Android emulator. The XAML contains a ScrollView that contains a Grid. The grid merely has two columns. I have a handler for the OnStart event that connects to a database, gets a response containing 64 objects, adds two labels to the grid for each object, and populates the labels. The ScrollView's background color is set to yellow. When I debug the app using the emulator, the initial screen is drawn correctly. But when I scroll down, the grid is not filled in. The background is still yellow, showing that the scrollview is the correct size (or at least, big enough to hold more labels). What am I doing wrong?
When I put a breakpoint at the bottom of the second for loop, after adding the labels, I see that I have all of the label objects I expect, and they have the text properties set as I expect.
Here's the XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CapsBases.MainPage">
<ScrollView BackgroundColor="Yellow"
VerticalOptions="FillAndExpand">
<Grid Margin="10"
x:Name="BasesGrid">
<!-- two columns: base ID and status -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</ScrollView>
</ContentPage>
The event handler calls a function named ShowBases() in the MainPage.xaml.cs file. Here it is:
public async void ShowBases()
{
string url = "http://10.0.2.2:5000/caps/bases";
//HttpClient client = new HttpClient();
//string info = await client.GetStringAsync(url);
//// await DisplayAlert("Coil Count", message, "Close");
//// The base ID and string will be returned in a single string with a space separating them.
//string[] pieces = info.Split(' ');
//BaseID.Text = pieces[0];
//BaseStatus.Text = pieces[1];
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
//IEnumerable<Base> baseList = JsonConvert.DeserializeObject<IEnumerable<Base>>(data);
List<Base> baseList = JsonConvert.DeserializeObject<List<Base>>(data).OrderBy(b =>b.BaseName).ToList();
//BaseID.Text = theBase.BaseName;
//BaseStatus.Text = theBase.Status;
string message = $"Retrieved {baseList.Count()} bases.";
await DisplayAlert("Result", message, "Close");
Grid theGrid = BasesGrid;
var rowDefinitions = theGrid.RowDefinitions;
for (int i = 0; i < baseList.Count(); i++)
{
RowDefinition rowDef = new RowDefinition { Height = 20 };
rowDefinitions.Add(rowDef);
}
await DisplayAlert("Row count", $"The grid has {BasesGrid.RowDefinitions.Count()} rows.", "Close");
// We have the grid with all the rows we need. Now we have to create a label for each grid cell.
try
{
for (int i = 0; i < baseList.Count(); i++)
{
BasesGrid.Children.Add
(
new Label
{
Text = baseList[i].BaseName,
HorizontalOptions = LayoutOptions.Start
},
0, i
);
BasesGrid.Children.Add
(
new Label
{
Text = baseList[i].Status,
HorizontalOptions = LayoutOptions.Start
},
1, i
);
}
}
catch (Exception ex)
{
await DisplayAlert("Error", $"Failed to add row data: {ex.Message}", "Close");
}
}
else
{
await DisplayAlert("Get Coils failure",
$"Failed to retrieve coils; status code: {response.StatusCode}",
"Close");
}
}
}
}
Surround the Grid in a StackLayout of vertical orientation. The Grid is not expanding inside the ScrollView.

Xamarin Forms - Change View's location in a Grid

I have a grid on my page with a few items.
I want to change the location/span of these items in C#.
On Xaml I can do this:
<Grid>
<Label Grid.Row="1" Grid.RowSpan="2"/>
</Grid>
I could do grd.Children.Remove and then grd.Children.Add in the new position, but is there another way?
public MyPage()
{
var grid = new Grid();
var lbl1 = new Label { Text= "lbl1" };
var lbl2 = new Label { Text = "lbl2" };
grid.Children.Add(lbl1);
grid.Children.Add(lbl2);
Grid.SetRow(lbl1, 1);
Grid.SetRow(lbl2, 0);
this.Content = grid;
}
However I would recommend not to do it dynamically but once in the constructor. More info in the official doc.

Displaying an image with Xamarin Forms

Solved: The answer was to update all of the nuget packages and target a newer version of Android. Now images loads as expected. I'm not happy with this as I was using exactly the code that Xamarin provided and targeting newer versions has deprecated some of the items the code relys on. Initial version was Xamarin.Forms v23 and I updated to V25
I have a brand new Xamarin forms project with a simple view in which I'm trying to display an image. I've tried several ways of getting an image to display and I am not having any luck at all.
I'm using <image> and I have also tried FFImageLoader control as well.
<StackLayout Orientation="Vertical">
<ff:CachedImage Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" WidthRequest="100" HeightRequest="100" />
<Button x:Name="btn" Text="Image" Clicked="Button_Clicked" />
<Frame OutlineColor="Red">
<Image x:Name="StupidImage" Source="{Binding Thumbnail}" Aspect="Fill" HeightRequest="100" WidthRequest="100" />
</Frame>
</StackLayout>
This is the current view. I've also set the Source directly to a value with no result.
I'm able to get a stream for the image. I'm able to read all of the bytes from the stream. I built a debug visualizer to display the bytes as an image. Getting the image from a source is not a problem. Getting the image control(s) to display the image is a problem.
I tried binding with a view model. When that failed, I tried that directly setting the source
StupidImage.Source = ImageSource.FromStream(() => result.Stream);
I also made a copy of the bytes and tried
StupidImage.Source = ImageSource.FromStream(() => new MemoryStream(imageBytes));
I've tried ImageSource.FromFile() and .FromUri. I tried adding an image to the project as a resource. Each try was the same, the resource was read and the bytes were available, but the image control just doesn't display it.
I thought maybe it was a size problem, so I set the size of the control. Nothing. I thought maybe it was a resolution problem, so I used a smaller image. I tried several different images of varying quality.
Then I gave up on the image control and I got the FFImageLoading nuget package and gave it a direct url to an image. Same example that FFImageLoading examples used. Still no image.
I tried the emulator and I tried 2 different physical devices. Same result.
I also tried setting an image on a button using btn.Image = "whatever.jpg" with the same result.
This is the result every time. I'm lost. How do I get images to display?
EDIT:
I did get this to work, but only on the emulator
<Image x:Name="StupidImage" Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" />
and same for
StupidImage.Source = ImageSource.FromUri(new Uri("https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg"));
EDIT 2 - Clarification
My goal is to allow the user to select a photo from the device and then display a preview of it.
If you want to use images in you app you can load them into your Shared Project, like
Make sure you change the Build Action to Embedded resource
Then in your code
image.Source = ImageSource.FromResource("App5.Images.useravatar.png");
Note the Resource name.
And XAML
<ContentPage.Content>
<StackLayout>
<Image x:Name="image" WidthRequest="50"/>
</StackLayout>
</ContentPage.Content>
Just a few things you can take off the list:
[x] Adding a image from Visual studio :
Right click on the correct folder
select Add >> New File ...
NB: you have to add it with visual studio and not just throw it in the folder. Visual studio needs to know about it
[x] When Adding the image is it in the correct place :
For android: it has to be in
ProjectName.Driod.Resources.drawable folder
For ios: it has to be in
ProjectName.iOS.Resources folder
[x] Naming Convention
Its always best to use .png , all lowercase , no spaces or special char on both android and ios
with ios you normally get 3 images of the same image with the following namting convention
theman.png
theman#2x.png
theman#3x.png
They are all the same image just different sizes
[x] Showing it in xaml :
<StackLayout>
<Image Source="thedog.png" HeightRequest="100" WidthRequest="100" />
</StackLayout>
In your example you used a frame , how about a stacklayout ? a frame has more requirements.
for MVVM you can change Source with the following , dont forget that twoway :)
Source="{Binding Thumbnail, Mode=TwoWay}"
NB This is VERY basic explanations
You can try implementing the CrossMedia Plugin.
Then in your button clicked code section, put the following:
Button_Clicked.Clicked += async (sender, args) =>
{
if ( !CrossMedia.Current.IsPickPhotoSupported )
{
DisplayAlert("Error message here", "More message", "OK");
return;
}
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
if (file == null)
return;
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
};
Once the button is clicked, the gallery/directory will be displayed. You can choose the photo you want. Once you hit OK the image will be displayed in the Image control/tag. I'm not sure if this is the solution you are looking for. Hopes it gets you on the right direction.
This may or may not help I'll add some code, one of the surprising things about Xamarin forms and Android and using a memory stream.. is that the device density multiplier is still applied even if you aren't using a resource(If I am remembering correctly) so I would imagine if you are looking at the ADB interface you will see memory issues which is why you cant display an image... I solved this previously via sampling
The way I solved it was creating a new Image subclass -ResourceImage,
public class ResourceImage :Image
{
public enum SourceTypes{
Database,
File,
Function,
}
private bool _LoadAct = false;
public bool LoadAct { get{
return _LoadAct;
}
set{ _LoadAct = value; OnPropertyChanged ("LoadAct");
}
}
public Func<Stream> Func{ get; set; }
public SourceTypes SourceType{ get; set;}
public string ResName{ get; set;}
public ResourceImage ()
{
}
public ResourceImage (string name)
{
ResName = name;
}
public ResourceImage(Func<Stream> func){
SourceType = SourceTypes.Function;
Func = func;
}
}
then in the Android Renderer : I did the following
public class ResourceImageRenderer : ImageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Image> e)
{
base.OnElementChanged (e);
if (e.OldElement == null)
{
var el = (ResourceImage)Element;
if (el.SourceType == ResourceImage.SourceTypes.Database) {
//Ignore for now
} else if (el.SourceType == ResourceImage.SourceTypes.File) {
using (global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ()) {
options.InJustDecodeBounds = false;
options.InSampleSize = 1;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
var gd = Context.Resources.GetIdentifier (el.ResName.Split (new char[]{ '.' }) [0], "drawable", Context.PackageName);
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
var bitmap = global::Android.Graphics.BitmapFactory.DecodeResource (Context.Resources, gd, options);//DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}
}
} else if (el.SourceType == ResourceImage.SourceTypes.Function) {
new Task (() => {
var ms = el.Func();
if(ms == null)return;
global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ();
options.InJustDecodeBounds = false;
options.InSampleSize = 2;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
ms.Position = 0;
Device.BeginInvokeOnMainThread(()=>{
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
try{
var bitmap = global::Android.Graphics.BitmapFactory.DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}catch(Exception eee){
}
}
});
}).Start();
}
}
}
Looking back at the code(haven't touched it in years.) there are plenty of places for improvement, I had to add the sampling to solve the same issue , users were selecting images to display in a messaging app and it worked perfectly on iOS just never displayed on Android
This is how I allow a user to select an image and then display it on a page.
I call my image service Select Image method passing in a callback method
await _imageService.SelectImage(ImageSelected);
This is my SelectImage method. There is some permission checking at the start. It uses the Media Plugin to display the gallery and allow the user to select an image.
public async Task SelectImage(Action<MediaFile> imageAction)
{
var allowed = await _permissionService.CheckOrRequestStoragePermission();
if (!allowed) return;
if (!_media.IsPickPhotoSupported)
{
throw new GalleryUnavailableException("Gallery unavailable");
}
var file = await _media.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Small,
CompressionQuality = 92
});
imageAction(file);
}
It returns a MediaFile
Here is the Image Selected callback method
private void ImageSelected(MediaFile image)
{
if (image == null)
{
return;
}
ChosenImage = new IncidentImage
{
ImageBytes = image.ToByteArray()
};
}
ChosenImage is a Property in my view model
public IncidentImage ChosenImage {get; set;}
I use PropertyChanged.Fody to trigger property changed notifications but you can also use INotifyPropertyChanged.
And IncidentImage is a class I use to both store and display images
public class IncidentImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int IncidentDetailsId { get; set; }
public byte[] ImageBytes { get; set; }
[Ignore]
public ImageSource ImageSource
{
get
{
ImageSource retval = null;
try
{
if (ImageBytes != null)
{
retval = ImageSource.FromStream(() => new MemoryStream(ImageBytes));
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return retval;
}
}
}
And here is the XAML
<Image Source="{Binding ChosenImage.ImageSource}"
Aspect="AspectFit"/>

How to fill a rectangle with an image that is coming from a bound listbox on event fire in WP7

i want to create a greeting card maker app for WP7, when a user double taps an image from a listbox, I want that selected image to fill a rectangle on the same page.
Im passing 50 images into the list box like this:
public GCM()
{
InitializeComponent();
var articles = new List<Article>();
for (byte i = 1; i <= 20; i++)
{
Article article = new Article() { Name = "name"+i, ImagePath = "Assets/Images/Backgrounds/"+i+".jpg" };
articles.Add(article);
}
listBox1.DataContext = articles;
}
and its working fine, now heres an xml snippet:
<Rectangle Fill="#FFF4F4F5" Margin="28,24,30,148" Stroke="Black" Name="rect1" />
................(more code here).........................
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10" >
<Image Name="bgs" Source="{Binding ImagePath}" Height="90" Width="90" DoubleTap="Load_BG" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
how can i fill the rectangle with the tapped image? this code (below) sets the string null everytime, no matter which image i select, although to my knowledge each has a different name and it should give different names for different images. I will use the name of the image to fill the rectangle. What am i doing wrong?
private void Load_BG(object sender, System.Windows.Input.GestureEventArgs e)
{
string abc = sender.GetType().Name;
}
Please excuse me if the solution is obvious..this is my first app ever. Thank you!
sender parameter should contains the control that triggered the event, in this case Image control. Try to cast it to Image type, then you can get the information you needed from Source property :
private void Load_BG(object sender, System.Windows.Input.GestureEventArgs e)
{
Image img = (Image)sender;
//do something with img.Source here
}
so heres the solution, it works now :)
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var img = listBox1.SelectedItem as Article;
ImageBrush imgBrush = new ImageBrush();
imgBrush.ImageSource = new BitmapImage(new Uri(img.ImagePath, UriKind.RelativeOrAbsolute));
rect1.Fill = imgBrush;
}

Load data to a list collection from isolated storage fails

I have stored nearly 150 high resolution images into isolated storage with 789x 1299 resolution. MY problem is when I load 60 - 70 images into the list collection it works fine but when it more than 70 Out of memory exception occurs in the bi.SetSource(ms). I am using the virtualizing satck panel in the item template. Whats the reason
List<SampleData> data = new List<SampleData>();
try
{
for (int i = 0; i < 150; i++)
{
byte[] data2;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = isf.OpenFile("IMAGES" + i + ".jpg", FileMode.Open, FileAccess.Read))
{
data2 = new byte[isfs.Length];
isfs.Read(data2, 0, data2.Length);
isfs.Close();
}
}
MemoryStream ms = new MemoryStream(data2);
BitmapImage bi = new BitmapImage();
bi.SetSource(ms);
data.Add(new SampleData() { Name = bi });
}
this.list.ItemsSource = data;
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public class SampleData
{
public ImageSource Name
{
get;
set;
}
}
}
}
<ListBox x:Name="list" Width="480">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Image Height="500" Width="500" Source="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I believe by creating the BitmapImages first, WP7 is allocating the memory for each image up front, rather than when the item in the listbox scrolls into view. Try storing the byte array, rather than the BitmapImage, in the SampleData class, and then create the BitmapImage when called, via a property.
So SampleData would look something like this:
public class SampleData
{
public byte[] ImgData {get;set;}
public BitmapImage Image
{
get
{
BitmapImage bi = new BitmapImage();
MemoryStream ms = new MemoryStream(ImgData);
bi.SetSource(ms);
return bi;
}
}
}
Being that you have many high resolution images, you may still run into performance issues - I might suggest storing lower resolution versions of those images for the ListBox, and then displaying the high resolution image when the user needs to view that particular image? Hope this helps!

Resources