Custom tile generates a System.OutOfMemoryException exception in wp8 - c#

Has anyone got any idea why I'm getting an OutOfMemoryException when I'm creating my custom tile?
I'm trying to create custom images for my primary tile on a windows phone 8 app from a ScheduledAgent. The error doesn't occur until my very last line of code is executed which is the NotifyComplete().
Here is the code (Not the cleanest but ok for prototyping I guess). This code only handles the wide tile and it tries to load an image an image downloaded from a website and then it tries to render a logo and a description over this image.
Here is the code:
private void CreateTiles()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
for (int i = 0; i < 2; i++)
{
var bmp = new WriteableBitmap(691, 336);
var articleImg = new BitmapImage(new Uri(articles[i].ImageFilename, UriKind.Relative));
var articleImage = new Image { Source = articleImg };
articleImage.Stretch = Stretch.UniformToFill;
articleImg.CreateOptions = BitmapCreateOptions.None; // Force the bitmapimage to load it's properties so the transform will work
var bmpLogo = new WriteableBitmap(100, 100);
var logoImg = new BitmapImage(new Uri("/Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative));
var logoImage = new Image { Source = logoImg };
logoImage.Opacity = 1.0;
logoImg.CreateOptions = BitmapCreateOptions.None; // Force the bitmapimage to load it's properties so the transform will work
var articleBannerGrid = new Grid();
articleBannerGrid.Background = ColorExtensions.ToSolidColorBrush("#000F558E");
articleBannerGrid.Opacity = .5;
articleBannerGrid.Height = 100;
articleBannerGrid.VerticalAlignment = VerticalAlignment.Bottom;
articleBannerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
articleBannerGrid.ColumnDefinitions.Add(new ColumnDefinition());
articleBannerGrid.Children.Add(logoImage);
Grid.SetColumn(logoImage, 0);
var textBlock = new TextBlock();
textBlock.Text = articles[i].Description;
textBlock.FontWeight = FontWeights.Bold;
textBlock.Margin = new Thickness(10, 5, 30, 5);
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Foreground = new SolidColorBrush(Colors.White); //color of the text on the Tile
textBlock.FontSize = 30;
textBlock.Opacity = 1.0;
var articleTextGrid = new Grid();
articleTextGrid.Height = 100;
articleTextGrid.VerticalAlignment = VerticalAlignment.Bottom;
articleTextGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
articleTextGrid.ColumnDefinitions.Add(new ColumnDefinition());
articleTextGrid.Children.Add(textBlock);
Grid.SetColumn(textBlock, 1);
var canvas = new Grid();
canvas.Width = articleImg.PixelWidth;
canvas.Height = articleImg.PixelHeight;
canvas.Children.Add(articleImage);
canvas.Children.Add(articleBannerGrid);
canvas.Children.Add(articleTextGrid);
bmp.Render(canvas, null);
bmp.Invalidate(); //Draw bitmap
FileStream fs = new FileStream(articles[i].ImageFilename, FileMode.Create);
bmp.SaveJpeg(fs, bmp.PixelWidth, bmp.PixelHeight, 0, 100);
fs.Close();
fs.Dispose();
articleImage = null;
articleImg = null;
}
//GC.Collect();
//GC.WaitForPendingFinalizers();
ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault();
if (TileToFind != null)
{
string title = articles[0].Tag;
string backTitle = articles[1].Tag;
string content = articles[0].Description;
string backContent = articles[1].Description;
FlipTileData tileData = new FlipTileData()
{
Title = title,
BackTitle = backTitle,
BackContent = backContent,
WideBackContent = backContent,
BackgroundImage = new Uri(articles[0].ImageFilename, UriKind.Relative),
BackBackgroundImage = new Uri(articles[1].ImageFilename, UriKind.Relative),
WideBackgroundImage = new Uri(articles[0].ImageFilename, UriKind.Relative),
WideBackBackgroundImage = new Uri(articles[1].ImageFilename, UriKind.Relative),
};
TileToFind.Update(tileData);
}
NotifyComplete();
});
}
I assume that it is the correct place to generate the custom tile i.e. ScheduledAgent.
I found an article Dynamic Live Tile issue WP8 [WriteableBitmap] on the Nokia website with the same problem but no solution there either.
I'll continue debugging this tomorrow by removing everything and adding each bit individually step by step to see if I can spot anything but if anyone has got any suggestion or solution, I'd appreciate if you could help.
If I'm going about it the wrong way, please let me know what's the best method to update tiles in the scheduled agent.
Thanks.

