I've got a WPF application that needs to send pre-generated EMF files to a specified printer/tray.
I don't want to show the PrintDialog; the actual printer/tray is configured before hand. I also don't need to actually view the EMF file either; but rather just send it to the printer.
So far, all my R&D into this has led to is a bunch of posts that are 5 years old dealing with EMF and WPF and how it isn't supported.
Has anybody had any luck with this before? Can someone point me in the right direction?
Turns out this was easier than I thought. You can do this via a Image control, and use of a converter. This example takes the file location of the emf file, and puts it into a WPF usercontrol that I then send to a printer.
In XAML:
<Grid Margin="12">
<Image Source="{Binding Path=FileName, Converter={StaticResource emfImageConverter}, Mode=OneWay}"></Image>
</Grid>
and your converter class:
[ValueConversion(typeof(string), typeof(BitmapImage))]
public class EmfImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var fileName = (string)value;
if (fileName == null || !File.Exists(fileName))
return new BitmapImage();
using (var stream = File.Open(fileName, FileMode.Open))
{
return GetImage(stream);
}
}
internal BitmapImage GetImage(Stream fileStream)
{
var img = Image.FromStream(fileStream);
var imgBrush = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad, CreateOptions = BitmapCreateOptions.PreservePixelFormat };
imgBrush.BeginInit();
imgBrush.StreamSource = ConvertImageToMemoryStream(img);
imgBrush.EndInit();
return imgBrush;
}
public MemoryStream ConvertImageToMemoryStream(Image img)
{
var ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
I have an ItemsControl control. In its items I show a lot of things: two images in each of the items, some textblocks, etc.
One of the images that are shown in each of the items is the same for all of the items. It's in the Resources.resx file in my project and here's how I load it:
<Image Width="60" Height="60" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="2" Grid.RowSpan="2">
<Image.Source>
<Binding Source="{x:Static properties:Resources.myImageName}" Converter="{StaticResource BitmapToImageConverter}" />
</Image.Source>
</Image>
The Convert method of my BitmapToImageConverter converter class looks like this:
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Now, the binding takes a lot of time and I want to shorten it somehow. The Convert method gets called once for every item in my itemscontrol, but with the same image (the same parameters). How can I call it just once for all of the items?
The Converter extends the IValueConverter class.
If I havent misunderstood your question Try this
public class ImageConverter : ValueConverter
{
static BitmapImage resultImage = null;
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (resultImage != null)
return resultImage;
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Create a static variable and assign it the value first time and return it for next times
I have a image in window
<Image Source="{Binding Path=MYImage, Converter={StaticResource ResourceKey=imageConverter}}" />
I have also tried using a value converter:
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and created a dependency property for it.
public string MYImage
{
get { return (string)GetValue(MYImageProperty); }
set { SetValue(MYImageProperty, value); }
}
public static readonly DependencyProperty MYImageProperty=DependencyProperty.Register("PickerImage",typeof(string),typeof(MYClass),new PropertyMetadata("/MYProject;component/pic.png"));
but when i use it, don't show image !!!
you don't have to convert the source.
you can Bind a string like that:
"/My.Namespace;component/Resources/thatsMyImage.png"
xaml:
<Image Source="{Binding Path=MYImage}" />
This is how I resolved the issue in my application.
My Application has many solutions and each solution has many projects.
I am running .NET 4.5 on VS 2012 using WPF.
My application structure is:
MyApplication
CoreSolution
-ProjectA1
-Resources
-Images
warning.ico (Build action set to Resource)
information.ico (Build action set to Resource)
error.ico (Build action set to Resource)
+ProjectA2
PersonDatabaseSolution
+ProjectB1
+ProjectB2
I have added images (actual images and not links) in CoreSolution's ProjectA1 project. I did not change the build action of any of the images. Compiled the project to get ProjectA1.dll.
In ProjectB2's PersonDatabaseSolution, I refer to error.ico in code behind using the following:
private ImageSource _myImage
public ImageSource MyImage
{
get
{
if(_myImage==null)
{
uriLoc=new Uri("pack://application:,,,/CoreSolution.ProjectA1;component/Resources/Images/error.ico", UriKInd.Absolute);
BitmapImage bmImage = new BitmapImage();
bmImage.BeginInit();
bmImage.UriSource = uriLoc;
bmImage.EndInit();
_myImage=bmImage;
}
return _myImage;
}
}
The MyImage property is bound in the xaml:
<Image Source="{Binding Path=MyImage}"/>
So far this has worked for me. Hope it helps others as well.
Thanks,
RDV
I need some help to figure out why two different ways of binding an Image in XAML don't behave the same way.
Here is my code:
public class Picture
{
public int ID
{
get;
set;
}
public string ThumbURL
{
get
{
return String.Format("http://"+ App.ServerAdress +"/Pictures/thumbs/{0}.jpg", ID);
}
}
public int ThumbLocal
{
get { return ID; }
}
}
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
using (var store =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.FileExists(value)) return null;
var stream =
store.OpenFile(path, FileMode.Open);
try
{
var image = new BitmapImage();
image.SetSource(stream);
return image;
}
finally
{
stream.Dispose();
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
In case I bind the image this way, it is working:
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbURL}"
Margin="12,0,9,0"/>
But this way does not:
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbLocal, Mode=TwoWay, Converter={StaticResource imgConverter}}"
Margin="12,0,9,0"/>
This situation happens when I try to set picture from one panorama item to the other. I my case I want to add picture from one list to another and when I use first type of binding everything is working fine. Also it is working when I navigate to other page and navigate back, the picture shows up. I'm a little confused by this behavior.
Any ideas how I can solve this problem?
Finally I realized why this happened. Before Binding I have to download picture from the web and save it to the Isolated Storage, all this process is async. And in time when I Bind the picture there in no picture yet. I have try to Bind existing picture from Isolated Storage and it is correctly displayed. So, now I need something like INotifyPropertyChanged. Is any way I can do it if I use IValueConverter?
You can make it so that if the image is not in isolated storage - you start downloading the image, but return a new BitmapImage immediately and only when downloading is complete - save the image to isostore and set it as the BitmapImage's source.
Something like:
if (!store.FileExists(value))
{
var image = new BitmapImage();
ImageDownloadAndCachingHelper.DownloadImage(path, (s, e) => image.SetSource(e.ImageStream));
return image;
}
It is up to you to implement the code that converts the local path into a web URI, download it and invoke the EventHandler, where ImageDownloadedEventArgs has the ImageStream.
I need to find the way to save images to the IsolatedStorage and show them I the Silverlight (XAML)
Important: Silverlight have to take image “himself”, I cannot set the image from the code behind
I have tried many solutions before.
The very last solution is, to bind byte array and convert them to the Image
XAML
StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbLocal,Converter={StaticResource imgConverter}}"
Margin="12,0,9,0"/>
<StackPanel Width="311">
Code behind
public byte[] ThumbLocal
{
get;
set;
}
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
MemoryStream memStream = new MemoryStream((byte[])value);
memStream.Seek(0, SeekOrigin.Begin);
BitmapImage thumbLocal = new BitmapImage();
thumbLocal.SetSource(memStream);
return thumbLocal;
}
}
Everything work till I save byte[] to the database and tried to retrieve.
By now I can see the only option save image as file to the IsolatedStorage and then retrieve and covert to byte[].
Is this “smart ” solution?
First, create this converter:
public class BinaryToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is byte[])
{
var bytes = value as byte[];
var stream = new MemoryStream(bytes);
var image = new BitmapImage();
image.SetSource(stream);
stream.Close();
return image;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Second, bind to Your byte[] using this converter, i.e. if you are using MVVM:
View:
<Image Source="{Binding IsolatedStorageImage, Converter={StaticResource BinaryToImageSourceConverter}}" x:Name="ScanImage"/>
You can make property in contrlol (prop snippet) type byte[] and read image to byte array from isostorage, then set value of property to it.
If You got more questions, feel free to ask me.
I'm using prism to load views to region. The problem is the loaded view overlapped the title bar of the main windows - the bar contains caption, close/minimize/maximize buttons. How can I get the title bar's height? Prefer to get it right in the xaml codes.
After a while, I figure it out:
<Window xmlns:local="clr-namespace:System.Windows;assembly=PresentationFramework">
<YourView Height="{x:Static local:SystemParameters.WindowCaptionHeight}" />
</Window>
Hope that helps!
SystemParameters.WindowCaptionHeight is in pixels whereas WPF needs screen cordinates. You have to convert it!
<Grid>
<Grid.Resources>
<wpfApp1:Pixel2ScreenConverter x:Key="Pixel2ScreenConverter" />
</Grid.Resources>
<YourView Height="{Binding Source={x:Static SystemParameters.WindowCaptionHeight},Converter={StaticResource Pixel2ScreenConverter}}" />
</Grid>
aa
public class Pixel2ScreenConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double pixels = (double) value;
bool horizontal = Equals(parameter, true);
double points = 0d;
// NOTE: Ideally, we would get the source from a visual:
// source = PresentationSource.FromVisual(visual);
//
using (var source = new HwndSource(new HwndSourceParameters()))
{
var matrix = source.CompositionTarget?.TransformToDevice;
if (matrix.HasValue)
{
points = pixels * (horizontal ? matrix.Value.M11 : matrix.Value.M22);
}
}
return points;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I think you can do this since .NET Framework 4.5.
Reference: https://learn.microsoft.com/en-us/dotnet/api/system.windows.shell.windowchrome.captionheight?view=windowsdesktop-7.0#system-windows-shell-windowchrome-captionheight
Here is what you can do:
double title_height = (new WindowChrome()).CaptionHeight;