I've looked all around this site and have yet to find a way to blur an image in XAML. I've tried some functions in C#, but they haven't worked. I'd like to achieve the blurred background image, like in the Audiocloud app (https://store-images.s-microsoft.com/image/apps.28769.9007199266467874.da79334d-0f1c-4851-8e0b-7ee566918b20.0658cd96-487e-4e1f-a330-10fb030aaa22?w=443&h=788&q=60). How would I go about accomplishing this?
Add Microsofts Win2D.uwp NuGet package to your project. (Right click your project in Solution Explorer > "Manage NuGet packages...")
Have this example XAML UI:
<Grid>
<Image x:Name="myBackground" Stretch="UniformToFill" />
<Image x:Name="myImage" Width="360" Height="300" Source="Assets/test.png" Stretch="UniformToFill" ImageOpened="myImage_ImageOpened"/>
</Grid>
Create an Extension Method for UIElement in a static class:
static class Extension
{
public static async Task<IRandomAccessStream>
RenderToRandomAccessStream(this UIElement element)
{
RenderTargetBitmap rtb = new RenderTargetBitmap();
await rtb.RenderAsync(element);
var pixelBuffer = await rtb.GetPixelsAsync();
var pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)rtb.PixelWidth,
(uint)rtb.PixelHeight,
displayInformation.RawDpiX,
displayInformation.RawDpiY,
pixels);
await encoder.FlushAsync();
stream.Seek(0);
return stream;
}
}
Use it. For example in your public sealed partial class MainPage : Page:
private async void BlurThisUI(UIElement sourceElement, Image outputImage)
{
using (var stream = await sourceElement.RenderToRandomAccessStream())
{
var device = new CanvasDevice();
var bitmap = await CanvasBitmap.LoadAsync(device, stream);
var renderer = new CanvasRenderTarget(device,
bitmap.SizeInPixels.Width,
bitmap.SizeInPixels.Height,
bitmap.Dpi);
using (var ds = renderer.CreateDrawingSession())
{
var blur = new GaussianBlurEffect();
blur.BlurAmount = 5.0f;
blur.Source = bitmap;
ds.DrawImage(blur);
}
stream.Seek(0);
await renderer.SaveAsync(stream, CanvasBitmapFileFormat.Png);
BitmapImage image = new BitmapImage();
image.SetSource(stream);
outputImage.Source = image;
}
}
Call it. In this example when the image was opened (see the XAML above: ImageOpened="myImage_ImageOpened"):
private void myImage_ImageOpened(object sender, RoutedEventArgs e)
{
this.BlurThisUI(myImage, myBackground);
}
Credits goes to Nikola Metulev by this tutorial
Note
As you can see, you can not just use this to blur images but also any UIElement (for example a grid with all its containing UI elements like buttons, checkboxes etc.)
all the usings
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Display;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
Related
Here is the problem.
I have a view, that should display an image and some controls.
User add new images, changes some options and click "finish".
Images are large and very large (400-1500 MB Tiff)
User should see the preview of image, but it is ok if it loading for 10-15 sec or even more, he have a job for this time.
Image is binding through MVVM pattern like simple string (file will be always in local folder)
<Image Name="ImagePreview" Source="{Binding SFilePathForPreview,
FallbackValue={StaticResource DefaultImage},
TargetNullValue={StaticResource DefaultImage}}"
HorizontalAlignment="Center" Width="200" Height="200"
VerticalAlignment="Center" />
Problem is that all is hangs when user try to add a file for loading time.
I understand that this case should be solved through multithreading - but have no idea how to implement this.
I tryed to update image from view in different thread like this:
Thread newThread = new Thread(LazyLoad);
newThread.Name = "LazyLoad";
newThread.Start(SFilePathForPreview);
public void LazyLoad(object SFilePath)
{
try
{
string path = (string)SFilePath;
BitmapImage t_source = new BitmapImage();
t_source.BeginInit();
t_source.UriSource = new Uri(path);
t_source.DecodePixelWidth = 200;
t_source.EndInit();
t_source.Freeze();
this.Dispatcher.Invoke(new Action(delegate
{
ImagePreview.Source = t_source;
}));
}
catch
{
//...
}
}
But anyway at point
ImagePreview.Source = t_source;
everything hangs up until image fully loaded.
Is there a way to load a preview in the background and show it without those terrible hangs?
The probably most simple way of asynchronously loading an image is via an asynchronous Binding. You would not have to deal with Threads or Tasks at all.
<Image Source="{Binding Image, IsAsync=True}"/>
A possible view model could look like shown below, where you must make sure that the Image property getter can be called from a background thread.
public class ViewModel : ViewModelBase
{
private string imagePath;
private BitmapImage image;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
image = null;
OnPropertyChanged(nameof(ImagePath));
OnPropertyChanged(nameof(Image));
}
}
public BitmapImage Image
{
get
{
lock (this)
{
if (image == null &&
!string.IsNullOrEmpty(imagePath) &&
File.Exists(imagePath))
{
using (var stream = File.OpenRead(imagePath))
{
image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.DecodePixelWidth = 200;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
}
}
}
return image;
}
}
}
As you already mentioned, you are blocking the UI thread with the image load. You can use a WriteableBitmap class instance as the source for your Image. This will let you load the image on a background thread or async task. Here is a quick guide (not mine) on the issue.
https://www.i-programmer.info/programming/wpf-workings/527-writeablebitmap.html
Another option would be using priortybinding with the highest priorty to the full image and a lower priority to the faster-loading preview image. MS has documented priority binding here:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-prioritybinding
I'm building a simple application that shows an image bitmap using the Image class in UWP.
When i change something on the image, the program won't refresh the image displayed.
I've tried to change source using another temp image but the problem is still the same.
This is the object in the XAML file
<Image x:Name="image" HorizontalAlignment="Left" Height="128" VerticalAlignment="Top" Width="128" AutomationProperties.AccessibilityView="Raw" ManipulationMode="All"/>
This is the code that changes the source of the image
private void ChangeImage_BTN_Click(object sender, RoutedEventArgs e)
{
readImage();
}
private void readImage()
{
switch (nimg)
{
case 1:
image.Source = new BitmapImage(new Uri("ms-appx:///Assets/France.bmp"));
nimg = 3;
break;
case 2:
//image.Source = new BitmapImage(new Uri("ms-appx:///Assets/Inghilterra.bmp"));
//nimg = 3;
break;
case 3:
image.Source = new BitmapImage(new Uri("ms-appx:///Assets/Area24_128x128.bmp"));
nimg = 1;
break;
}
}
When i switch the image Area_128x128.bmp to France.bmp, I modify the bitmap file adding some draw, switching again to Area_128x128.bmp the image has the old things.
How can i see the image properly?
The "ms-appx:///" prefix in the URI refers to the app's install directory. It turns out to be not supported update edits of files at runtime.
For your user case, there are some workarounds you can have a try.
You can use ApplicationData.RoamingFolder like this:
Windows.Storage.StorageFolder roamingFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
var file = await roamingFolder.GetFileAsync("France.bmp");
using (var fileStream = (await file.OpenAsync(Windows.Storage.FileAccessMode.Read)))
{
var bitImg = new BitmapImage();
bitImg.SetSource(fileStream);
image.Source = bitImg;
}
You can use KnownFolders.PicturesLibrary like this:
var file = await KnownFolders.PicturesLibrary.GetFileAsync("France.bmp");
using (var fileStream = (await file.OpenAsync(Windows.Storage.FileAccessMode.Read)))
{
var bitImg = new BitmapImage();
bitImg.SetSource(fileStream);
image.Source = bitImg;
}
In general, the path of KnownFolders.PicturesLibrary is C:\Users\[YOUR USER NAME]\Pictures.
For more information, you can reference Store and retrieve settings and File access permissions.
I am developing windows mobile app 8.1,
I'd like to convert complete design in page(wpf control) into image.
Currently I have implemented screenshot capture task using this
Its capture currently viewing area not complete page if page design has scroll-able.
If I want to convert complete page design with scrollable design, how to do this?
Please check it below screen shot
This is the problem,
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(uielement);
its capture only current viewing area instead of complete uielement content.
Is it possible to capture complete uielement content?
You can use the RenderTargetBitmap class. Create an extension method which takes any UIElement of which you want to create an image (in this case it will be your page) and a file into which the generated image will be saved. The method return true if successful:
public static async Task<bool> SaveVisualElementToFile(this UIElement element, StorageFile file)
{
try
{
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(element);
var pixels = await renderTargetBitmap.GetPixelsAsync();
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
byte[] bytes = pixels.ToArray();
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
bytes);
await encoder.FlushAsync();
}
return true;
}
catch (Exception)
{
return false;
}
}
You need to make sure that your UIElement is in the visual tree (rendered), otherwise the method will not work.
Then you can call the method like this:
StorageFolder localFolder = ApplicationData.Current.TemporaryFolder;
StorageFile file = await localFolder.CreateFileAsync(ShareSreenshotFileName, CreationCollisionOption.ReplaceExisting);
await view.SaveVisualElementToFile(file);
EDIT:
Your problem appears because the ListView is virtualized and therefore not all items are rendered. You can disable virtualization by using a StackPanel as the items panel of the list view:
<ListView>
<ListView.ItemsStackPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListView.ItemsStackPanel>
</ListView>
Another problem is if you are passing the container of the listview (grid or whatever it is), only the container bounds will be displayed in the generated image, so you need to pass the ListView reference to the SaveVisualElementToFile method.
I'm trying to add a image to a togglebutton in WPF - C#. The thing is that the assignment I'm working on can't be made with the use of XAML at all. I've tried to set the Content property to an image, but all I get is a normal togglebutton, which isn't helping my cause at all.
myToggleButton = new ToggleButton();
myImage = new Image();
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource = new Uri("myImageResource.bmp", UriKind.Relative);
bmi.EndInit();
myImage.Source = bmi;
myToggleButton.Content = myImage;
Hope I supplied enough info, if not please ask for more.
Updated #Phil Wright:
When I ad an image like this:
myImage = new Image();
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource = new Uri("myImageResource.bmp", UriKind.Relative);
bmi.EndInit();
myImage.Source = bmi;
it works...
Update #Matt West:
myGrid.Children.add(MyToggleButton); // This gives me an empty ToggleButton
myGrid.Children.add(MyImage); // This gives me an image with content
You are creating a new toggle button but you aren't adding it to anything. The image is getting added to the toggle button but the actual toggle button isn't added as a Child to anything. You either need to add the toggle button in code behind with something like this:
this.AddChild(myToggleButton);
Or if you already have the toggle button defined in XAML with a name of myToggleButton then remove this line from your code above
myToggleButton = new ToggleButton();
As requested here is the code that works for me in its entirety:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Name="_Root">
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var tb = new ToggleButton();
var image = new Image();
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource = new Uri("/Images/6.png", UriKind.Relative);
bmi.EndInit();
image.Source = bmi;
tb.Content = image;
_Root.Children.Add(tb);
}
}
}
Where the image is a resource; as noted before those last two lines make no sense, if you can get the image to display on its own it should also display inside the button.
Are you sure that the provided bitmap resource is able to be located. If not then the image will be empty and so occupy no space and hence the toggle button looks empty.
image of a toggle button can be set like this:
ToggleButton tgb = new ToggleButton();
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource = new Uri("myImageResource.bmp", UriKind.Relative);
bmi.EndInit();
tgb.Content = new Image { Source = bmi };
I need to generate an image dynamically, and after I read the tutorial here I realize i can use all the controls and layouts from WPF to generate my rendering, and then save it as a JPG.
The idea is to use this instead of GDI+, which is quite primitive.
The question is, how to I create a regular dll file that would generate programatically a WPF canvas so then i can add controls to it and then ouput that to an image file. Keep in mind that it would be consumed by an ASP.NET application.
Any ideas somebody?
That example has a good start but I've found it's got a lot of unneeded junk along with it. The main thing is that you don't need to have a separate WPF project.
Here's what to do:
Reference PresentationCore, PresentationFramework and WindowsBase in your web project.
Create a Canvas and other WPF objects programmatically in an STA thread.
Call a few special methods on them to make sure they update outside of the context of a WPF app.
Render them to an image with RenderTargetBitmap.
Shut down the thread's dispatcher.
Set the mime type and output the image with ASP.NET.
To make this more efficient you could re-use the same thread rather than creating a new one for each image. In that case you would only need to clean up the dispatcher when you shut down the thread.
Here's the full working code I have:
using System;
using System.Web;
using System.Threading;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Documents;
public partial class _Default : System.Web.UI.Page
{
private byte[] imageBuffer;
public void Page_Load(object sender, EventArgs e)
{
this.RenderImage();
Response.Clear();
Response.ContentType = #"image/png";
Response.BufferOutput = true;
Response.BinaryWrite(this.imageBuffer);
Response.Flush();
}
public void RenderImage()
{
Thread worker = new Thread(new ThreadStart(this.RenderImageWorker));
worker.SetApartmentState(ApartmentState.STA);
worker.Name = "RenderImageWorker";
worker.Start();
worker.Join();
}
public void RenderImageWorker()
{
Canvas imageCanvas = new Canvas { Width = 600, Height = 200, Background = Brushes.Azure };
TextBlock tb = new TextBlock();
tb.Width = (double)400;
//tb.Height = (double)200;
tb.TextAlignment = TextAlignment.Center;
tb.Inlines.Add(new Run("This is "));
tb.Inlines.Add(new Bold(new Run("bold")));
tb.Inlines.Add(new Run(" text."));
tb.FontSize = 30;
tb.Foreground = Brushes.Blue;
imageCanvas.Children.Add(tb);
// Update layout
imageCanvas.Measure(new Size(imageCanvas.Width, imageCanvas.Height));
imageCanvas.Arrange(new Rect(new Size(imageCanvas.Width, imageCanvas.Height)));
RenderTargetBitmap bitmapRenderer = new RenderTargetBitmap((int)imageCanvas.ActualWidth, (int)imageCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmapRenderer.Render(imageCanvas);
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(bitmapRenderer));
using (MemoryStream memoryStream = new MemoryStream())
{
png.Save(memoryStream);
this.imageBuffer = memoryStream.ToArray();
}
if (bitmapRenderer.Dispatcher.Thread.IsAlive)
{
bitmapRenderer.Dispatcher.InvokeShutdown();
}
}
}