Telerik PdfViewer from MVVM View Not Displaying from Byte Stream - wpf

I'm trying to using MVVM binding for the pdfviewer but the document always turns out blank. I'm querying a DB field that contains the bytes of the PDF file and I'm trying to get it to display in the viewer (I've confirmed that the file is not malformed and it displays fine if I save it as a PDF to my desktop).
Here are my MVVM properties:
private RadFixedDocument _currentDocument;
public RadFixedDocument CurrentDocument
{
get => _currentDocument; set => this.RaiseAndSetIfChanged(ref _currentDocument, value);
}
Here is the method I'm using to populate the CurrentDocument property:
var sqlStr = #"select top 1 * from FileManager order by ID desc;";
var file = await con.QuerySingleAsync<FMFile>(sqlStr);
var fileBytes = file.FileContent.ToArray(); // this is type byte[] - I confirmed that it has the correct contents
var ms = new MemoryStream(fileBytes); // ms gets filled properly
FormatProviderSettings settings = new FormatProviderSettings(ReadingMode.AllAtOnce);
PdfFormatProvider provider = new PdfFormatProvider(ms, settings);
CurrentDocument = provider.Import();
And here is how my XAML looks:
<telerik:RadPdfViewerToolBar RadPdfViewer="{Binding ElementName=pdfViewer, Mode=OneTime}" />
<telerik:RadPdfViewer x:Name="pdfViewer"
Grid.Row="1"
DocumentSource="{Binding CurrentDocument, Mode=TwoWay}" />
However, nothing get's displayed in the viewer. I confirmed that my VM is hooked up correctly as other property display just fine. Any advice would be appreciated.

So it looks like you are binding to the wrong Object Type.
Instead of a RadFixedDocument, Bind to type PdfDocumentSource.
private PdfDocumentSource _PDFDocument;
public PdfDocumentSource PDFDocument
{
get { return _PDFDocument; }
set
{
_PDFDocument = value;
OnPropertyChanged("PDFDocument");
}
}
private void LoadPDFDocument(string pdfFilePath)
{
MemoryStream stream = new MemoryStream();
using (Stream input = File.OpenRead(pdfFilePath))
{
input.CopyTo(stream);
}
FormatProviderSettings settings = new FormatProviderSettings(ReadingMode.OnDemand);
PDFDocument = new PdfDocumentSource(stream, settings);
}

Related

In a WPF application (with Devexpress), Store data for retrieval after the application is reopened

I want to save the contents of a SelectedItem or Item's from a ComboBox as well as DataGrid Column Order so as retain the information when the application is reopened.
Initially I am using the below code for saving the data as long as the application is open:
App.Current.Properties[1] = SelectedDataSetList;
App.Current.Properties[2] = SelectedModuleList;
App.Current.Properties[0] = SelectedContentSet;
SelectedDataSetList is bound to a ComboBox:
<dxe:ComboBoxEdit Text="SCOPE" x:Name="ContentSetCombobox" Grid.Column="1" Height="25" IncrementalFiltering="True" ItemsSource="{Binding ContentSetList}" DisplayMember="Name" AllowUpdateTwoWayBoundPropertiesOnSynchronization="False" SelectedItem="{Binding SelectedContentSet,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
But, I was unable to store the information in a Chache memory for retrieving even if the application is closed and opened again.
Is there any way to do this without using an external file like .xml?
If you're looking to save to IsolatedStorage you can use this class I've put together (see below). It's not perfect and will fail if you try to save a type that isn't marked as Serializable but it's good enough for casual use. I've left exception handling as an exercise for the OP.
public class IsolatedStorageManager
{
public void Save<T>(T item, string key)
{
var isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
using (var writeStream = new IsolatedStorageFileStream(key, FileMode.Create, isf))
{
Serialise(item, writeStream);
}
}
public T Open<T>(string key)
{
var isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
using (var readStream = new IsolatedStorageFileStream(key, FileMode.Open, isf))
{
var item = Deserialise<T>(readStream);
return item;
}
}
private Stream Serialise<T>(T item, Stream stream)
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, item);
return stream;
}
private T Deserialise<T>(Stream stream)
{
var formatter = new BinaryFormatter();
var item = formatter.Deserialize(stream);
return (T) item;
}
}
Saving classes and datasets is demonstrated in the test fixture below.
[TestFixture]
public class IsolatedStorageManagerTestFixture
{
private IsolatedStorageManager _underTest;
private const string SaveFileKey = "TestSaveFileKey";
[SetUp]
public void SetUp()
{
_underTest = new IsolatedStorageManager();
}
[Test]
public void TestSavingDataset()
{
var tableName = "TestTable";
var ds = new DataSet();
ds.Tables.Add(new DataTable(tableName));
_underTest.Save(ds, SaveFileKey);
var saved = _underTest.Open<DataSet>(SaveFileKey);
Assert.That(saved.Tables.Count==1);
Assert.That(saved.Tables[0].TableName == tableName);
}
[Test]
public void TestSavingClass()
{
var list = new ArrayList {"Hello", new DataTable(), 2};
_underTest.Save(list,SaveFileKey);
var saved = _underTest.Open<ArrayList>(SaveFileKey);
Assert.That(saved.Count==3);
Assert.That(string.Equals((string)saved[0], "Hello"));
Assert.That(list[1] is DataTable);
Assert.That((int)list[2] == 2);
}
}