WP BackgroundAgents have a small memory cap. 20MB for WP8 without update 3 and 25 for WP8 with Update 3.
I would suggest that you create a another project and add Coding4Fun toolkit and MemoryCounter in to your page and then add the code in your BackgroudAgent to the sample page and try to create the tiles. Then look at how much memory it uses. I think amount of memory usage is higher than the 20/25MB cap. if so you have to find a way to reduce it.

As usual, Microsoft is omitting critical information from its documentation.
After reducing the size of my tile from 691x336 to 336x165, it worked, so it got me thinking and I thought that the size recommended by Microsoft in this article Flip Tile template for Windows Phone 8 for wp8 and wp8.1 seemed excessive, so I did a bit more research and this is when I found this excellent explanation in an article in stackoverflow i.e.
Windows Phone 8 Startscreen Tile sizes and margins
This clearly states the size of the tiles but not based on OS versions but based on screen resolutions.
In my test environment, I was using the default 'Emulator WVGA 512MB' and again by default the screen size is 480x800, so taking into account the information provided in the answer of this article, my tile size should have been 430x210.
I resized my (already reduced) tile back up to 430x210, and it still worked.
While there is a memory cap of 20/25Mb depending on OS and patch being used, which probably causes many problems from the various articles I've read on the web, in my instance I believe it was more likely to have been affected by tile size being incorrect, and therefore the additional memory, or lack thereof.
I will update this article once I get to using 8.1 in my IDE with a larger resolution but for now, I have to assume that it was definitely related to the size of my tile.
I'll definitely make sure to add code to create tile based on os, patch and resolution being used. Bit of a pain to best honest!
Hope this helps.

Related

Having issues when attempting to load images into a bitmap/Picture Box Visual Studio 2019

I am attempting to load images into an Picture Box using the following code:
private void button1_Click(object sender, EventArgs e)
{
//Clear the Image Area
if (templateArea.Image != null)
{ templateArea.Image = null; }
//Set Base Variables
Bitmap img = new Bitmap(Resources.BlankBackground);
Graphics gpx = Graphics.FromImage(img);
Bitmap newBitmap = null;
string bitmapToLoad;
ResourceManager rm;
//Loop Lists
for (int i = 0; i < comps.Count; i++)
{
bitmapToLoad = Convert.ToString(comps[i][3]);
rm = Resources.ResourceManager;
newBitmap = (Bitmap)rm.GetObject(bitmapToLoad);
gpx.DrawImage(newBitmap,
Convert.ToInt32(comps[i][4]),
Convert.ToInt32(comps[i][5]),
newBitmap.Width,
newBitmap.Height);
gpx.Dispose();
}
//Set Image Area to newBitmap
templateArea.Image = newBitmap;
}
when trying to run the code I get errors on the second loop where i = 1 in this portion:
gpx.DrawImage(newBitmap,
Convert.ToInt32(comps[i][4]),
Convert.ToInt32(comps[i][5]),
newBitmap.Width,
newBitmap.Height);
//given error: System.ArgumentException: 'Parameter is not valid.' //Does not point to anything specific.
the comps list contains lists formatted as such:
//These listed items are defaulted at program start but more are added while running
// p elementName type sub type x y sX sY R G B O i
eleComps = new List<Object> {0,"Background","Static","BlankBackground",0,0,100,100,50,50,50,0,0};
comps.Add(eleComps);
eleComps = new List<Object> {0,"Border", "Static","CardBorder_02", 0,0,100,100,50,50,50,0,0};
comps.Add(eleComps);
eleComps = new List<Object> {0,"Cut Line", "Static","00_Card_CutLine",0,0,100,100,50,50,50,0,0};
comps.Add(eleComps);
Each possible "sub type" is a bitmap file in resources, and there are more than just those listed above.
What I have tried:
I have consulted the miracle that is Google and YouTube and haven't been able to solve this one. Although I feel like I'm missing something basic I have been looking at it all day and may need some fresh eyes on it to correct something that may be obvious but for some reason I seem to be missing.
What I am looking for:
I am trying to create an image viewer that will overlap images or parts of images (.png) at specific coordinates in an Picture Box. The comps list will be added to and removed from actively during application use and as such using a variable to reference the Resources is necessary and that portion of the code has given me the most difficult time out of the supplied code samples.
UPDATE 1: changed image box to picture box to reduce confusion.
UPDATE 2: NOTE: This sample achieves the desired end result, but does not allow me to use the variables as needed to change positions, scale, color, and opacity.
//Clear Img Area
templateArea.InitialImage = null;
//Find Bitmap(s)
string p = #"C:\Users\david\Desktop\Test Fields\Default\";
Bitmap img1 = new Bitmap(p + "00_Empty.png", true);
Bitmap img2 = new Bitmap(p + "CardBorder_02.png", true);
Bitmap img3 = new Bitmap(p + "00_Card_CutLine.png", true);
//Set initial image for image area
Graphics gpx = Graphics.FromImage(img1);
//place an image at x, y, Width, Height
gpx.DrawImage(img2, 0, 0, img2.Width, img2.Height);
gpx.DrawImage(img3, 0, 0, img3.Width, img3.Height);
//clear gpx cache
gpx.Dispose();
//set image area to modified img1
templateArea.Image = img1;
Also please let me know if I'm just plain going about this in the wrong way, and what I need to do to get it right.
UPDATE 3: I seem to have gotten it to work properly with this variation, using the originally supplied list of listed parameters. Thanks to Idle_Mind's pointers I was able to figure it out.
Bitmap newBitmap_01 = new Bitmap(templateArea.Width, templateArea.Height);
Graphics gpx = Graphics.FromImage(newBitmap_01);
string bitmapToLoad;
ResourceManager rm = Resources.ResourceManager;
Bitmap newBitmap_02;
for (int i = 0; i < comps.Count; i++)
{
bitmapToLoad = Convert.ToString(comps[i][3]);
newBitmap_02 = (Bitmap)rm.GetObject(bitmapToLoad);
gpx.DrawImage(newBitmap_02,
Convert.ToInt32(comps[i][4]),
Convert.ToInt32(comps[i][5]),
newBitmap_02.Width,
newBitmap_02.Height);
}
gpx.Dispose();
templateArea.Image = newBitmap_01;

