Why does Image not appear when rendering it before? - silverlight

Starting with a newly created project I add a button to my main page and do the following in the click handler:
I create an Image and assign a BitmapImage as its Source. Then I add the Image to my LayoutRoot. My expectation is that I see the image in the GUI after I click the button.
Now there is the twist: I also want to render this Image to a WriteableBitmap. Therefore I create such a bitmap and call its Render method to render the image.
Here is the problem: when I comment out the Render call then I immediately see the Image appear on my main page. When I include the Render call, then the Image appears not on the first button click, but on the second. Why?
Here is the code:
private void button1_Click(object sender, RoutedEventArgs e)
{
WriteableBitmap wbmp = new WriteableBitmap(62, 62);
BitmapImage bmp = new BitmapImage(new Uri("ApplicationIcon.png", UriKind.Relative));
Image img = new Image() { Width = 62, Height = 62, Source = bmp };
wbmp.Render(img, null); // <------ this line makes the difference
LayoutRoot.Children.Add(img);
}

There are some remarks of using Render (may be one of these makes your issue):
After calling this method, you must call Invalidate in order to render
the bitmap.
This method does support UIElement objects that are not part of the
visual tree. You are required to call Measure and Arrange for
UIElement objects that are not in the visual tree, before calling
Render.

try this..
private Image _img = new Image ();
public Image img
{
get
{
return _img;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String PropertyName)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
WriteableBitmap wbmp = new WriteableBitmap(62, 62);
BitmapImage bmp = new BitmapImage(new Uri("ApplicationIcon.png", UriKind.Relative));
Image img = new Image() { Width = 62, Height = 62, Source = bmp };
wbmp.Render(img, null); // <------ this line makes the difference
LayoutRoot.Children.Add(img);
NotifyPropertyChanged("Image");
}
this should work.. try once...

Related

WPF - Why would Loading image from relative path during runtime fail *unless* I examine the object in debugger?

Made a new test project just for this, with a button and an image. MyImage.png is placed in the project's debug working directory, and NOT included in the project.
This works fine, with an absolute path specified:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
Uri fileUri = new Uri("C:/blah/blah/blah/MyImage.png");
BitmapImage myimage = new BitmapImage(fileUri);
Imagebox.Source = myimage;
}
This does NOT show any image, with a relative path:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
Uri fileUri = new Uri("MyImage.png", UriKind.Relative);
BitmapImage myimage = new BitmapImage(fileUri);
Imagebox.Source = myimage;
}
However, I noticed that the relative path DOES work if I set a breakpoint at the last line, examine myimage's properties (it holds data indicating MyImage.png was successfully found), then continue execution. At which point the image shows up. I'm using Visual Studio 2019 Community.
I'm very confused why this is happening.
That looks like a bug. A workaround would be to set BitmapCacheOption.OnLoad:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
var fileUri = new Uri("MyImage.png", UriKind.Relative);
var myImage = new BitmapImage();
myImage.BeginInit();
myImage.UriSource = fileUri;
myImage.CacheOption = BitmapCacheOption.OnLoad;
myImage.EndInit();
Imagebox.Source = myImage;
}
Or always use an absolute URI:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
var fileUri = new Uri(Path.Combine(Environment.CurrentDirectory, "MyImage.jpg"));
var myImage = new BitmapImage(fileUri);
Imagebox.Source = myImage;
}

Loading Images in background while not locking the files

My application loads a lot of images in a BackgroundWorker to stay usable. My Image control is bound to a property named "ImageSource". If this is null it's loaded in the background and raised again.
public ImageSource ImageSource
{
get
{
if (imageSource != null)
{
return imageSource;
}
if (!backgroundImageLoadWorker.IsBusy)
{
backgroundImageLoadWorker.DoWork += new DoWorkEventHandler(bw_DoWork);
backgroundImageLoadWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
backgroundImageLoadWorker.RunWorkerAsync();
}
return imageSource;
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
bitmap = new BitmapImage();
bitmap.BeginInit();
try
{
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.DecodePixelWidth = 300;
MemoryStream memoryStream = new MemoryStream();
byte[] fileContent = File.ReadAllBytes(imagePath);
memoryStream.Write(fileContent, 0, fileContent.Length);
memoryStream.Position = 0;
bitmap.StreamSource = memoryStream;
}
finally
{
bitmap.EndInit();
}
bitmap.Freeze();
e.Result = bitmap;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BitmapSource bitmap = e.Result as BitmapSource;
if (bitmap != null)
{
Dispatcher.CurrentDispatcher.BeginInvoke(
(ThreadStart)delegate()
{
imageSource = bitmap;
RaisePropertyChanged("ImageSource");
}, DispatcherPriority.Normal);
}
}
This is all well so far but my users can change the images in question. They choose a new image in an OpenDialog, the old image file is overwritten with the new and ImageSource is raised again which loads the new image with the same filename again:
public string ImagePath
{
get { return imagePath; }
set
{
imagePath= value;
imageSource = null;
RaisePropertyChanged("ImageSource");
}
}
On some systems the overwriting of the old file results in an exception:
"a generic error occured in GDI+" and "The process cannot access the file..."
I tried a lot of things like loading with BitmapCreateOptions.IgnoreImageCache and BitmapCacheOption.OnLoad. This raised Exceptions when loading them:
Key cannot be null.
Parameter name: key
If I try this without the BackgroundWorker on the UI thread it works fine. Am I doing something wrong? Isn't it possible to load the images in the background while keeping the files unlocked?
Well, it seems all of the above works. I simplified the example for the question and somehow on the way lost the problem.
The only difference I could see in my code is that the loading of the image itself was delegated to a specific image loader class which somehow created the problem. When I removed this dependency the errors disappeared.

How do I load a BitmapImage for processing in Silverlight?

The following code will only show a size for my bitmap (needed for processing) if I uncomment the commented lines. This doesn't seem the right to go about things but it's all I've come up with so far that works. I don't want to display my bitmap as an image in a UI element, I just want to process it.
BitmapImage bmpi;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
bmpi = new BitmapImage(new Uri("multicolor.png", UriKind.Relative));
//Image img = new Image();
//img.Source = bmpi;
//LayoutRoot.Children.Add(img);
//LayoutRoot.Children.Clear();
MessageBox.Show(bmpi.PixelWidth.ToString());
}
To load the image upfront, you need to set CreateOptions to None from its default value, DelayCreation.
Then you can get the width in the ImageOpened event.
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
bmpi = new BitmapImage();
bmpi.CreateOptions = BitmapCreateOptions.None;
bmpi.ImageOpened += new EventHandler<RoutedEventArgs>(bmpi_ImageOpened);
bmpi.UriSource = new Uri("multicolor.png", UriKind.RelativeOrAbsolute);
}
void bmpi_ImageOpened(object sender, RoutedEventArgs e)
{
MessageBox.Show(bmpi.PixelWidth.ToString());
}

