SimpleWPFReporting's PDF has no content - c#

I'm trying to generate a very simple PDF just for practice by using SimpleWPFReporting.
Since there is no sample code in its documentation, I tried creating a sample from what I understood:
Window1.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public class Sample
{
private string _aw;
public string Aw
{
get { return _aw; }
set { _aw = value; }
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
StackPanel stkMain = new StackPanel();
stkMain.Background = Brushes.Pink;
stkMain.Height = 100;
stkMain.Width = 100;
TextBlock txtSample = new TextBlock();
txtSample.Text = "SAMPLE TEXT";
stkMain.Children.Add(txtSample);
ReportOrientation portrait = ReportOrientation.Portrait;
Sample sample = new Sample()
{
Aw = "Some text"
};
Report.ExportReportAsPdf(stkMain, sample, portrait);
}
}
It does save as a PDF, but when I open it, it's blank. What I'm expecting is to see at least the TextBlock there.
I don't really understand the use of data context in the arguments, so I just added a class there. I tried looking for tutorials but failed. Did I miss something or did I create it wrong?

I believe you have to specify the stackpanel orientation as "vertical", as stated in the doc
https://github.com/maximcus/SimpleWPFReporting

After digging in to the source code, it uses a Visual,so you have to add the stackpanel to the visual tree in order to be renderer and then printed.
You can create a previewer window, add the stackpanel and now the pdf will show the content.
I will create a previewer by myself, so I will post when I finish it.

Related

In WPF, how do I change the source of an image programatically? [duplicate]