PictureBox not sizing properly

I created a PictureBox and load an image into it, and I wanted the pictures to have a maximum size (let's say 250px). Here is the code I'm using at that moment
PictureBox cellPictureBox = new PictureBox();
cellPictureBox.AutoSize = false;
cellPictureBox.Dock = DockStyle.Fill;
cellPictureBox.SizeMode = PictureBoxSizeMode.Zoom;
cellPictureBox.Image = Base64ToImage(data.ToString().Trim());
cellPictureBox.Width = 250;
cellPictureBox.Height = 250;
When I load the first image, it's quite large. When I load a second image, it's automatically scaled down to be very tiny, and the newly loaded image takesthe large size the first image had. This trend continues as I add more rows to my data.
What can I do to help manage the sizing of my images? Making a custom control I've been told is useful, but I don't see what is happening that I have wrong.
To set maximum and minimum sizes, have a look at the following:
PictureBox cellPictureBox = new PictureBox();
cellPictureBox.AutoSize = false;
cellPictureBox.Dock = DockStyle.Fill;
cellPictureBox.SizeMode = PictureBoxSizeMode.Zoom;
cellPictureBox.Image = Base64ToImage(data.ToString().Trim());
cellPictureBox.MinimumSize = new Size(100, 100); // or whatever size you want.
cellPictureBox.MaximumSize = new Size(250, 250);
If you only want to set a limit on one dimension, for example, the width, but the height is allowed to be anything, then use int.MaxValue:
cellPictureBox.MaximumSize = new Size(250, int.MaxValue);

PrintFixedDocument wpf print quality- Windows 10/8 vs Windows 7

I am currently trying to print the contents of a content container (it only contains datagrids with information) and an image using PrintFixedDocument. It prints flawlessly on my machine (windows 10) with full image quality and on another pc which is windows 8, the quality is the same.
However when this is done on a Windows 7 pc the image quality becomes very poor and the final result is very blurred. This is a problem as the computer cannot be updated from windows 7 for various reasons, so im wondering if anyone else has experienced this and if so is there a workaround? Also could be an issue with my GetFixedDocument method, though I cannot work out why this would work on both win 10 and 8 but not 7.
NOTE THIS IS RUNNING FROM AN INSTALLED VERSION OF THE APPLICATION ON EACH PC
ALSO BEEN TRIED ON MULTIPLE PRINTERS ON ALL 3 OPERATING SYSTEMS
Any help would be appreciated
Xaml:
<StackPanel Margin="-105,146,66,0" Height="900" VerticalAlignment="Top" x:Name="PrintImageContextMenu">
<Image Canvas.ZIndex="0" Source="{Binding Coupon.OverlayImagePath}" Margin="0,-21,-76,108" Stretch="Fill" />
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource DataViewerDataTemplateSelector}" />
</StackPanel>
C#:
public partial class CouponViewerView
{
public CouponViewerView()
{
InitializeComponent();
}
public void Print()
{
//Executes On Thread
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (EventHandler)delegate
{
UpdateLayout();
var fixedDoc = PrintHelper.GetFixedDocument(StackPanelToPrint, new PrintDialog());
PrintHelper.ShowPrintPreview(fixedDoc);
}, null, null);
}
private void PrintCurrentForm(object sender, RoutedEventArgs e)
{
Print();
}
C# Print helper code:
public static void ShowPrintPreview(FixedDocument fixedDoc)
{
var wnd = new Window();
var viewer = new DocumentViewer();
viewer.Document = fixedDoc;
wnd.Content = viewer;
wnd.ShowDialog();
}
public static FixedDocument GetFixedDocument(FrameworkElement toPrint, PrintDialog printDialog)
{
var capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
var pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
var visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
var fixedDoc = new FixedDocument();
//If the toPrint visual is not displayed on screen we neeed to measure and arrange it
toPrint.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
toPrint.Arrange(new Rect(new Point(0, 0), toPrint.DesiredSize));
//
var size = toPrint.DesiredSize;
//Will assume for simplicity the control fits horizontally on the page
double yOffset = 0;
while (yOffset < size.Height)
{
var vb = new VisualBrush(toPrint)
{
Stretch = Stretch.None,
AlignmentX = AlignmentX.Left,
AlignmentY = AlignmentY.Top,
ViewboxUnits = BrushMappingMode.Absolute,
TileMode = TileMode.None,
Viewbox = new Rect(0, yOffset, visibleSize.Width, visibleSize.Height)
};
var pageContent = new PageContent();
var page = new FixedPage();
((IAddChild)pageContent).AddChild(page);
fixedDoc.Pages.Add(pageContent);
page.Width = pageSize.Width;
page.Height = pageSize.Height;
var canvas = new Canvas();
FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
canvas.Width = visibleSize.Width;
canvas.Height = visibleSize.Height;
canvas.Background = vb;
page.Children.Add(canvas);
yOffset += visibleSize.Height;
}
return fixedDoc;
}
anyone else has experienced this and if so is there a workaround?
That's the only directly answerable question, yes, many. Keep in mind that you often substantially rescale an image on a printer, they are devices with very high dots-per-inch resolution compared to a monitor. A machine that boots Win7 often runs at 96dpi, later ones tend to have better monitors so are often run at higher dpi settings. First thing to watch out for is the source image. If it has a pixel size that's adequate for that Win7 PC then it can get very blurry when it is blown-up to 600 dpi.
Probably the most unintuitive scaling behavior in WPF is what happens when the image alignment does not perfectly match a target pixel after scaling. A problem described well in this blog post. Be sure to also read this SO question, an almost perfect fit for your usage of VisualBrush and its blurriness problem. This problem was addressed in .NET 4.0 with the added UseLayoutRounding property. You are not using it, you definitely should. Don't blindly apply the BitmapScalingMode that the dup recommends, the type of image (line-art vs photo) matters a lot.
I had a similar issue I ended up instead of directly printing to creating a PDF and having that open for the client and they could print it if they wanted just fine.
Windows 7 just seems broken with some of the WPF printing.

