I have a preexisting PDF with several drop down combo boxes. I would like to be able to get the list of options from these combo boxes in a C# program.
I have looked at iText but have not been able to figure out if it can do what I want it to.
Any suggestions would be greatly appreciated. Thank you in advance!
iText[Sharp] can indeed do what you want:
PdfReader read = new PdfReader(pdfPath);
AcroFields af = reader.getAcroFields();
String displayOptions[] = af.getListOptionDisplay(fldName);
String valueOptions[] = af.getListOptionValue(fldName);
That's Java code written off the cuff in the "Your Answer" box here, but I suspect the C# will be remarkably similar.
(is anyone else so accustomed to in-line expansions that they're expecting it outside their IDE? I keep hitting ctrl-space and expecting to see a list of available functions. :/ )
Display options are what the user sees, value options are what is submitted to the server. They are often identical, but not always. A list of countries might show their full name in the local language to the user, then use an international country code as the value... or it might both show and submit those codes. You get the idea.
You may try Docotic PDF Library. That library allows you to read and write combo box or other form elements values.
Here is a short sample for your task:
using System.Collections.ObjectModel;
using BitMiracle.Docotic.Pdf;
namespace BitMiracle.Docotic.Samples
{
public static class ReadComboOptions
{
public static void Main()
{
using (PdfDocument document = new PdfDocument("DocumentName.pdf"))
{
PdfCollection<PdfWidget> widgets = document.Pages[0].Widgets;
foreach (PdfWidget widget in widgets)
{
PdfComboBox comboBox = widget as PdfComboBox;
if (comboBox != null)
{
foreach (string item in comboBox.Items)
{
// do something with combo box option
}
}
}
}
}
}
}
Disclaimer: I work for the vendor of the library.
Related
In my application, there is a need to create an excel file which should have an auto-completion feature. Suppose I am fetching some values "Data 1","Data 2" from the database, after fetching I want to bind those values to a particular column of excel. So when a user types something it would prompt "Data 1","Data 2"...
How to achieve this? help needed
You are describing a feature called Data Validation. It's main purpose is to limit the possible values of a cell to a certain range but, as you have spotted, this has the handy side-effect of having Excel show a drop down of the available values.
You can do something like this using various openXML libraries which allow you to create .xlsx files in .NET code. I like EPPlus which would let you do something like this:
static void Main(string[] args)
{
var fInfo = new FileInfo("output.xlsx");
using (var excel = new ExcelPackage())
{
var sht1 = excel.Workbook.Worksheets.Add("DataSheet1");
sht1.Cells[1,1].Value = "Occupation:";
var validation = sht1.DataValidations.AddListValidation("A2");
foreach(var allowedValue in GetAllowedValues())
{
validation.Formula.Values.Add(allowedValue);
}
excel.SaveAs(fInfo);
}
}
private static IEnumerable<string> GetAllowedValues()
{
return new string []{"Doctor","Baker","Candlestick Maker"};
}
This gives the dropdown list you are referring to (my screengrab is from LibreOffice but you will see the same effect in Excel):
Im still working on my first asp.net core project and now I want to display "a qoute of the day".
I have the qoutes in a xml file stored in a folder called File under wwwroot.
Im planning on my making this a View Component.
Im used to working with web forms so it seems like Im spending alot of time on small issues, but I guess its the only way to learn.
I've created a folder named Custom where I plan to hold all my custom classes. the QuoteController.cs is located in the Controllers folder.
So yeah, I think I know how to crate the View Component. "I think" is an important factor here.
Im also used to using XmlDocument, so Im trying my best to get XmlReader to work. But any hint or tips would be highly appreciated.
This is what I got so far. QuoteController.cs
public class QuoteController : Controller
{
public Custom.Quote Index()
{
Custom.Quote result = new Custom.Quote();
XmlReader rdr = XmlReader.Create(#"\File\qoutes.xml");
Random rnd = new Random(DateTime.Now.Millisecond);
int tmp = rdr.AttributeCount;
int count = rnd.Next(0, tmp);
int i = 0;
while (rdr.Read())
{
if (count.Equals(i))
{
result = new Custom.Quote(rdr.GetAttribute("q"), rdr.GetAttribute("author"));
break;
}
i++;
}
rdr.Dispose();
rdr = null;
rnd = null;
return result;
}
}
I guess the next step will be to add some visuals, but I cant imagine that my code actully works. Does anybody know how to easily parse through and xml file i CORE? Should I go for async?
I guess it doesnt matter, but the xml file is formated like:
<quotes>
<q>Be Strong</b>
<author>Stein The Ruler</author>
</quotes>
Again, I will be very happy if you take the time to look at this :)
Thank you!
My way to implement this:
1)convert the xmldocument to look like this
<quotes>
<quote Content="Be Strong" Author="Stein..."/>
</quotes>
2) Fix the Custom.Quote object to contain these 2 (public getters, setters string) fields: Content and Author,
and then,3) use this code to turn the xml to a list:
XDocument quotesDoc = XDocument.Parse('your path');
List<Custom.Quote> quotes = quotesDoc.Root
.Elements("quote")
.Select(x => new Speaker
{
Content= (string)x.Attribute("Content"),
Author = (string)x.Attribute("Author")
})
.ToList<Custom.Quote>();
Hope this helps!
We recently upgraded from Ektron 8.6 to 9.0 (Ektron CMS400.NET, Version: 9.00 SP2(Build 9.0.0.249)).
I have some code (below) which we use to display links to items in a taxonomy. Under 8.6, this would show library items if they had been added to the taxonomy. As of 9.0, it no longer displays library items. It still works for DMS items and normal pages (all first class content in Ektron).
private List<ContentData> getTaxonomyItems(long TaxonomyId)
{
listContentManager = new ContentManager();
criteria = new ContentTaxonomyCriteria(ContentProperty.Id, EkEnumeration.OrderByDirection.Ascending);
criteria.PagingInfo = new Ektron.Cms.PagingInfo(400); // there's a lot of items and I don't want to page them.
criteria.AddFilter(TaxonomyId, true); // this gets sub taxonomies too :)
List<ContentData> contentList = listContentManager.GetList(criteria);
return contentList;
}
(I would love to simply say to users to use the DMS instead of the library, but we have a security requirement and I'm not aware of a way I can enforce security on DMS items like we can with library items by dropping a webconfig file in the library folder.)
Is this a bug that anyone else has experienced?
Or is there a problem with my code (did an API change in the upgrade to 9.0)?
Thanks.
I ended up emailing Ektron support in Sydney (I'm in Australia), and they said:
I would expect ContentManager to only return content, not library
items – must have been a loophole which is now closed. Taxonomy is the
way to go.
So I used some of the code they provided and came up with the following, which appears to work...
private List<TaxonomyItemData> getTaxonomyItems(long TaxonomyId)
{
List<TaxonomyItemData> list = new List<TaxonomyItemData>();
TaxonomyManager taxManager = new TaxonomyManager(Ektron.Cms.Framework.ApiAccessMode.Admin);
TaxonomyCriteria taxonomyCriteria = new Ektron.Cms.Organization.TaxonomyCriteria();
taxonomyCriteria.AddFilter(Ektron.Cms.Organization.TaxonomyProperty.Path,
Ektron.Cms.Common.CriteriaFilterOperator.StartsWith, GetTaxonomyPathById(TaxonomyId));
List<TaxonomyData> TaxonomyDataList = taxManager.GetList(taxonomyCriteria);
foreach (TaxonomyData taxd in TaxonomyDataList)
{
TaxonomyData taxTree = taxManager.GetTree(taxd.Path,
1, // depth. doesn't seem to work. have to manually tranverse lower taxonomies.
true, // include items
null,
Ektron.Cms.Common.EkEnumeration.TaxonomyType.Content,
Ektron.Cms.Common.EkEnumeration.TaxonomyItemsSortOrder.taxonomy_item_display_order);
foreach (TaxonomyItemData taxItem in taxTree.TaxonomyItems)
{
list.Add(taxItem);
}
}
return list;
}
private static String GetTaxonomyPathById(long taxonomyId)
{
TaxonomyManager tMgr = new TaxonomyManager();
TaxonomyData tData = tMgr.GetItem(taxonomyId);
if (tData != null)
{
return tData.Path;
}
return "";
}
This code fetches items for all the child taxonomies as well as returning library items.
The one problem is that it fetches duplicates for some items, but those are easy to clean out.
I was also told by Ektron that...
TaxonomyManager.GetItem(“{path}”) is a more efficient way to get the
categories
That's why I've included the GetTaxonomyPathById() method (inspired by this blog post: http://www.nimbleuser.com/blog/posts/2009/iterating-through-ektron-content-in-multiple-taxonomies-via-directly-interfacing-with-search-indexing-services/ )
I am a bit new at C# and I have run into a string concatenation issue. I am hoping someone might be able to give me a hint and help me resolve this. I have searched Google extensively and have spent more than a week on this so any help/advice would be greatly appreciated.
I have created a custom PathEditor for a string property. The property basically allows the user to key in a file to use in the app. If the file typed in is correct, it shows in the property cell as it should. What I am trying to do is output to the property cell an error message if the file typed in does not exist - I check this in my file validator. Here is the string literal issue.
If I use:
return inputFile+"Error_";
this works OK and I get the outpur file123.txtError_ in the property grid cell.
If I use:
return "Error_"+inputFile;
I get only the inputFile without the literal "Error_". Sot he property grid cell shows file123.txt in the property grid cell.
I have checked and inputFile is a string type. Any ideas as to why this is happening?
Also, is there any way to change to font, and/or, color of the message output? I tried to change the background of the property grid cell and I understand that this is not possible to do.
Thank you.
Z
More of the code:
[
Description("Enter or select the wave file. If no extension, or a non .wav extension, is specified, the default extension .wav will be added to the filename."),
GridCategory("Sound"),
Gui.Design.DisplayName ("Input Sound"),
PathEditor.OfdParamsAttribute("Wave files (*.wav)|*.wav", "Select Audio File"),
Editor(typeof(PathEditor), typeof(System.Drawing.Design.UITypeEditor))
]
public string InputWavefile
{
get { return System.IO.Path.GetFileName(inputtWavefile); }
set
{
if (value != inputWavefile) // inputWavefile has been changed
{
// validate the input stringg
_inputWavefile = FileValidation.ValidateFile(value);
// assign validated value
inputWavefile = _inputWavefile;
}
}
}
My guess is that you've got a funky character at the start of inputFile which is confusing things - try looking at it in the debugger using inputFile.ToCharArray() to get an array of characters.
The string concatenation itself should be fine - it's how the value is being interpreted which is the problem, I suspect...
I'm guessing your filename looks something like this, C:\Folder\FileName.txt when you start out.
In your FileValidation.ValidateFile() method you
return "Error_" + InputFileName;
it now looks like this: Error_C:\Folder\FileName.txt.
So, when you run the line below,
get { return System.IO.Path.GetFileName( _inputWavefile ); }
it strips off the path and returns the filename only, FileName.txt.
Even when the filename is not valid, you are still running System.IO.Path.GetFileName() on it.
Assuming this is a PropertyGrid in winforms app. Then it's neither a string concatenation issue, nor PropertyGrid issue, as could be proven by the following snippet. So you need to look elsewhere in your code:
public partial class Form1 : Form {
PropertyGrid pg;
public Form1() {
pg = new PropertyGrid();
pg.Dock = DockStyle.Fill;
this.Controls.Add(pg);
var inputFile = "some fileName.txt";
var obj = new Obj();
obj.One = "Error_" + inputFile;
obj.Two = inputFile + "Error_";
pg.SelectedObject = obj;
}
}
class Obj {
public string One { get; set; }
public string Two { get; set; }
}
I have a program that uses clipboard but I want to restore the clipboard to its former state after I am done with it.
This is my code :
IDataObject temp = Clipboard.GetDataObject();
//Some stuff that change Cliboard here
Clipboard.SetText("Hello");
//Some stuff that change Cliboard here
Clipboard.SetDataObject(temp);
But it if I copy a text, and run this code, I get nothing on notepad.
NOTE : I can't use Clipboard.Contains because I want to preserve the Clipboard EXACLY how it was before, even if the user copied a file.
I cannot confirm whether this will work, but I see no reason why you shouldn't be able to back up the data using the longer approach of actually reading the data and restoring it afterwards.
Read here: http://msdn.microsoft.com/en-us/library/system.windows.forms.idataobject.aspx
You would do something like (pseudo-code)
//Backup
var lBackup = new Dictionary<string, object>();
var lDataObject = Clipboard.GetDataObject();
var lFormats = lDataObject.GetFormats(false);
foreach(var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Would be interesting to check the contents of lDataObject here
//Restore data
foreach(var lFormat in lFormats)
{
lDataObject.SetData(lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
Is your application exiting after resetting the clipboard?
Assuming it is a Win Form app. (not sure how it works in wpf though)
You could use one of the other overloaded version of Clipboard.SetDataObject
public static void SetDataObject(object data, bool copy)
which preserves the data even after your app exits.
ex: in your case after removing the text content you could call Clipboard.SetDataObject(iDataObject, true);
EDIT:2
I Could source step through Clipboard.cs .NET Frameword 4 / VS 2010.
Download the .NET Framework 4 from here http://referencesource.microsoft.com/netframework.aspx.
Follow the below steps and if it asks for the source (Clipboard.cs) it would be in the Source sub-dir of the installation dir.
EDIT:1
Not sure why the same code doesn't work.
Cannot be a security/permission issue as the code doesn't throw an exception as you say.
There is another approach - source stepping into Framework code - Clipboard.cs
Based on the VS version and .NET framework it may vary ( I couldn't get the source stepping work for .NET 4 as the info is that the symbols with source support haven't yet been released). I'm trying my luck by downloading it manually from here (.NET Version 4)
If you are running VS 2008 and older version of .NET then the below steps should work for you.
More details are here. For .NET Framework 4 - here
This cannot be done. You cannot backup/restore the clipboard without causing unintended consequences.
Please see my post on a similar question. My answer is the one that starts with "It's folly to try to do this".
How do I backup and restore the system clipboard in C#?
Furthermore, I suspect that your motivation for wanting to backup/restore the clipboard is because you want to use it as a crutch to move data, without the user's knowledge or consent. Please read:
http://www.clipboardextender.com/developing-clipboard-aware-programs-for-windows/common-general-clipboard-mistakes
and
http://www.flounder.com/badprogram.htm#clipboard
Lastly, please read and understand this quote:
“Programs should not transfer data into our out of the clipboard without an explicit instruction from the user.” — Charles Petzold, Programming Windows 3.1, Microsoft Press, 1992
I tested the pseudocode from Lukas and found out doesn't work always, this works in all my tests:
// Backup clipboard
lBackup = new Dictionary<string, object>();
lDataObject = Clipboard.GetDataObject();
lFormats = lDataObject.GetFormats(false);
foreach (var lFormat in lFormats)
{
lBackup.Add(lFormat, lDataObject.GetData(lFormat, false));
}
//Set test data
Clipboard.SetText("asd");
//Restore clipboard
lDataObject = new DataObject();
foreach (var lFormat in lFormats)
{
lDataObject.SetData(lFormat, lBackup[lFormat]);
}
//This might be unnecessary
Clipboard.SetDataObject(lDataObject);
I have had success with this.
...to a certain degree.
Where I am currently falling down is trying to copy and restore Bitmaps of varying size.
I can successfully copy and restore a Bitmap of smallish size.
I then tried to do the same for (as the fore-warning Chris Thornton suggested) a gargantuan Excel worksheet with both thousands of cell data, as well as two sets of data on a graph, lying on the same worksheet.
I have found that the data copies and restores without problem. Where it falls down in this instance is allowing the 2-set graph with the worksheet copy.
If any of you have had a problem in copying and restoring Bitmaps, let me suggest what worked for me: when attempting to restore the Clipboard, iterate through the list of formats in reverse order and set each data object that way. (i.e. It seems that a Clipboard must be set in reverse order that it was copied in)
Regarding the case of the gargantuan Excel worksheet and accompanying graph, I also hit another stumbling block: I could not successfully copy the data object whose format was "Meta Data File". That could be the reason why Copy/Restore doesn't work in this case.
I got this far about two weeks ago, and tabled it for more pressing issues.
I wanted to put this out there to let anyone else trying to do the same that it seems like it can be done. (anything can be done in computer science. anything.)
I compiled this code and it seems to work for me. I am persisting via converting to and from json. (Note. It will not do steams so adapt if you need it to)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Clipboard
{
class Program
{
static void Main(string[] args)
{
Execute(() =>
{
var backup = Backup();
System.Windows.Forms.Clipboard.SetText("text"); //just to change clipboard
Restore(backup);
});
}
private static void Execute(Action action)
{
var thread = new Thread(() => action());
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private static List<ClipboardItem> Backup()
{
var backup = new List<ClipboardItem>();
var data = System.Windows.Forms.Clipboard.GetDataObject();
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true); //This seems to be needed to be able to serialize data later.
data = System.Windows.Forms.Clipboard.GetDataObject();
var formats = data.GetFormats(false).ToList();
formats.ForEach(f =>
{
if (data.GetData(f, false) != null && !(data.GetData(f, false) is Stream))
{
backup.Add(new ClipboardItem()
{
Format = f,
ObjectType = data.GetData(f, false).GetType(),
ObjectJson = JsonConvert.SerializeObject(data.GetData(f, false))
});
}
});
return backup;
}
private static void Restore(List<ClipboardItem> backup)
{
var data = new System.Windows.Forms.DataObject();
backup.ForEach(item =>
{
data.SetData(item.Format, JsonConvert.DeserializeObject(item.ObjectJson, item.ObjectType));
});
System.Windows.Forms.Clipboard.SetDataObject(data, copy: true);
}
}
public class ClipboardItem
{
public string Format { get; set; }
public Type ObjectType { get; set; }
public string ObjectJson { get; set; }
}
}