How to create autocompletion in Excel? - c#

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):

Related

Creating XML file from legay application

I have an old application that I wrote in Access VBA, the time has come to upgrade the code and the company decided to go with C# since we use it the most. My question is following, I have this code in VBA that works great,
Set RS2 = Db.OpenRecordset("Select * FROM TTable WHERE ID="&Forms![test]![SifraFirme]&")
su = RS2.RecordCount
RS2.MoveFirst
Do While Not RS2.EOF
//lines of code
RS3.MoveNext
Loop
RS3.Close
Now my question is, is there a C# command similar to Do While Not RS.EOF, any literature or examples would be highly appreciated. Just a nudge in the right direction because it has become frustrating. The main point of code above is to go through the table and filter the data and write it to XML (predefined structure) based on ID once he is done with first, move on to the second, and ...
Thank you,
Answering to:
The main point of code above is to go through the table and filter the
data and write it to XML
You can read database table to some DataSet, using OleDbDataAdapter from System.Data namespace. Then easily work with filled DataSet or instantly get its XML representation by GetXml method:
static void Main(string[] args)
{
// Note about set Prefer 32-bit app version of your C# app to use Jet.OLEDB provider
var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=YourDBPath";
var query = "Select * FROM TTable";
// Introducing our DataSet
var dataSet = new System.Data.DataSet();
using (var connection = new System.Data.OleDb.OleDbConnection(connectionString))
{
var command = new System.Data.OleDb.OleDbCommand(query, connection);
try
{
connection.Open();
using (var dataAdapter = new System.Data.OleDb.OleDbDataAdapter(command))
{
// Fill DataSet
dataAdapter.Fill(dataSet);
}
// Get XML representation of DataSet and save to XML file
System.IO.File.WriteAllText(#"TTable.xml", dataSet.GetXml());
// Or if need to filter data before save - read through DataSet
var TTable = dataSet.Tables["TTable"];
foreach (var row in TTable.Rows.Cast<System.Data.DataRow>().ToArray()) // using System.Linq needed
{
}
}
catch (System.Exception ex)
{
// Handle exception in some way
System.Console.WriteLine(ex.Message);
}
}
System.Console.ReadKey();
}
C# has the XMLWriter class and you can use the SQL classes for querying and reading the information.
The while loop in C# would be something like this:
while (!RS2.EOF)
{
//lines of code
RS2.MoveNext();
}
The ! is the Logical negation operator.
ADO.NET has the DataSet class which works with data in a way that is similar to a RecordSet in VBA.
See Microsoft's documentation on DataSet

Programmatically script SSIS package - Dynamic XML to OLE DB

I've been trying to piece together how other users have finished their projects, but my understanding is still limited.
I want to take any given XML source, make a Data Flow Task, and pass its data to an OLE DB destination matching the table name of the XML file. Running it with the visual tool means I cannot do dynamic data flow tasks because the Metadata does not refresh.
I have created a script that creates a package, but when I open the package in Visual Studio, it has a red-x saying that there cannot be zero Input Columns. When I drill down and look at the mappings of the OLE DB Destination, then click OK - it corrects it for me. I cannot figure out how to do that programmatically.
I've seen others solve it by using foreach loops and going through the Input columns, but I cannot seem to figure it out.
I also have a separate script that I tried to mimic several people's scripts with, and it has different issues. Not sure how to post it as an attachment
Thank you in advance for the help :)
EDIT
I've been getting positive feedback for trying out BIML, and I will...but I want to know if in the short term anyone can help me figure out why this doesn't fill in ExternalMetaDataColumnId for my input. I've posted my updated code below with foreach loops that aren't doing what I expect them to.
Thank you
#region
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using System.Xml;
#endregion
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
#region Initial Setup
Application a = new Application();
Package p = new Package();
TaskHost t = p.Executables.Add("DTS.Pipeline") as TaskHost;
t.Name = "DataFlow Task";
t.Description = "Flat File to Database";
MainPipe mp = t.InnerObject as MainPipe;
#endregion
#region Flat File Source in Dataflow Task
IDTSComponentMetaData100 md = mp.ComponentMetaDataCollection.New();
md.ComponentClassID = "Microsoft.XmlSourceAdapter";
md.Name = "XML Source";
CManagedComponentWrapper wrp = md.Instantiate();
wrp.ProvideComponentProperties();
#endregion
#region Add connection manager to OLE DB
ConnectionManager conn = p.Connections.Add("OLEDB");
conn.Name = "westcoastuserDBO";
conn.ConnectionString = "Data Source=SERVER;Initial Catalog=DBO;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;";
#endregion
#region XML Source Properties
wrp.SetComponentProperty("XMLData", #"C:\Users\file.xml");
wrp.SetComponentProperty("XMLSchemaDefinition", #"C:\Users\file.xsd");
wrp.SetComponentProperty("AccessMode", 0);
wrp.SetComponentProperty("UseInlineSchema", false);
//below does not work
//wrp.SetComponentProperty("XMLIntegerMapping", 0).TypeConverter = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter + XMLIntegerMappingConverter";
wrp.ReinitializeMetaData();
wrp.ReleaseConnections();
IDTSComponentMetaData100 md2 = mp.ComponentMetaDataCollection.New();
md2.ComponentClassID = "Microsoft.OLEDBDestination";
CManagedComponentWrapper wrp2 = md2.Instantiate();
wrp2.ProvideComponentProperties();
md2.Name = "OLE DB Connection";
md2.UsesDispositions = true;
md2.Version = 4;
wrp2.SetComponentProperty("OpenRowset", "dbo.authorizations");
#endregion
IDTSPath100 path = mp.PathCollection.New();
path.AttachPathAndPropagateNotifications(md.OutputCollection[0], md2.InputCollection[0]);
IDTSInput100 input = md2.InputCollection[0];
IDTSVirtualInput100 vInput = input.GetVirtualInput();
//below taken from https://stackoverflow.com/questions/12587709/c-sharp-ssis-data-flow-component-creating-custom-input-columns
IDTSExternalMetadataColumnCollection100 externalColumnCollection = input.ExternalMetadataColumnCollection;
// Iterate through the virtual input column collection.
foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
{
// Call the SetUsageType method of the destination
// to add each available virtual input column as an input column.
wrp2.SetUsageType(
input.ID, vInput, vColumn.LineageID, DTSUsageType.UT_READONLY);
}
// Get the destination's default output collection
IDTSOutputCollection100 outColl = md2.OutputCollection;
// Iterate through the outputs in default output collection
foreach (IDTSOutput100 output in outColl)
{
// Iterate through the default output columns in the output
int count = output.OutputColumnCollection.Count;
foreach (IDTSOutputColumn100 outputColumn in output.OutputColumnCollection)
{
// Get the output's external metadata column collection
IDTSExternalMetadataColumnCollection100 extMetadataColumnColl = output.ExternalMetadataColumnCollection;
// Iterate through the external metadata column collection's external metadata columns
foreach (IDTSExternalMetadataColumn100 extMetadataColumn in extMetadataColumnColl)
{
// Call the MapOutPutColumn method of the destination to map
// each available output column to an external metadata column
wrp2.MapOutputColumn(
output.ID, outputColumn.ID, extMetadataColumn.ID, true);
}
}
}
md2.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(conn);
md2.RuntimeConnectionCollection[0].ConnectionManagerID = conn.ID;
conn.AcquireConnection(null);
#region Save Package to FileSystem
string packageXml = #"C:\Users\test.dtsx";
XmlDocument myPkgDocument = new XmlDocument();
p.SaveToXML(ref myPkgDocument, null, null);
a.SaveToXml(packageXml, p, null);
#endregion
}
}
}
I think the problem that you are not mapping the input columns to the OLEDB Destination, and after opening the package, if you click on the OLEDB Destination and go to the Mapping section, it will automatically map the columns based on their names. The Foreach loop that is used by others are to loop over columns and map them to the related Destination columns.
There are many articles talking about creating SSIS package dynamically, you can refer to them for more information:
Dynamic Data Flow in SSIS using .NET/C#
Programmatically map the columns of a flat file destination?
Building Packages Programmatically
Samples for creating SSIS packages programmatically
Generating SSIS Packages Programmatically (Part I)