Button image sides cropped

I'm trying to fit image to button perfectly.
But the image is cropped on its right and bottom faces, see attached print screen:
I edited the button as follows:
var l_oStopImage = Image.FromFile(#"C:\Users\AmitL\Downloads\Button-2-stop-icon72p.png");
var l_oStopPic = new Bitmap(l_oStopImage , new Size(btnStopOperation.Width, btnStopOperation.Height));
btnStopOperation.Image = l_oStopPic ;
btnStopOperation.ImageAlign = System.Drawing.ContentAlignment.MiddleCenter;
btnStopOperation.TabStop = false;
btnStopOperation.FlatStyle = FlatStyle.Flat;
btnStopOperation.FlatAppearance.BorderSize = 0;
I also tried to edit the BackgroundImageLayout but none of the ImageLayouts fixed the problem..
Any suggestions?
Thanks in advance
1https://msdn.microsoft.com/en-us/library/system.windows.forms.imagelayout(v=vs.110).aspx
You should use stretch, I suggest in designtime (this is not java where you have to add elements by code):
this.buttonOk.BackColor = System.Drawing.SystemColors.MenuHighlight;
this.buttonOk.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("buttonOk.BackgroundImage")));
this.buttonOk.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.buttonOk.Location = new System.Drawing.Point(475, 15);
this.buttonOk.Name = "buttonOk";
this.buttonOk.Size = new System.Drawing.Size(50, 50);
this.buttonOk.TabIndex = 11;
this.buttonOk.UseVisualStyleBackColor = false;
this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click);
And it will work, done it many times before
I got this code from my own working Form1.Designer.cs but because of that: please use the Visual Studio designer and don't try to write all this code / logic in your constructor or something.
The problem is because you are showing an image with the same size as your button.
When you want an image fit in your button, the width and height of image should be at least 1 point less than your button size. (or in other word, you can set your button width and height 1 point more than the image size).
So you can change your code to this:
var l_oStopPic = new Bitmap(l_oStopImage ,
new Size(btnStopOperation.Width-1, btnStopOperation.Height-1));

