I'm using SlimDX to render to a Windows Panel within a form using Visual C# Express. SlimDX derives a class from System.Windows.Forms.Form called SlimDX.Windows.RenderForm. I need to use this class in order to handle the windows message loop. I in turn must derive a class from SlimDX.Windows.RenderForm in order to add controls. My derived class looks like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Windows;
namespace SceneGenerator
{
public partial class ApplicationForm : RenderForm
{
public ApplicationForm() { }
public ApplicationForm( string text )
{
InitializeComponent();
this.Text = text;
}
public Panel GetViewport() { return this.ViewportPanel; }
}
}
Now though my background image does not show up in Visual C# environment( nor during runtime ). The background image is set in the usual InitializeComponent() function of the base class...Here are my designer and entry point codes:
namespace SceneGenerator
{
partial class ApplicationForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ApplicationForm));
this.ViewportPanel = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// ViewportPanel
//
this.ViewportPanel.Location = new System.Drawing.Point(410, 12);
this.ViewportPanel.Name = "ViewportPanel";
this.ViewportPanel.Size = new System.Drawing.Size(475, 345);
this.ViewportPanel.TabIndex = 0;
//
// ApplicationForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.ClientSize = new System.Drawing.Size(897, 788);
this.Controls.Add(this.ViewportPanel);
this.DoubleBuffered = true;
this.Name = "ApplicationForm";
this.Text = "ApplicationForm";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel ViewportPanel;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using SlimDX.Windows;
using SlimDX.Direct3D9;
using Device = SlimDX.Direct3D9.Device;
using Resource = SlimDX.Direct3D9.Resource;
namespace SceneGenerator
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
// form
var form = new ApplicationForm("Scene Generator");
Panel viewportPanel = form.GetViewport();
// device
var device = new Device(new Direct3D(), 0, DeviceType.Hardware, viewportPanel.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters()
{
BackBufferWidth = viewportPanel.Width,
BackBufferHeight = viewportPanel.Height
});
MessagePump.Run(form, () =>
{
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Green, 1.0f, 0);
device.BeginScene();
device.EndScene();
device.Present();
});
}
}
}
Why might this occur? Seems related to Visual C# Express and my lack of instructions to use the background image...
This is a problem caused by this RenderForm class. It overrides the OnPaintBackground() method and does nothing. So you don't see the BackgroundImage, you don't see BackColor either. You do get interesting LSD-style psychedelic effects in the designer. You can fix thix by writing your own:
protected override void OnPaintBackground(PaintEventArgs e) {
if (this.BackgroundImage != null) {
e.Graphics.DrawImage(this.BackgroundImage, this.DisplayRectangle);
}
}
But you'd better work from the assumption that this was done intentionally to solve some kind of drawing interference problem. Which you'll probably get back by using a Panel, you'd have to derive your own and in turn override its OnPaintBackground() method:
using System;
using System.Windows.Forms;
class RenderPanel : Panel {
protected override void OnPaintBackground(PaintEventArgs e) {
// Do nothing, like RenderForm
}
}
Might work, but this is pretty iffy stuff, not entirely uncommon for open source. This just doesn't look like it was made to do what you like to do. Best to avoid trouble and pass the form's Handle to the Device constructor.
Try referencing it as an external resource as in via a direct path or something. The way I'd try it is:
Environment.CurrentDirectory + #"\backgroundimage.png"
And if it still doesn't work like that one of two things might be happening.
Your background image is corrupt somehow
SlimDX might not accept BackgroundImages properly
Now I've never used SlimDX before so I'm not entirely sure on this but you could also try referencing it via the designer.
Related
i'm building a quick and dirty program to basically turn on a light in my room from a website as a code kata. during the programming i decided i would temporarily use a winform to test instead of hooking the physical light up (and having all sorts of possible problems there). but when i run my program the winform doesn't show, I've tried to run the executable but still nothing. when debugging i can see all the code works fine it's just that the winform doesn't show up. here is all the code:
form1.cs:
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;
namespace alarm_light_test
{
public partial class Form1 : Form
{
WebClient client = new WebClient();
public Form1()
{
InitializeComponent();
while (true)
{
if (CheckData())
{
TurnSirenOn();
client.DownloadString("**link to .php file to reset the .txt file**");
Thread.Sleep(5000);
TurnSirenOff();
}
else
{
Thread.Sleep(1000);
}
}
}
public bool CheckData()
{
bool retval = false;
Stream stream = client.OpenRead("**link to online .txt file**");
StreamReader reader = new StreamReader(stream);
String content = reader.ReadToEnd();
if(content == "1")
{
retval = true;
}
return retval;
}
public void TurnSirenOn()
{
pictureBox1.BackColor = Color.Green;
}
public void TurnSirenOff()
{
pictureBox1.BackColor = Color.Red;
}
}
}
program.cs:
using System;
using System.Windows.Forms;
namespace alarm_light_test
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Form1.Designer.cs
namespace alarm_light_test
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(13, 13);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(235, 235);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(260, 260);
this.Controls.Add(this.pictureBox1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
}
}
In your constructor public Form1(), you have a loop for-ever (a.k.a an infinite loop).
The while(true) will prevent the constructor from finishing the construction of the form object, and therefore will not be able to display anything.
public Form1()
{
InitializeComponent();
while (true) // This loop will never exit, and will run forever
{
...
}
}
Edit: Sample of how to run your code Asynchronously, therefore allowing the form to fully initialise and display as expected.
public Form1()
{
InitializeComponent();
DoWorkAsynchronously();
}
private async Task DoWorkAsynchronously()
{
await Task.Run(() =>
{
while (true)
{
if (CheckData())
{
TurnSirenOn();
client.DownloadString("**link to .php file to reset the .txt file**");
Thread.Sleep(5000);
TurnSirenOff();
}
else
{
Thread.Sleep(1000);
}
}
});
}
I have windows forms app. When it's closed the main window is disposed and then when user click on tray window is recreated - it works. However I have werid problem when I try to bring application back when using FileSystemWatcher. Idea is simple, when file is changed application is brought back. However in this case application appears but hangs and then dissapears. The shape of window comes back but is unresponsive, moving mouse on window shows like app is "thinking" or "hanging", the app doesn't have icon on taskbar.
My guess is that this is connected with threads/synchronization but I have no idea how to make it work again. I tried many different things connected with threading but failed. I cannot create this window again in UI thread because as I understand I can write _mainWindow.BeginInvoke but I can't do that before I create this form.
I have created the minimal working example that demonstrates the issue. It is available at https://gitlab.com/virtual92/getting-forms-up or here:
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GettingFormsUp
{
static class Program
{
private static bool hideFlag = true;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var initApplicationContext = new InitApplicationContext();
Console.WriteLine();
var fileSystemWatcher = new FileSystemWatcher(Path.GetFullPath("../.."), "watcher");
fileSystemWatcher.Changed += (sender, args) =>
{
Console.WriteLine("Watched");
initApplicationContext.Show();
};
fileSystemWatcher.EnableRaisingEvents = true;
Application.Run(initApplicationContext);
}
private class InitApplicationContext : ApplicationContext
{
private static MainWindow _mainWindow;
public InitApplicationContext()
{
NewForm();
}
private void NewForm()
{
Console.WriteLine("Creating new MainWindow");
_mainWindow = new MainWindow();
_mainWindow.Invoke((MethodInvoker) delegate
{
_mainWindow.Show();
});
}
public void Show()
{
if (_mainWindow == null || _mainWindow.IsDisposed)
{
NewForm();
}
else if (!_mainWindow.Visible)
{
_mainWindow.BeginInvoke((MethodInvoker) delegate
{
Console.WriteLine("showing");
_mainWindow.Show();
});
}
}
public void Delete()
{
if (_mainWindow != null && !_mainWindow.IsDisposed)
{
_mainWindow.Dispose();
}
}
}
}
}
MainWindow.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GettingFormsUp
{
public class MainWindow : Form
{
public MainWindow()
{
CreateHandle();
InitializeComponent();
}
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}
}
}
How can I make it work?
The problem with your code is that when you create the window again, it's being created in the thread used to raise the FileSystemWatcher.Changed event, which is a background thread, not the thread that the Application.Run() method is using to pump window messages. So that background thread winds up owning the window.
Messages for the window are sent to the thread that owns it, but that thread doesn't have a message pumping loop, so the window never sees the messages. Those messages are critical, as they are what handle everything about the interaction with the window, both user input and everything involved in drawing the window (except for the bare minimum, which the Windows desktop manager handles). Those messages are even used to handle things like calls to Control.Invoke() and Control.BeginInvoke(). Without the message-pumping loop, BeginInvoke() delegates will never be handled, and Invoke() will never even return.
There are a variety of ways to address this. Simply not disposing the window in the first place could be an option (you can override OnFormClosing(), canceling the event and hiding the window instead). Then the call to _mainWindow.Invoke() will always go to the correct thread and work like you expect:
public partial class MainWindow : Form
{
// ...
protected override void OnFormClosing(FormClosingEventArgs e)
{
Visible = false;
e.Cancel = true;
base.OnFormClosing(e);
}
// ...
}
Alternatively, you can capture the main thread's SynchronizationContext object, which can be used to do the same thing as Control.Invoke()/BeginInvoke(). The key to that technique is that until you've created a Winforms component in a thread, that thread won't have a SynchronizationContext. In your code, the form is created when you create the InitApplicationContext object, so capturing the SynchronizationContext after that will work:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var initApplicationContext = new InitApplicationContext();
SynchronizationContext context = SynchronizationContext.Current;
Console.WriteLine();
string path = Path.GetFullPath("../..");
Console.WriteLine($"Watching {path}");
var fileSystemWatcher = new FileSystemWatcher(path, "watcher");
fileSystemWatcher.Changed += (sender, args) =>
{
context.Post(o =>
{
Console.WriteLine("Watched");
initApplicationContext.Show();
}, null);
};
fileSystemWatcher.EnableRaisingEvents = true;
Application.Run(initApplicationContext);
}
If you take this approach, then of course you don't need the call to Control.Invoke() when creating the window. It's superfluous the first time anyway, and because you'll be using SynchronizationContext from the FileSystemWatcher.Changed event handler in the subsequent instances, it's also unneeded then:
private void NewForm()
{
Console.WriteLine("Creating new MainWindow");
_mainWindow = new MainWindow();
_mainWindow.Show();
}
I am recently working in a Windows Phone 8 app that update tiles in background. I followed this http://jeffblankenburgdotcom.wordpress.com/2011/11/25/31-days-of-mango-day-25-background-agents/ tutorial to make my work done. I followed every steps, but its putting the tile first but it does not updated the next tile after 60seconds in background. You can download the solution file here https://onedrive.live.com/?cid=843581c79c017ebc&id=843581C79C017EBC%21149&ithint=file,.rar&authkey=!ABbazYty8Rw7a_A
MainPage.xaml.cs is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using BackgroundAgentTileTest.Resources;
using Microsoft.Phone.Scheduler;
namespace BackgroundAgentTileTest
{
public partial class MainPage : PhoneApplicationPage
{
PeriodicTask periodicTask;
string periodicTaskName = "PeriodicAgent";
bool agentsAreEnabled = true;
// Constructor
public MainPage()
{
InitializeComponent();
agentsAreEnabled = true;
// Obtain a reference to the period task, if one exists
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
if (periodicTask != null)
{
RemoveAgent(periodicTaskName);
}
periodicTask = new PeriodicTask(periodicTaskName);
// The description is required for periodic agents. This is the string that the user
// will see in the background services Settings page on the device.
periodicTask.Description = "This demonstrates a periodic task.";
// Place the call to Add in a try block in case the user has disabled agents.
try
{
ScheduledActionService.Add(periodicTask);
// If debugging is enabled, use LaunchForTest to launch the agent in one minute.
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(10));
#endif
}
catch (InvalidOperationException exception)
{
if (exception.Message.Contains("BNS Error: The action is disabled"))
{
MessageBox.Show("Background agents for this application have been disabled by the user.");
agentsAreEnabled = false;
}
if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
{
// No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
}
}
catch (SchedulerServiceException)
{
// No user action required.
}
}
private void RemoveAgent(string periodicTaskName)
{
try
{
ScheduledActionService.Remove(periodicTaskName);
}
catch (Exception)
{
}
}
}
}
ScheduledAgent.cs is
#define DEBUG_AGENT
using System.Diagnostics;
using System.Windows;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using System;
using System.Linq;
namespace ScheduledTaskAgent1
{
public class ScheduledAgent : ScheduledTaskAgent
{
/// <remarks>
/// ScheduledAgent constructor, initializes the UnhandledException handler
/// </remarks>
static ScheduledAgent()
{
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += UnhandledException;
});
}
/// Code to execute on Unhandled Exceptions
private static void UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
Debugger.Break();
}
}
/// <summary>
/// Agent that runs a scheduled task
/// </summary>
/// <param name="task">
/// The invoked task
/// </param>
/// <remarks>
/// This method is called when a periodic or resource intensive task is invoked
/// </remarks>
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
//UpdateAppTile(GetLastUpdatedTimeMessage());
StandardTileData data = new StandardTileData
{
Title = "My tile!",
Count = 10, // I Need To Get This Counter From Isolated Storage Or My Other main Project
BackgroundImage = new Uri("/Background.png", UriKind.RelativeOrAbsolute),
BackTitle = "This is the back",
BackContent = DateTime.Now.ToString(),
BackBackgroundImage = new Uri("/Background.png", UriKind.RelativeOrAbsolute)
};
ShellTile.ActiveTiles.First().Update(data);
// If debugging is enabled, launch the agent again in one minute.
#if DEBUG_AGENT
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
#endif
NotifyComplete();
}
private string GetLastUpdatedTimeMessage()
{
return string.Format("Last Updated: {0}", DateTime.Now);
}
private void UpdateAppTile(string message)
{
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
BackContent = message
};
appTile.Update(tileData);
}
}
}
}
Note : I have put the reference.
It works after adding #define DEBUG_AGENT line on top of the MainPage.xaml.cs file.
I am getting three errors that are preventing my program from running and I cant figure out what they mean? I must be stupid because I even have my C# book next to me and it still doesn't make sense. Any help would be great. The errors are:
Projects\WindowsFormsApplication1\obj\x86\Release\WindowsFormsApplication1.exe' has more than one entry point defined: 'WindowsFormsApplication1.Program.Main()'. Compile with /main to specify the type that contains the entry point.
'WindowsFormsApplication1.Form1.Dispose(bool)': no suitable method found to override
Projects\WindowsFormsApplication1\obj\x86\Release\WindowsFormsApplication1.exe' has more than one entry point defined: 'Text.Main()'. Compile with /main to specify the type that contains the entry point.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
public class Text : Form
{
private Font arialBold24 = new Font("Arial", 24, FontStyle.Bold);
private Font timesItalic14 = new Font("Times New Roman", 14, FontStyle.Italic);
private Font courierPlain18 = new Font("Courier New", 18, FontStyle.Strikeout);
private Font genericSerifBI20 = new Font(FontFamily.GenericSerif, 20, FontStyle.Bold | FontStyle.Italic);
private Font verdanaPlain18 = new Font("Verdana", 18, FontStyle.Regular | FontStyle.Underline);
public Text()
{
Size = new Size(400, 200);
Text = "Text";
BackColor = Color.White;
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
int w = (int)g.MeasureString(arialBold24.Name, arialBold24).Width;
int arialStart = (Width - w) / 2;
int otherStart = Width / 4;
int h = DisplayRectangle.Height;
g.DrawString(arialBold24.Name, arialBold24, Brushes.Blue, arialStart, 0);
g.DrawString(timesItalic14.Name, timesItalic14, Brushes.Blue, otherStart, h / 5);
g.DrawString(courierPlain18.Name, courierPlain18, Brushes.Blue, otherStart, 2 * h / 5);
g.DrawString(genericSerifBI20.Name, genericSerifBI20, Brushes.Blue, otherStart, 3 * h / 5);
g.DrawString(verdanaPlain18.Name, verdanaPlain18, Brushes.Blue, otherStart, 4 * h / 5);
base.OnPaint(e);
}
public static void Main()
{
Application.Run(new Text());
}
}
The program.cs code is:
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Updated Code of Form1:
namespace WindowsFormsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
}
}
Program.CS code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Text());
}
}
}
First of all, you don't need another Main method in class Text, as it is already defined in the Program.cs file if you use the built-in template to create a WinForms app.
I hope removing the redundant Main method from class Text may be enough to run the app successfully.
EDIT
In the Program.cs file, instantiate the Text class instead of Form1 like below:
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Text());
}
}
}
Also you need to enclose the class Text with the following namespace:
namespace WindowsFormsApplication1
{
public class Text : Form
{
// ...
}
}
EDIT 2:
Please override the Dispose() method inside class Form1 like below:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
I am writing Windows Phone application and can't understand why the emulator crashes. It says that: "Unhandled exception at 0x773415de in XDE.exe: 0xC0000005: Access violation reading location 0x05190000." This problem appears only when I begin to scroll very fast my listbox. I load per 20 items to listbox (the number of all items is 200). The problem is in my class of downloading images. I create a Webclient request for each image to server or it's better to create a thread pool?
In XAML:
<Image Name="userPic" Margin="0,8,1,2"
DelayFix:LowProfileImageLoader.UriSource="{Binding photoUrl}" Stretch="Fill"
Width="60"
Height="60"/>
downloadImage class:
using System;
using System.Net;
using System.Windows.Resources;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone;
using System.Diagnostics;
namespace PhoneApp
{
public class downloadImage
{
public int ImageIndex;
public string ImageURL;
public Boolean Downloaded;
public BitmapImage Bitmap;
public Image destinationImage;
private WebClient client;
public delegate void FileCompleteHandler(object sender);
public event FileCompleteHandler FileCompleteEvent;
private static readonly object _syncBlock = new object();
public downloadImage(int index, string imageURL, Image destinationImage = null)
{
this.ImageIndex = index;
this.ImageURL = imageURL;
this.destinationImage = destinationImage;
}
public void Download()
{
client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
client.OpenReadAsync(new Uri(this.ImageURL, UriKind.Absolute));
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null && e.Result != null)
{
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
if (destinationImage == null)
{
this.Bitmap = new BitmapImage();
this.Bitmap.SetSource(sri.Stream);
}
else
{
this.destinationImage.Source = PictureDecoder.DecodeJpeg(e.Result);
}
this.Downloaded = true;
if (FileCompleteEvent != null)
FileCompleteEvent(this);
}));
}
}
}
}
The code of my class:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.Phone;
using Microsoft.Phone.Info;
namespace DelayFix
{
/// <summary>
/// Provides access to the Image.UriSource attached property which allows
/// Images to be loaded by Windows Phone with less impact to the UI thread.
/// </summary>
public static class LowProfileImageLoader
{
private static readonly object _syncBlock = new object();
private static bool _exiting;
/// <summary>
/// Gets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <returns>Uri to use for providing the contents of the Source property.</returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static Uri GetUriSource(Image obj)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
return (Uri)obj.GetValue(UriSourceProperty);
}
/// <summary>
/// Sets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <param name="value">Uri to use for providing the contents of the Source property.</param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static void SetUriSource(Image obj, Uri value)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
obj.SetValue(UriSourceProperty, value);
}
/// <summary>
/// Identifies the UriSource attached DependencyProperty.
/// </summary>
public static readonly DependencyProperty UriSourceProperty = DependencyProperty.RegisterAttached(
"UriSource", typeof(Uri), typeof(LowProfileImageLoader), new PropertyMetadata(OnUriSourceChanged));
/// <summary>
/// Gets or sets a value indicating whether low-profile image loading is enabled.
/// </summary>
public static bool IsEnabled { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Static constructor performs additional tasks.")]
static LowProfileImageLoader()
{
Debug.WriteLine("Limit Memory:" + DeviceStatus.ApplicationMemoryUsageLimit / 1024 / 1024);
}
private static void HandleApplicationExit(object sender, EventArgs e)
{
Debug.WriteLine("Exit!");
}
private static void OnUriSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
lock (_syncBlock)
{
var image = (Image)o;
var uri = (Uri)e.NewValue;
Debug.WriteLine("Memory:" + DeviceStatus.ApplicationCurrentMemoryUsage / 1024 / 1024);
if (DesignerProperties.IsInDesignTool || !uri.IsAbsoluteUri)
{
image.Source = PhoneAppVKContest.ImageUtils.getJpegImage(uri.OriginalString);
}
else
{
PhoneAppVKContest.downloadImage di = new PhoneAppVKContest.downloadImage(0, uri.AbsoluteUri);
di.destinationImage = image;
di.FileCompleteEvent += new PhoneAppVKContest.downloadImage.FileCompleteHandler(di_FileCompleteEvent);
di.Download();
}
}
}
static void di_FileCompleteEvent(object sender)
{
Debug.WriteLine("Download success!");
}
}
}
When you are creating webClient request for each image, then a conflict may arise when the webClient is bombarded with all the requests. You need to order the requests sequentially via a queue. If the image in the queue's first element is already in the cache or IS, link it, else check if webclient is busy or not. If busy then send it to the end of the queue. And check for the next image. If its not busy, submit a downloading request. This is the method which I have followed. You might also consider binding the Imagesource for each image inorder to prevent auto reloading on page reload or due to problem in scroll cache. I hope this answer helps you.