How to use Boolean values as check box values in Excel from C#

I am trying to send boolean values from my WPF C# app to a Xcel file but the content is showing 'TRUE' and 'FALSE' as text instead of checkboxes.
C# Model:
public class ExcelModel
{
public string ID { get; set; }
public string Title { get; set; }
public bool IsHotel { get; set; }
}
C# Code:
var dataList = new List<ExcelModel>() { new ExcelModel{ID = "1", Title = "Test1", IsHotel = True} };
var workbook = new XLWorkbook(); //creates the workbook
var wsDetailedData = workbook.AddWorksheet("Test"); //creates the worksheet with sheetname 'data'
wsDetailedData.Cell(1, 1).InsertTable(dataList); //inserts the data to cell A1 including default column name
wsDetailedData.Columns().AdjustToContents();
// wsDetailedData.Column.
wsDetailedData.Rows().AdjustToContents();
workbook.SaveAs(MainWindow.appRoot + #"test.xlsx"); //saves the workbook
This isn't possible from the ClosedXML library, which you seem to be using.
It is possible if you use Microsoft.Office.Interop.Excel to create the excel. (But you would need office installed on the computer running your program).
See:
https://stackoverflow.com/a/8936775/994679
If that's not acceptable, it ought to be possible using OpenXml instead. But you'd really have your work cut out for you, and I can't find any simple examples of doing that. Here's an example of a guy adding a Combobox:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/4489302b-57a0-434f-b7ed-d8bb6479edf0/how-to-insert-a-combobox-in-a-spreadsheet-by-code-behind-c-using-open-xml-format-sdk?forum=oxmlsdk
You might be able to find out the code if you're using OpenXml by using the OpenXml Productivity tool. There you can create an excel with a checkbox, and use the Reflect Code button to generate the OpenXml code to use, though it will probably still be challenging to find the specific generated code you need to use, and understand how to adapt it properly.

C# Backing Up And Restoring Clipboard

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; }
}
}

Getting data from a PDF using C#

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.

Categories

Resources