How do I resize a System.Windows.Forms.ToolBar?

I've not bothered with panels, docking, or anchors. I've simply thrown together a ToolBar control (not ToolStrip) and seem unable to size it.
System.Windows.Forms.ToolBar tb = new System.Windows.Forms.ToolBar();
// Reports 292x28 (approx) if I check width and height
// Basically the width of the form and I assume a default height
tb.Size = new System.Drawing.Size(195, 48);
// Reports 48x48, but does not actually create buttons of that size
// (It reports 48x48 because I'm retrieving 48x48 icons from a ResourceManager (resx))
tb.ButtonSize = new System.Drawing.Size(48, 48); //
The closest thing I found to making my ToolBar taller was:
http://bytes.com/topic/c-sharp/answers/241614-changing-height-toolbar-button
Although it's rather dated. And I didn't understand it. ToolBarButtons don't have Height, Width, or Size properties.
I'm using SharpDevelop, coding completely by hand on Vista, with all the .NET frameworks.
EDIT:
Here is the EXACT code that I am currently using.
#region ImageList/Toolbar
ImageList toolbarImages = new ImageList();
Image wizardToolbarImage = (Bitmap) rm.GetObject("wizard");
Image optionsToolbarImage = (Bitmap) rm.GetObject("configure");
toolbarImages.Images.Add(wizardToolbarImage);
toolbarImages.Images.Add(optionsToolbarImage);
ToolBar toolbarMain = new ToolBar();
toolbarMain.Size = new Size(195, 25); // no effect
ToolBarButton wizardToolbarButton = new ToolBarButton();
ToolBarButton optionsToolbarButton = new ToolBarButton();
wizardToolbarButton.ImageIndex = 0;
wizardToolbarButton.ToolTipText = "Wizard!";
optionsToolbarButton.ImageIndex = 1;
optionsToolbarButton.ToolTipText = "Options!";
toolbarMain.Buttons.Add(wizardToolbarButton);
toolbarMain.Buttons.Add(optionsToolbarButton);
toolbarMain.Appearance = ToolBarAppearance.Normal;
toolbarMain.ButtonSize = new System.Drawing.Size(48, 48); // no effect
toolbarMain.ImageList = toolbarImages;
toolbarMain.ButtonClick += new ToolBarButtonClickEventHandler(toolbarMain_Click);
Controls.Add(toolbarMain);
#endregion
In just about every winforms application I've written, regardless of language or framework, the toolbar could only be made taller by using larger icons.
You can also put the toolstrip inside a Panel and set the Dock property of the tool strip to Fill. And then you can size the Panel to whatever size you need.

Categories