After playing a MediaElement, how can I play it again?

I have a variable MediaElement variable named TestAudio in my Silverlight app.
When I click the button, it plays the audio correctly.
But when I click the button again, it does not play the audio.
How can I make the MediaElement play a second time?
None of the tries below to put position back to 0 worked:
private void Button_Click_PlayTest(object sender, RoutedEventArgs e)
{
//TestAudio.Position = new TimeSpan(0, 0, 0);
//TestAudio.Position = TestAudio.Position.Add(new TimeSpan(0, 0, 0));
//TestAudio.Position = new TimeSpan(0, 0, 0, 0, 0);
//TestAudio.Position = TimeSpan.Zero;
TestAudio.Play();
}
I found it, you just have to stop the audio first, then set the position:
TestAudio.Stop();
TestAudio.Position = TimeSpan.Zero;
The MediaElement has no built-in support for looping playback. You can use the MediaEnded event and simply set the media position to zero or you can call the Stop() method. Either one will put you at the beginning of your video/audio for playback.
public partial class MainPage : UserControl
{
public MainPage()
{
// Required to initialize variables
InitializeComponent();
// Used for loopback.
MyME.MediaEnded += new RoutedEventHandler(MyME_MediaEnded);
}
// MediaElement has no looping capabilities so need to set the position back
// to the begining after the video finishes in order to play again.
// Or you can use the stop method
void MyME_MediaEnded(object sender, RoutedEventArgs e)
{
//MyME.Position = TimeSpan.Zero;
MyME.Stop();
}
private void BtnPlay_Click(object sender, RoutedEventArgs e)
{
MyME.Play();
}
private void BtnPause_Click(object sender, RoutedEventArgs e)
{
MyME.Pause();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
MyME.Stop();
}
}
I found the above did not work for me, and the only way I could get it working was to create a mediaelement dynamically. Heres the code I used - I copied the values from a MediaElement named mePlayClick I initially put in the XAML but you may not need to do this.
private void Play_MediaSound()
{
// Create new media element dynamically
MediaElement mediaElement = new MediaElement();
// Reuse settings in XAML
mediaElement.Volume = mePlayClick.Volume;
mediaElement.Source = mePlayClick.Source;
mediaElement.AutoPlay = mePlayClick.AutoPlay;
// WHen the media ends, remove the media element
mediaElement.MediaEnded += (sender, args) =>
{
LayoutRoot.Children.Remove(mediaElement);
mediaElement = null;
};
// Add the media element, must be in visual ui tree
LayoutRoot.Children.Add(mediaElement);
// When opened, play
mediaElement.MediaOpened += (sender, args) =>
{
mediaElement.Play();
};
}

WPF, Image MouseDown Event

I have an control with a mouse down event where Id like to chnage the Image when the image is clicked. But I cant seem to alter ANY of the images properties in the event.
Event
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
BitmapImage bitImg = new BitmapImage();
bitImg.BeginInit();
bitImg.UriSource = new Uri("./Resource/Images/Bar1.png", UriKind.Relative);
bitImg.EndInit();
((Image)sender).Source = null;
((Image)sender).Width = 100;
((Image)sender).Visibility = Visibility.Hidden;
}
The event does fire, and even the .Visibility property does not alter the image and make it hidden.
What am I doing wrong?
Assuming the file is in your application, you need to use the Pack URI scheme:
var img = sender as Image;
BitmapImage bmp = new BitmapImage(new Uri("pack://application:,,,/Resources/Images/Bar1.png"));
img.Source = bmp;
In the above example, this would indicate a subfolder in your project of Resources/Images.

Resources