I need to download a webcam image thru http, and refresh it at 10fps and display it on a WPF window. Right now I'm using this code:
Window1 wndMain;
BitmapImage img;
DispatcherTimer tmrRefresh;
public WCam(Window1 wndMain, string imguri)
{
this.wndMain = wndMain;
this.MouseLeftButtonDown += delegate { DragMove(); };
url = imguri;
InitializeComponent();
tmrRefresh = new DispatcherTimer(TimeSpan.FromMilliseconds(100),
DispatcherPriority.Normal, Refresh, Dispatcher.CurrentDispatcher);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (url != "")
{
try
{
img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
img.UriSource=(new Uri(url));
img.EndInit();
}
catch (Exception ex)
{
new WPopup().Show(ex.Message);
}
ImgBox.Source = img;
tmrRefresh.Start();
}
}
public void Refresh(object sender, EventArgs e)
{
try
{
img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
img.UriSource = (new Uri(url));
img.EndInit();
}
catch (Exception ex)
{
new WPopup().Show(ex.Message);
}
ImgBox.Source = null;
ImgBox.Source = img;
}
It displays nothing, if I increase the timer interval to 1000 it displays images but the image goes away while it loads the next one. Also the window loads in awfully slowly.
Seperate the loading logic from the image.
Keep the loaded code as is, the refresh should download the image with a simple async(to keep ui running) request and only when finished change the image source to the local memory.
Related
I'm trying to have a rotating ad display in my WPF application. When I load the application the GetNewAd() method properly displays the advertisement. When I try to update the ad by calling my GetNewAd() method again, the chatHost is returning the new ad, but the image does not update in the UI. I have tried updating the image without the animation, but I still have the same problem. What am I missing here?
public class ncWindow
{
public ncWindow(Grid grid)
{
imgAd = new Image();
imgAd.Margin = new Thickness(2,2,2,2);
imgAd.HorizontalAlignment = HorizontalAlignment.Left;
imgAd.MouseDown += imgAd_MouseDown;
adTimer.Interval = 60000;
adTimer.Elapsed += adTimer_Elapsed;
adTimer.AutoReset = true;
adTimer.Start();
grid.Children.Add(imgAd);
}
public void GetNewAd()
{
DisplayedAd = chatHost.GetNewAd();
debug.Print("GetNewAd: " + DisplayedAd.VendorName + ", ImageData.Length = " + DisplayedAd.ImageData.Length);
BitmapImage image = new BitmapImage();
if (DisplayedAd.ImageData!=null && DisplayedAd.ImageData.Length>0)
{
using (var mem = new MemoryStream(DisplayedAd.ImageData))
{
mem.Position = 0;
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = mem;
image.EndInit();
}
}
else
return;
image.Freeze();
imgAd.Source = image;
}
private void adTimer_Elapsed(object source, ElapsedEventArgs e)
{
GetNewAd();
}
}
If the timer that calls your GetNewAd() method is not a DispatcherTimer, you'll explicitly have to invoke the assignment of the Image control's Source property in the UI thread:
image.Freeze(); // necessary for cross-thread access
imgAd.Dispatcher.BeginInvoke(new Action(() => imgAd.Source = image));
I want to use a webcam in c# code with Aforge.NET, but It didin't work because I don't use forms.
See my code below :
public partial class CapturePage : Page
{
private bool DeviceExist = false;
private FilterInfoCollection videoDevices;
private VideoCaptureDevice videoSource = null;
public CapturePage()
{
InitializeComponent();
getCamList();
startVideo();
}
// get the devices name
private void getCamList()
{
try
{
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count == 0)
throw new ApplicationException();
DeviceExist = true;
}
catch (ApplicationException)
{
DeviceExist = false;
}
}
//toggle start and stop button
private void startVideo() // as it's say
{
if (DeviceExist)
{
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString); // the only one webcam
videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
CloseVideoSource();
// videoSource.DesiredFrameSize = new Size(160, 120); // deprecated ?
//videoSource.DesiredFrameRate = 10;
videoSource.Start();
}
else
{
// error
}
}
else
{
if (videoSource.IsRunning)
{
timer1.Enabled = false;
CloseVideoSource();
}
}
}
//eventhandler if new frame is ready
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
//do processing here
pictureBox1.Image = img; // But I can't have pictureBox in xaml ??
}
//close the device safely
private void CloseVideoSource()
{
if (!(videoSource == null))
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource = null;
}
}
}
}
I tried this code in a form application, and it's work, but with a page, PictureBox are not referenced. Even if I reference it and create one in the code, this didn't work.
Can I, and how, use a PictureBox in a XAML Page ? Or there is an other solution tu use Aforge.net Webcam in Xaml Page ?
I succeded with changing the NewFrame methode like this:
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
System.Drawing.Image imgforms = (System.Drawing.Bitmap)eventArgs.Frame.Clone();
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
imgforms.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
//Using the freeze function to avoid cross thread operations
bi.Freeze();
//Calling the UI thread using the Dispatcher to update the 'Image' WPF control
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
ImageWebcam.Source = bi; /*frameholder is the name of the 'Image' WPF control*/
}));
}
cheers !
You could use WindowsFormsHost to integrate this Page in your WPF application.
If you need a WPF bitmap you may "convert" it like desribed here: Load a WPF BitmapImage from a System.Drawing.Bitmap
i am trying to download an image from the web save it in the media library , below is my code, am i missing something here, Thanks in advance
public void storePicture()
{
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
string url = #"http://mynokiablog.com/wp-content/uploads/2012/11/wp8.jpeg";
BitmapImage storeimage = new BitmapImage(new Uri(url));
// height and width are 0
int testheight = storeimage.PixelHeight;
int testwidth = storeimage.PixelWidth;
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile("testname");
// NullRefrenceException
WriteableBitmap wb = new WriteableBitmap(storeimage);
wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
add event handler as below
storeimage.ImageOpened += bitmapImage_ImageOpened;
storeimage.ImageFailed += bitmapImage_ImageFailed;
storeimage.DownloadProgress += bitmapImage_DownloadProgress;
then in bitmapImage_DownloadProgress, create WritableBitMap and save
private void LoadIMG()
{
var bitmapImage = new BitmapImage { CreateOptions = BitmapCreateOptions.None };
bitmapImage.ImageOpened += bitmapImage_ImageOpened;
bitmapImage.ImageFailed += bitmapImage_ImageFailed;
bitmapImage.DownloadProgress += bitmapImage_DownloadProgress;
bitmapImage.UriSource = new Uri("http://ds.serving-sys.com/BurstingRes///Site-16990/Type-0/7b912e70-352a-454f-8ea7-5d5ecd6ebfae.gif");
}
private void bitmapImage_DownloadProgress(object sender, DownloadProgressEventArgs e)
{
}
private void bitmapImage_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
}
private void bitmapImage_ImageOpened(object sender, RoutedEventArgs e)
{
var userStoreForApplication = IsolatedStorageFile.GetUserStoreForApplication();
var writeableBitmap = new WriteableBitmap(sender as BitmapImage);
var isolatedStorageFileStream = userStoreForApplication.CreateFile("Myfile.gif");
writeableBitmap.SaveJpeg(isolatedStorageFileStream, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight, 0, 85);
}
You can try it: http://www.kunal-chowdhury.com/2012/02/how-to-download-and-save-images-in-wp7.html#.TzUtWBSaLkM.twitter
Hope this helps.
In my windows application I use Windows Media Player dlls to play a video.
In my form I have a button to take a picture of the current video frame.
I did a lot of tests and code examinations but I couldn't find out why taking a picture of the current frame fails.
I tried this code, but the resulting image was black:
private Graphics g = null;
private void btnTakePicture_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(axWMVMovie.URL))
{
axWMVMovie.Ctlcontrols.pause();
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
System.Drawing.Image ret = null;
try
{
Bitmap bitmap = new Bitmap(axWMVMovie.Width, axWMVMovie.Height);
{
g = Graphics.FromImage(bitmap);
{
Graphics gg = axWMVMovie.CreateGraphics();
{
timerTakePicFromVideo.Start();
}
}
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ret = System.Drawing.Image.FromStream(ms);
ret.Save(saveFileDialog1.FileName);
}
}
}
catch
{
}
}
}
}
private void timerTakePicFromVideo_Tick(object sender, EventArgs e)
{
timerTakePicFromVideo.Stop();
g.CopyFromScreen(axWMVMovie.PointToScreen(new System.Drawing.Point()).X,
axWMVMovie.PointToScreen(new System.Drawing.Point()).Y, 0, 0,
new System.Drawing.Size(axWMVMovie.Width, axWMVMovie.Height));
}
I use Timer because when the user selects the save path, function takes image from the file user specified in save file dialog. Video format is WMV.
I took your code and modified it. I put the code to capture the photo a little bit up and now it works. I create the picture right before the saveFileDialog pops up, so you will really get only the picture and not the saveFileDialog within your pic.
if (!string.IsNullOrEmpty(axWindowsMediaPlayer1.URL))
{
axWindowsMediaPlayer1.Ctlcontrols.pause();
System.Drawing.Image ret = null;
try
{
// take picture BEFORE saveFileDialog pops up!!
Bitmap bitmap = new Bitmap(axWindowsMediaPlayer1.Width, axWindowsMediaPlayer1.Height);
{
Graphics g = Graphics.FromImage(bitmap);
{
Graphics gg = axWindowsMediaPlayer1.CreateGraphics();
{
//timerTakePicFromVideo.Start();
this.BringToFront();
g.CopyFromScreen(
axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).X,
axWindowsMediaPlayer1.PointToScreen(
new System.Drawing.Point()).Y,
0, 0,
new System.Drawing.Size(
axWindowsMediaPlayer1.Width,
axWindowsMediaPlayer1.Height)
);
}
}
// afterwards save bitmap file if user wants to
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ret = System.Drawing.Image.FromStream(ms);
ret.Save(saveFileDialog1.FileName);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
With previous answer a trick to avoid getting the controls on the capture is to do this before you capture :
string uimode_previus = axWindowsMediaPlayer2.uiMode;
axWindowsMediaPlayer2.uiMode = "none";
then when capture is done set the uimode back to previous like this :
axWindowsMediaPlayer2.uiMode = uimode_previus ;
In that way you only get the actual shoot from the current frame.
it is a little workaround but it does the job.
Here is working example
private void button8_Click_1(object sender, EventArgs e)
{
string uimode_previus = axWindowsMediaPlayer2.uiMode;
axWindowsMediaPlayer2.uiMode = "none";
if (!string.IsNullOrEmpty(axWindowsMediaPlayer2.URL))
{
ret = null;
try
{
// take picture BEFORE saveFileDialog pops up!!
Bitmap bitmap = new Bitmap(axWindowsMediaPlayer2.Width, axWindowsMediaPlayer2.Height);
{
Graphics g = Graphics.FromImage(bitmap);
{
Graphics gg = axWindowsMediaPlayer2.CreateGraphics();
{
//timerTakePicFromVideo.Start();
this.BringToFront();
g.CopyFromScreen(axWindowsMediaPlayer2.PointToScreen(
new System.Drawing.Point()).X,
axWindowsMediaPlayer2.PointToScreen(
new System.Drawing.Point()).Y,
0, 0,
new System.Drawing.Size(
axWindowsMediaPlayer2.Width - 0,
axWindowsMediaPlayer2.Height - 0)
);
}
}
// afterwards save bitmap file if user wants to
try
{
using (MemoryStream ms = new MemoryStream())
{
string rute = axWindowsMediaPlayer2.URL.ToString().Replace(".", "Review_."); //
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ret = new Bitmap(System.Drawing.Image.FromStream(ms));
ret.Save(rute.Replace(".mp4", ".Png"));
}
// open captured frame in new form
TeamEasy.ShowPictureForm spf = new ShowPictureForm();
spf.ImagePictureBox.Image = ret;
spf.ShowDialog();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
axWindowsMediaPlayer2.uiMode = uimode_previus;
// restore the UImode of player
}
What's the best way to asynchronously load an BitmapImage in C# using WPF?
I was just looking into this and had to throw in my two cents, though a few years after the original post (just in case any one else comes looking for this same thing I was looking into).
I have an Image control that needs to have it's image loaded in the background using a Stream, and then displayed.
The problem that I kept running into is that the BitmapSource, it's Stream source and the Image control all had to be on the same thread.
In this case, using a Binding and setting it's IsAsynch = true will throw a cross thread exception.
A BackgroundWorker is great for WinForms, and you can use this in WPF, but I prefer to avoid using the WinForm assemblies in WPF (bloating of a project is not recommended, and it's a good rule of thumb too). This should throw an invalid cross reference exception in this case too, but I didn't test it.
Turns out that one line of code will make any of these work:
//Create the image control
Image img = new Image {HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, VerticalAlignment = System.Windows.VerticalAlignment.Stretch};
//Create a seperate thread to load the image
ThreadStart thread = delegate
{
//Load the image in a seperate thread
BitmapImage bmpImage = new BitmapImage();
MemoryStream ms = new MemoryStream();
//A custom class that reads the bytes of off the HD and shoves them into the MemoryStream. You could just replace the MemoryStream with something like this: FileStream fs = File.Open(#"C:\ImageFileName.jpg", FileMode.Open);
MediaCoder.MediaDecoder.DecodeMediaWithStream(ImageItem, true, ms);
bmpImage.BeginInit();
bmpImage.StreamSource = ms;
bmpImage.EndInit();
//**THIS LINE locks the BitmapImage so that it can be transported across threads!!
bmpImage.Freeze();
//Call the UI thread using the Dispatcher to update the Image control
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
img.Source = bmpImage;
img.Unloaded += delegate
{
ms.Close();
ms.Dispose();
};
grdImageContainer.Children.Add(img);
}));
};
//Start previously mentioned thread...
new Thread(thread).Start();
Assuming you're using data binding, setting Binding.IsAsync property to True seems to be a standard way to achieve this.
If you're loading the bitmap in the code-behind file using background thread + Dispatcher object is a common way to update UI asynchronous
This will allow you to create the BitmapImage on the UI thread by using the HttpClient to do the async downloading:
private async Task<BitmapImage> LoadImage(string url)
{
HttpClient client = new HttpClient();
try
{
BitmapImage img = new BitmapImage();
img.CacheOption = BitmapCacheOption.OnLoad;
img.BeginInit();
img.StreamSource = await client.GetStreamAsync(url);
img.EndInit();
return img;
}
catch (HttpRequestException)
{
// the download failed, log error
return null;
}
}
To elaborate onto aku's answer, here is a small example as to where to set the IsAsync:
ItemsSource="{Binding IsAsync=True,Source={StaticResource ACollection},Path=AnObjectInCollection}"
That's what you would do in XAML.
BitmapCacheOption.OnLoad
var bmp = await System.Threading.Tasks.Task.Run(() =>
{
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.UriSource = new Uri(path);
img.EndInit();
ImageBrush brush = new ImageBrush(img);
}
Use or extend System.ComponentModel.BackgroundWorker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Personally, I find this to be the easiest way to perform asynchronous operations in client apps. (I've used this in WinForms, but not WPF. I'm assuming this will work in WPF as well.)
I usually extend Backgroundworker, but you dont' have to.
public class ResizeFolderBackgroundWorker : BackgroundWorker
{
public ResizeFolderBackgroundWorker(string sourceFolder, int resizeTo)
{
this.sourceFolder = sourceFolder;
this.destinationFolder = destinationFolder;
this.resizeTo = resizeTo;
this.WorkerReportsProgress = true;
this.DoWork += new DoWorkEventHandler(ResizeFolderBackgroundWorker_DoWork);
}
void ResizeFolderBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DirectoryInfo dirInfo = new DirectoryInfo(sourceFolder);
FileInfo[] files = dirInfo.GetFiles("*.jpg");
foreach (FileInfo fileInfo in files)
{
/* iterate over each file and resizing it */
}
}
}
This is how you would use it in your form:
//handle a button click to start lengthy operation
private void resizeImageButtonClick(object sender, EventArgs e)
{
string sourceFolder = getSourceFolderSomehow();
resizer = new ResizeFolderBackgroundWorker(sourceFolder,290);
resizer.ProgressChanged += new progressChangedEventHandler(genericProgressChanged);
resizer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(genericRunWorkerCompleted);
progressBar1.Value = 0;
progressBar1.Visible = true;
resizer.RunWorkerAsync();
}
void genericRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
//signal to user that operation has completed
}
void genericProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
//I just update a progress bar
}