Alternative to BitmapImage to load a PNG

On a WPF project, I need to set a ImageSource property from a local Resource.
I am trying to use the following code:
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute);
bmp.EndInit();
Avatar = bmp;
Where Avatar is defined before as:
private ImageSource myAvatar;
The problem is that BitmapImage does not support MetaData information, that the source image have (it was created with Paint.net) so it throws a error. The error is the following:
Metadata = 'bmp.Metadata' threw an exception of type
'System.NotSupportedException'
So I believe that I need alternatives to BitmapImage to properly load the desired image.
As a end note, using the same image inside xaml, directly in a "source" property it works fine.
Thank you
Unlike BitmapImage, BitmapFrame supports the Metadata property:
So you may replace
Avatar = new BitmapImage(new Uri(...));
by
Avatar = BitmapFrame.Create(new Uri(...));
From MSDN:
BitmapFrame provides additional functionality not defined by
BitmapSource ... BitmapFrame also supports the writing of metadata
information by using the Metadata property or the
CreateInPlaceBitmapMetadataWriter method.
As you have the path to the image it can be done by below using Image Control,
XAML
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding ImagePath}"></BitmapImage>
</Image.Source>
</Image>
VM
public ViewerViewModel()// Constructor
{
ImagePath = #"..\Images\Untitled.jpg";// Change the image path based on your input.
}
private string _ImagePath = string.Empty;
public string ImagePath {
get { return _ImagePath; }
set { _ImagePath = value; NotifyPropertyChanged(); }
}
Problem solved.
This was inside a Class with the property:
private ImageSource myAvatar;
....
public ImageSource Avatar
{
set { myAvatar = Avatar; }
get { return myAvatar; }
}
And I was trying to change the Avatar (that sets the myAvatar through the set).
I do not understand why, but changing directly the myAvatar works. For example doing:
BitmapSource image = BitmapFrame.Create(new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute));
myAvatar = image;
Is ok, but:
BitmapSource image = BitmapFrame.Create(new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute));
Avatar = image;
Always sets Avatar as null.
This is a method inside a class. I would appreciate to understand why as it is not clear to me. Thank you.

Silverlight ComboBox Filter - Show All