This question already has answers here:
How to load image to WPF in runtime?
(2 answers)
Closed 1 year ago.
I have a custom control with a default image that I want to change based on which iteration of the control it is. For example, I have one for "F1" and "NumLock" and so on. In the constructor of the control, I have this:
public FixerBox(Dictionary<string,string> deets)
{
InitializeComponent();
btnOff();
this.FixerTitle.Text = deets["Title"];
this.FixerDesc.Text = deets["Description"];
this.FixerTags.Text = deets["Tags"];
this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
}
The bitmap stuff was based on another answer and produces this:
Below is the control itself showing that it's correctly getting the title, tags, and description, but the image is bunk (on the left side, that thin grey line is the border that should be around the image).c#
If I was using HTML/CSS, I could right-click the image to see what exactly its properties are, but I don't know how to get that kind of information using WPF. The best I could manage was in the top area is a status window where I've manually printed a "Tostring" output of the first controls image source data. Near as I can tell, it's all correct, but there's no actual image there. Every subsequent control has the same output (one thin line where the image should be).
EDIT Per comments, here is some more of the information. The main XAML file loads up the controls like so in its constructor:
public partial class MainWindow : Window
{
private Fixers fixers = new Fixers();
// This is the custom control consisting mostly of various boxes
private Dictionary<string,FixerBox> fixerBoxes = new Dictionary<string, FixerBox> { };
public MainWindow()
{
InitializeComponent();
var fixNames = fixers.FixerNames();
foreach (string key in fixNames)
{
fixerBoxes[key] = new FixerBox(fixers.GetFix(key));
FixersArea.Children.Add(fixerBoxes[key]);
}
StatusBox.Text += fixerBoxes["F1"].FixerImg.Source.ToString();
}
}
The fixers variable is of class Fixers which consists of the below (abbreviated to show just the F1 function for brevity):
class Fixers
{
private string ClearWS(string str)
{
var first = str.Replace(System.Environment.NewLine, "");
return first.Replace("\t", "");
}
// Loads registry functions
private Regis regStuff = new Regis();
// Loads preferences from the file
private Prefs prefs = new Prefs();
// A timer to make sure the system behaves
private Timer watcher;
// Watcher action toggles
private bool watchNumL = false;
// Translation array from fix shortname to various data about them
private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
{
["F1"] = new Dictionary<string,string> {
["PrefName"] = "KillF1UnhelpfulHelp",
["Img"] = #"/graphics/F1key.png",
["Title"] = #"Diable F1 ""Help"" function",
["Description"] = #"
Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result?
Windows set the F1 key to a generic help function that basically never helps and always gets in the way.
Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord,
but this will stop the default Windows behavior in things like Windows Explorer at least.
",
["Tags"] = "#Keyboard,#Rage"
},
};
public Fixers()
{
// The readability hack above with multi-line strings introduces a bunch of extra whitespace. Let's clear that out
foreach (var fixKey in fixers.Keys)
{
fixers[fixKey]["Description"] = ClearWS(fixers[fixKey]["Description"]);
}
}
public List<string> FixerNames()
{
return fixers.Keys.ToList();
}
public bool IsFixed(string which)
{
// If we're watching, it's fixed
if ("NumL" == which) return watchNumL;
// For anything registry related
return regStuff.IsFixed(which);
}
public Dictionary<string,string> GetFix(string which)
{
return fixers[which];
}
}
if you use binding, you can create in your ViewModel a string, in which is stored the path of your image, then you can easily change programatically its path.
Then in XAML just bind image's source to the string.
In my case I have a list of objects, with the property `ImageName' :
<Image Source="{Binding DataContext.SelectedMacro.ImageName,
RelativeSource={RelativeSource AncestorType=Window}}"/>

Access data in datagridview from a class

I've read a lot of topics on this issue but I'm not finding an answer. I'm fairly new to this so please bear with me.
I'm trying to pass values from datagridview to a list. And then in a new class I want to make som methods accessing that list. Trouble is that when I pass the datagridview it returns it without content and values which means I can't do anything with it.
The code under ////TESTING//// works like I want. I create an instance of the specified list and it's counting the amount of rows properly, see screenshot.
public List<vertEl> getVertList = new List<vertEl>();
//Opens the file dialog and assigns file path to Textbox
OpenFileDialog browseButton = new OpenFileDialog();
private void browse_Click(object sender, EventArgs e)
{
browseButton.Filter = "Excel Files |*.xlsx;*.xls;*.xlsm;*.csv";
if (browseButton.ShowDialog() == DialogResult.OK)
{
//SOME CODE TO GET DATA FROM EXCEL AND SOME METHODS TO CALCULATE
//VALUES TO PASS TO THE TAB VERTIKALELEMENTER TAB IN MY DATAGRIDVIEW
//VERTIKALELEMENTER IS vertElementerDgv IN MY CODE
////TESTING////
GetVertElementasList TEST = new GetVertElementasList();
getVertList = TEST.vertList(vertElementerDgv);
MessageBox.Show(getVertList.Count.ToString());
}
else return;
}
I now want to do this in a seperate class and call a method from that class to do the same but when I try that with code underneath I do not get the same count as when I have the code in form1 (public partial class BridgeGeometry). It return count of 0. The method foo() is assigned to the button 1 in the form.
class GetKoord
{
public GetVertElementasList getList = new GetVertElementasList();
BridgGeometry obj = new BridgGeometry();
public void foo()
{
var TEST = getList.vertList(obj.vertElementerDgv);
//var TEST = obj.getVertList;
MessageBox.Show(TEST.Count.ToString());
}
}
I also tried to get the values directly from the datagridview but there's nothing in it when I access it from a class which is not the form1/BridgeGeometry class.
Form - screenshot
You could run a loop and store the information with selected rows into a public var with something like this:
string itemOne = dataGridView1.SelectedRows[0].Cells[1].Value + string.Empty;
string itemTwo= dataGridView1.SelectedRows[0].Cells[2].Value + string.Empty;
string itemThree = dgMasterGridBun.SelectedRows[0].Cells[3].Value + string.Empty;
Variables
public var varItemOne = itemOne;
public var varItemTwo = itemTwo;
public var varItemThree = itemThree;
Based on the link I managed to get this working. Probably not the best solution, but a working one. I tried to wrap my head around databinding, listbinding etc. but since the class with the input values are a messy one I gave that up for now. The datagriview input values are a little from lists and some from other datagridview.
MSDN-forum: Accessing Form1 controls from a different class
Explanations are given in the link so I'll just provide how I did it in my program.
If my GetKoord class are like this:
public class GetKoord
{
private BridgGeometry bridgeGeometry;
public GetKoord(BridgGeometry form1)
{
bridgeGeometry = form1;
}
public List<vertElementerParam> getListvertElementer(List<vertElementerParam> theList)
{
//var vertElementerDgv = bridgeGeometry.vertElementerDgv;
GetVertElementasList getVertElementasList = new GetVertElementasList();
List<vertElementerParam> MyDgvListList = new List<vertElementerParam>();
MyDgvListList = getVertElementasList.vertList(bridgeGeometry.vertElementerDgv);
//MessageBox.Show(MyDgvListList.Count.ToString());
theList = MyDgvListList;
return theList;
}
}
then I can get the list in Button1_Click like this, check the screenshot in the first post:
public List<vertElementerParam> getVertList = new List<vertElementerParam>();
private void button1_Click(object sender, EventArgs e)
{
GetKoord getKoord = new GetKoord(this);
List<vertElementerParam> testList = new List<vertElementerParam>();
testList = getKoord.getListvertElementer(getVertList);
MessageBox.Show(testList.Count.ToString());
}

Error in printing from WPF Canvas

I learn how to do this based on this tutorial : http://www.nbdtech.com/Blog/archive/2009/04/20/wpf-printing-part-2-the-fixed-document.aspx
This is the method that my Print Button fired when clicked :
PrintManager _pm;
private void btnPrint_Click(object sender, RoutedEventArgs e)
{
_pm = new PrintManager();
List<Canvas> pages = new List<Canvas>();
pages.Add(cnv);
_pm.Print(pages);
}
And this is my PrintManager.cs (_pm) :
private const double PAGE_WIDTH = 793.92; //8.27' * 96px
private const double PAGE_HEIGHT = 1122.24; //11.69' * 96px
private FixedDocument _document;
private List<FixedPage> _listOfPages;
private PageContent _tempPageContent;
private FixedPage _tempPage;
public PrintManager()
{
}
public void Print(List<Canvas> pages)
{
PrintDialog pd = new PrintDialog();
Canvas temp;
if (pd.ShowDialog() == true)
{
_document = new FixedDocument();
_document.DocumentPaginator.PageSize = new System.Windows.Size(PAGE_WIDTH, PAGE_HEIGHT);
_listOfPages = new List<FixedPage>();
for (int i = 0; i < pages.Count; i++)
{
_listOfPages.Add(new FixedPage());
_tempPage = _listOfPages[_listOfPages.Count - 1];
_tempPage.Width = PAGE_WIDTH;
_tempPage.Height = PAGE_HEIGHT;
_tempPage.Children.Add(pages[i]); //THIS IS THE PROBLEM LINE
_tempPageContent = new PageContent();
((IAddChild)_tempPageContent).AddChild(_tempPage);
_document.Pages.Add(_tempPageContent);
}
pd.PrintDocument(_document.DocumentPaginator, "Docdoc");
}
}
It turns out that there's an error from the problem line (_tempPage.Children.Add(pages[i]);) line which says :
Specified element is already the logical child of another element. Disconnect it first.
Any idea why?
Even removing the element from private List<FixedPage> _listOfPages; before it's added into the page list didn't help (of course I tried it outside the for loop) with just 1 canvas in the list.
P.S. I need it to be multi-page-able, since my document is usually long (it's a musical scores).
P.S.S. Even printing a canvas with no children manually added, the error was still the same.
P.S.S.S. Another simpler algorithm, even if it's different, is acceptable.
Thanks.
It seems that I need to "copy" the canvas content into a new canvas class before sending it to printer.
And clear it from the old canvas. Is anyone can do it better?

Windows Forms is getting too large

Im making a program with many many pages... and in my design, the buttons will eventually get stacked up, so its getting harder and harder to work the more there are.
This question is a clone of this topic.
However, i didn't really get the answer since they were talking about xaml and wpf.
I've also tried to make multiple windows forms, hide and show them to split it up.
But when i hide and show a wndow, its very very easily to see the GUI fading in and out which looks ugly.
I want an instant hide/show function so it looks like its just 1 program with 1 window and now switching.
So what are the tecniques to make a big windowsforms program more managable?
You may create a couple of UserControls, organize and separate your logic on them. Then you may use String Array to store your control names and iterate througth items to display apropriate view. The form may have Panel as container and Dock Style defined as Fill. The simplefied code will look like:
public class MyContainer:Control
{
public MyContainer(string szControlName, UserControl nControl)
{
UserControlName = szControlName; MyControl = nControl;
}
public string UserControlName { get; set; }
public UserControl MyControl { get; set; }
}
In main form:
public List<MyContainer> MyNavigationArray;
public void InitArray()
{
MyNavigationArray = new List<MyContainer>(MyFormPageCount);
for (int i = 0; i < MyFormPageCount; i++)
{
MyNavigationArray.Add(new MyContainer(TheNameOfUserControl, new PredefinedUserControl()));
}
}
private void NextButton_click(object sender, EventArgs e)
{
MyContainer mYcn = this.panel1.Controls[0] as MyContainer;
int nCurPos = MyNavigationArray.IndexOf(mYcn);
if (nCurPos < MyNavigationArray.Count)
{
panel1.Controls.Clear();
MyContainer c = MyNavigationArray[nCurPos + 1];
panel1.Controls.Add(c);
c.Dock = DockStyle.Fill;
}
}
All information stored inside user controls will be in your array and can be used later.
Hope this helped.

Insert Text and Image in HubTile after online-content is loaded

I'm new to WP8 and follow many tutorials. For parts of the menu I use a viewModel with NotifyPropertyChanged. When I get my list of news articles it creates a viewModel and displays it in a longListSelector.
But also I want to make 1 HubTile with the image and some preview-text of the first article. Is there a nice way to send some event to the .xaml.cs? Or do I have to make another viewModel for this one HubTile and make a binding?
Ony try was to make such a variable:
private bool _isDataLoaded = false;
public bool IsDataLoaded
{
get
{
return _isDataLoaded;
}
set
{
if (value != _isDataLoaded)
{
_isDataLoaded = value;
NotifyPropertyChanged("IsDataLoaded");
}
}
}
The same thing is used with "IsLoading"-variable to create a loading-indicator in the systemTray:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("MainPage_Loaded-Funktion");
Binding binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsVisibleProperty, binding);
binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsIndeterminateProperty, binding);
prog.Text = "Lade aktuelle Inhalte...";
}
Can I use this to call a function, when my variable is set and I get a notification?
The solution that helped me out was this:
<toolkit:HubTile Message="{Binding OnlineNews[0].TeaserText}"/>
Didn't know that you can access the viewModel like that. Thanks to Toni Petrina!

Categories