In Silverlight5 with RIA Services, using DomainDataSources.
I have a filter ComboBox which a DataGrid is bound to.
Problem: How to implement a "Select All" at the top of the combo box - have currently done it by putting a row of ID=0 in the database and using IgnoredValue="0" in the filter
<riaControls:DomainDataSource.FilterDescriptors>
<riaControls:FilterDescriptor PropertyPath="AccountTypeID" Operator="IsEqualTo" IgnoredValue="0" Value="{Binding Path=SelectedItem.AccountTypeID,
ElementName=AccountTypeComboBox, FallbackValue=0}" />
</riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>
<riaControls:DomainDataSource x:Name="AccountTypeDataSource" AutoLoad="True" QueryName="GetAccountTypes" PageSize="15" LoadSize="30">
<riaControls:DomainDataSource.DomainContext>
<domain:xxxDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Would like to manually add the Show All in code after the data has been loaded in the ComboBox
EDIT
Thanks to Martin below I got it working like this:
private xxxDomainContext xxxDomainContext;
public MainPage()
{
InitializeComponent();
// Load up the AccountType ComboBox - instead of doing it in XAML
// then add in the Select All via proxy class above
xxxDomainContext = new xxxDomainContext();
EntityQuery<AccountType> query = xxxDomainContext.GetAccountTypesQuery();
LoadOperation loadOperation = xxxDomainContext.Load<AccountType>(query);
// everything is async so need a callback, otherwise will get an empty collection when trying to iterate over it here
loadOperation.Completed += AccountTypeLoadOperationCompleted;
and
private void AccountTypeLoadOperationCompleted(object sender, System.EventArgs e)
{
// create new proxy class
var listOfSelectableObjects = new List<SelectableObject<int>>();
var selectAll = new SelectableObject<int> { Display = "Select All", KeyValue = 0};
listOfSelectableObjects.Add(selectAll);
// load values into new list
foreach (var accountType in xxxDomainContext.AccountTypes)
{
var so = new SelectableObject<int>();
so.Display = accountType.Description;
so.KeyValue = accountType.AccountTypeID;
listOfSelectableObjects.Add(so);
}
AccountTypeComboBox.ItemsSource = listOfSelectableObjects;
// Set index to 0 otherwise a blank item will appear at the top and be selected
AccountTypeComboBox.SelectedIndex = 0;
}
In the past, I have achieved this by creating a proxy class, something like SelectableValue with a display, key and isDefault as shown below:-
public class SelectableObject<K>
{
public string Display { get; set; }
public K KeyValue { get;set; }
public bool IsDefaultSelection { get; set; }
}
This means that I can then transform the list of items I am selecting from into a List<SelectableObject<int>> for example, and optionally add an extra item to that list that is Display="Select All" and IsDefault=true rather than trying to bind directly to the list.
Hope that helps.

WPF loading serialized image

In an app I need to serialize an image through a binarywriter, and to get it in an other app to display it.
Here is a part of my "serialization" code :
FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write);
BinaryWriter bin = new BinaryWriter(fs);
bin.Write((short)this.Animations.Count);
for (int i = 0; i < this.Animations.Count; i++)
{
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(Animations[i].Image));
encoder.Save(stream);
stream.Seek(0, SeekOrigin.Begin);
bin.Write((int)stream.GetBuffer().Length);
bin.Write(stream.GetBuffer());
stream.Close();
}
bin.Close();
And here is the part of my deserialization that load the image :
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader bin = new BinaryReader(fs);
int animCount = bin.ReadInt16();
int imageBytesLenght;
byte[] imageBytes;
BitmapSource img;
for (int i = 0; i < animCount; i++)
{
imageBytesLenght = bin.ReadInt32();
imageBytes = bin.ReadBytes(imageBytesLenght);
img = new BitmapImage();
MemoryStream stream = new MemoryStream(imageBytes);
BitmapDecoder dec = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
img = dec.Frames[0];
stream.Close();
}
bin.Close();
When I use this method, I load the image (it seems to be stored in the "img" object) but it can't be displayed.
Has somedy an idea?
Thanks
KiTe
UPD :
I already do this : updating my binding, or even trying to affect it directly through the window code behing. None of these approaches work :s
However, when I add this :
private void CreateFile(byte[] bytes)
{
FileStream fs = new FileStream(Environment.CurrentDirectory + "/" + "testeuh.png", FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
at the end of you function, it perfectly create the file, which can be read without any problems ... So I don't know where the problem can be.
UPD 2 :
A weird things happens.
Here is the binding I use :
<Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding ElementName=lstAnims, Path=SelectedItem.Image}" Stretch="Uniform" />
(the list is itself binded on an observableCollection).
When I create manually the animation through the app, it works (the image is displayed).
But when I load it, it is not displayed (I look at my code, there are not any "new" which could break up my binding, since there are others properties binded the same way and they does not fail).
Moreover, I've put a breakpoint on the getter/setter of the properties Image of my animation.
When it is created, no problems, it has the good informations.
But when it is retreived through the getter, it return a good image the first time, and then and image with pixelWidth, pixelHeight, width, height of only 1 but without going through the setter anymore !
Any idea?
UPD3
tried what you said like this :
Added a property TEST in my viewModel :
private BitmapSource test;
public BitmapSource TEST { get { return test; } set { test = value; RaisePropertyChanged("TEST"); } }
then did it :
img = getBmpSrcFromBytes(bin.ReadBytes(imageBytesLenght));
TEST = img;
(in the code showed and modified before)
and my binding :
<Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding Path=TEST}" Stretch="Uniform" />
(datacontext is set to my ViewModel)
And it still doesn't work and does the weird image modification (pixW, pixH, W and H set to 1)
FINAL UPD:
It seems I finally solved the problem. Here is simply what I did :
byte[] bytes = bin.ReadBytes(imageBytesLenght);
MemoryStream mem = new MemoryStream(bytes);
img = new BitmapImage();
img.BeginInit();
img.StreamSource = mem;
img.EndInit();
the strange thing is that it didn't work the first time, maybe it is due to an architectural modification within my spriteanimation class, but I don't think it is.
Well, thank you a lot to Eugene Cheverda for his help
At the first, I think you should store your images in a list of BitmapSource and provide reverse encoding of image for using stream as BitmapImage.StreamSource:
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader bin = new BinaryReader(fs);
int animCount = bin.ReadInt16();
int imageBytesLenght;
byte[] imageBytes;
List<BitmapSource> bitmaps = new List<BitmapSource>();
for (int i = 0; i < animCount; i++)
{
imageBytesLenght = bin.ReadInt32();
imageBytes = bin.ReadBytes(imageBytesLenght);
bitmaps.Add(getBmpSrcFromBytes(imageBytes));
}
bin.Close();
UPD
In code above use func written below:
private BitmapSource getBmpSrcFromBytes(byte[] bytes)
{
using (var srcStream = new MemoryStream(bytes))
{
var dec = new PngBitmapDecoder(srcStream, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(dec.Frames[0]);
BitmapImage bitmapImage = null;
bool isCreated;
try
{
using (var ms = new MemoryStream())
{
encoder.Save(ms);
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
isCreated = true;
}
}
catch
{
isCreated = false;
}
return isCreated ? bitmapImage : null;
}
}
Hope this helps.
UPD #2
Your binding is incorrect. You may be should bind selected item to for example CurrentImageSource. And then this CurrentImageSource bind to Image control.
Code:
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<BitmapSource> ImagesCollection { get; set; }
private BitmapSource _currentImage;
public BitmapSource CurrentImage
{
get { return _currentImage; }
set
{
_currentImage = value;
raiseOnPropertyChanged("CurrentImage");
}
}
private void raiseOnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then:
<ListBox ItemsSource="{Binding ImagesCollection}" SelectedItem="{Binding CurrentImage}"/>
<Image Source="{Binding CurrentImage}"/>
ADDED
Here is the sample project in which implemented technique as I described above.
It creates animations collection and represent them in listbox, selected animation is displayed in Image control using its Image property.
What I want to say is that if you cannot obtain image as it should be, you need to search problems in serialization/deserialization.

Is it possible to embed metadata within Winform or control at runtime?

MS Access and VB has Tag property for form and control where you can store at runtime and persist with the app. Is there something equivalent in .NET ?
Most of the UI controls in .NET do have a .Tag property that can be used for the same purpose.
However, if you need additional functionality, you can make a class which inherits from the base control (i.e. you could make a class called SpecialPictureBox that inherits from PictureBox and adds additional fields), and use it in your Windows Forms just as you could use a PictureBox.
There is a Tag property in Windows Forms, but it is not persisted.
The pattern I use is to create a class called Preferences (has properties for each piece of information I want to persist), which is then managed with a PreferencesManager class like this one:
public class PreferencesManager
{
static private string dataPath = null;
static public string DataPath
{
get
{
if (dataPath == null)
{
string baseFolder = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
dataPath = Path.Combine(baseFolder, #"MY_APP_NAME");
if (!Directory.Exists(dataPath))
{
Directory.CreateDirectory(dataPath);
}
}
return dataPath;
}
}
/// <summary>
/// Saves the specified preferences.
/// </summary>
/// <param name="pref">The preferences.</param>
static public void Save(Preferences pref)
{
// Create file to save the data to
string fn = "Preferences.xml";
string path = Path.Combine(DataPath, fn);
using (FileStream fs = new FileStream(path, FileMode.Create))
{
// Create an XmlSerializer object to perform the serialization
XmlSerializer xs = new XmlSerializer(typeof(Preferences));
// Use the XmlSerializer object to serialize the data to the file
xs.Serialize(fs, pref);
}
}
static public Preferences Load()
{
Preferences ret = null;
string path = string.Empty;
try
{
// Open file to read the data from
string fn = "Preferences.xml";
path = Path.Combine(DataPath, fn);
using (FileStream fs = new FileStream(path, FileMode.Open))
{
// Create an XmlSerializer object to perform the deserialization
XmlSerializer xs = new XmlSerializer(typeof(Preferences));
// Use the XmlSerializer object to deserialize the data from the file
ret = (Preferences)xs.Deserialize(fs);
}
}
catch (System.IO.DirectoryNotFoundException)
{
throw new Exception("Could not find the data directory '" + DataPath + "'");
}
catch (InvalidOperationException)
{
return new Preferences();
}
catch (System.IO.FileNotFoundException)
{
return new Preferences();
}
return ret;
}
}
System.Windows.Forms.Control.Tag

Resources