I am integrating excel with an external service which involves receiving data from that service and showing in excel. I want to be able to store the information I get in the Excel.Range object. Is there any property of an Excel.Range object where one can store meta data?
To clarify, like in Outlook an Outlook.TaskItem has ItemProperties which is an Outlook.ItemProperty Object. So is there anything similar like that in Excel?
And if not, then what is the best way to store meta data for a Excel.Range?
EDIT:
I need to persist this meta data information. So if a user saves, closes and then re opens the workbook, I need to be able to extract this meta data from the Excel.Range object (or any other property)
Since you need the information to be persistent, I was using a simpler and clearer approach.
Create a new WorkSheet, call it something like [YourSheetName]Metadata (in case you have multiple of this kind). Set it to VeryHidden (xlSheetVeryHiddencan't be Unhidden from with Excel you have to unhide it from code):
xl.XlSheetVisibility.xlSheetVeryHidden
Save all your metadata for a Range R1 in metadata sheet in Range R1.
Your code will be very simple and clear in that way.
It may look something like:
Sheet1.Range[row,col].Value = SomeValue;
Sheet1Metadata.Range[row,col].Value = MetaDataOfSomeValue;
I am not sure if there is a such property even if, I was developing my own custom Range Metadata class. In that way, you have the flexibility to do whatever you need or may need later.
Following is a starting example of how you can do it:
class RangeListMetaData
{
private readonly List<RangeMetaData> _rangeList;
public RangeListMetaData()
{
_rangeList = new List<RangeMetaData>();
}
public void Add (RangeMetaData rangeMetaData)
{
_rangeList.Add(rangeMetaData);
}
}
class RangeMetaData
{
public RangeMetaData(xl.Range range)
{
this.Range = range;
}
public RangeMetaData(xl.Range range, object value) : this(range)
{
this.RangeValue = value;
}
public xl.Range Range { get; private set; }
public object RangeValue { get; set; }
}
class TestRangeMetaData
{
void Test()
{
var rangeListMetaData = new RangeListMetaData();
// storing part
RangeMetaData range = new RangeMetaData([Your excel Cells], [You Value]);
rangeListMetaData.Add(range);
// Retrieve Part
rangeListMetaData.FindByRange(...);
rangeListMetaData.FindByValue(...);
rangeListMetaData.FindBySomethingElse(...);
}
}
Since you are working with interops, you need to take care to release COM objects. For that, you can rely to another post, see my answer there
Related
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.
What I want to do is have the user add items to the list. Then when they add an item I need the list to save, so that when the user closes the app and opens it again, the list they've created is still there.
Right now, I can add items to my list, but as soon as i close the app they will be gone.
private static ObservableCollection<ViewModels.ZoneViewModel> Zones = new ObservableCollection<ViewModels.ZoneViewModel>();
public void PopulateListView(string image, string name, string address)
{
if (name != "" && address != "")
{
Zones.Add(new ViewModels.ZoneViewModel { Image = image, Name = name, Address = address });
Application.Current.Properties["zoneslist"] = Zones;
}
}
protected override void OnAppearing()
{
if (Application.Current.Properties.ContainsKey("zoneslist"))
{
// Put the contents of the "zoneslist" key into a variable as a string.
var savedZones = Application.Current.Properties["zoneslist"] as ObservableCollection<ViewModels.ZoneViewModel>;
// Set the listviews' itemssource to the savedzones list.
zonesList.ItemsSource = savedZones;
}
}
Here's the code I use right now, I thought this could work to save it but that doesn't work.
EDIT: So I've tried what #Alessandro Calario suggested and after using json serialization the listview just gives me a ton of empty list items(even though i only added one). But an item is added and is saved, even when the app is closed. Progress, at least, but I'm not quite there yet. Anyone know a solution?
my code:
public void PopulateListView(string image, string name, string address)
{
if (name != "" && address != "")
{
Zones.Add(new ViewModels.ZoneViewModel { Image = image, Name = name, Address = address });
//Serialize to json string
var json = JsonConvert.SerializeObject(Zones);
Application.Current.Properties["zoneslist"] = json;
}
}
protected override void OnAppearing()
{
if (Application.Current.Properties.ContainsKey("zoneslist"))
{
// Put the contents of the "zoneslist" key into a variable as a string.
var savedZones = Application.Current.Properties["zoneslist"] as string; //ObservableCollection<ViewModels.ZoneViewModel>
JsonConvert.DeserializeObject<ObservableCollection<ViewModels.ZoneViewModel>>(savedZones);
// Set the listviews' itemssource to the savedzones list.
zonesList.ItemsSource = savedZones;
}
}
I think you can Serialize your List of Objects to a json String and save it to Application Properties
If using 3rd parties libraries is not a thing for your project I highly recommend you to use Akavache. This is an Async, persistent key-value store.
Once setup is very simple to use.
//To Insert your object
IObservable<Unit> InsertObject<T>(string key, T value, DateTimeOffset? absoluteExpiration = null);
//To Get your object
IObservable<T> GetObject<T>(string key);
where T can be your whole list.
Of course it's a little more than this but trust me just a little. Read the full documentation and hope it fits your needs.
The Application Properties only stores primitive types.
Note: the Properties dictionary can only serialize primitive types for
storage. Attempting to store other types (such as List can
fail silently).
Source: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/application-class/
Either set it up so you are using the properties as a primitive storage, or go for another local storage mechanism such as Sqlite (a good guide here: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/databases/)
Good evening; I have an application that has a drop down list; This drop down list is meant to be a list of commonly visited websites which can be altered by the user.
My question is how can I store these values in such a manor that would allow the users to change it.
Example; I as the user, decide i want google to be my first website, and youtube to be my second.
I have considered making a "settings" file however is it practical to put 20+ websites into a settings file and then load them at startup? Or a local database, but this may be overkill for the simple need.
Please point me in the right direction.
Given you have already excluded database (probably for right reasons.. as it may be over kill for a small app), I'd recommend writing the data to a local file.. but not plain text..
But preferably serialized either as XML or JSON.
This approach has at least two benefits -
More complex data can be stored in future.. example - while order can be implicit, it can be made explicit.. or additional data like last time the url was used etc..
Structured data is easier to validate against random corruption.. If it was a plain text file.. It will be much harder to ensure its integrity.
The best would be to use the power of Serializer and Deserializer in c#, which will let you work with the file in an Object Oriented. At the same time you don't need to worry about storing into files etc... etc...
Here is the sample code I quickly wrote for you.
using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
public class UrlSerializer
{
private static void Write(string filename)
{
URLCollection urls = new URLCollection();
urls.Add(new Url { Address = "http://www.google.com", Order = 1 });
urls.Add(new Url { Address = "http://www.yahoo.com", Order = 2 });
XmlSerializer x = new XmlSerializer(typeof(URLCollection));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, urls);
}
private static URLCollection Read(string filename)
{
var x = new XmlSerializer(typeof(URLCollection));
TextReader reader = new StreamReader(filename);
var urls = (URLCollection)x.Deserialize(reader);
return urls;
}
}
public class URLCollection : ICollection
{
public string CollectionName;
private ArrayList _urls = new ArrayList();
public Url this[int index]
{
get { return (Url)_urls[index]; }
}
public void CopyTo(Array a, int index)
{
_urls.CopyTo(a, index);
}
public int Count
{
get { return _urls.Count; }
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return _urls.GetEnumerator();
}
public void Add(Url url)
{
if (url == null) throw new ArgumentNullException("url");
_urls.Add(url);
}
}
}
You clearly need some sort of persistence, for which there are a few options:
Local database
- As you have noted, total overkill. You are just storing a list, not relational data
Simple text file
- Pretty easy, but maybe not the most "professional" way. Using XML serialization to this file would allow for complex data types.
Settings file
- Are these preferences really settings? If they are, then this makes sense.
The Registry - This is great for settings you don't want your users to ever manually mess with. Probably not the best option for a significant amount of data though
I would go with number 2. It doesn't sound like you need any fancy encoding or security, so just store everything in a text file. *.ini files tend to meet this description, but you can use any extension you want. A settings file doesn't seem like the right place for this scenario.
I'm coming from a SQL Server background, and experimenting with Redis in .NET using ServiceStack. I don't mean for Redis to be a full replacement for SQL Server, but I just wanted to get a basic idea of how to use it so I could see where we might make good use of it.
I'm struggling with what I think is a pretty basic issue. We have a list of items that are maintained in a couple of different data stores. For the sake of simplicity, assume the definition of the item is basic: an integer id and a string name. I'm trying to do the following:
Store an item
Retrieve an item if we only know its id
Overwrite an existing item if we only know its id
Show all the items for that specific type
And here's some of the code I've put together:
public class DocumentRepositoryRedis
{
private static string DOCUMENT_ID_KEY_BASE = "document::id::";
public IQueryable<Document> GetAllDocuments()
{
IEnumerable<Document> documentsFromRedis;
using (var documents = new RedisClient("localhost").As<Document>())
{
documentsFromRedis = documents.GetAll();
}
return documentsFromRedis.AsQueryable();
}
public Document GetDocument(int id)
{
Document document = null;
using (var redisDocuments = new RedisClient("localhost").As<Document>())
{
var documentKey = GetKeyByID(document.ID);
if (documentKey != null)
document = redisDocuments.GetValue(documentKey);
}
return document;
}
public void SaveDocument(Document document)
{
using (var redisDocuments = new RedisClient("localhost").As<Document>())
{
var documentKey = GetKeyByID(document.ID);
redisDocuments.SetEntry(documentKey, document);
}
}
private string GetKeyByID(int id)
{
return DOCUMENT_ID_KEY_BASE + id.ToString();
}
}
It all seems to work - except for GetAllDocuments. That's returning 0 documents, regardless of how many documents I have stored. What am I doing wrong?
The typed Redis client also gives you access to the non-typed methods - since Redis ultimately doesn't know or care about your object types. So when you use the client.SetEntry() method, it bypasses some of the typed client's features and just stores the object by a key. You'll want to use the client.Store method since it goes ahead and creates a SET in Redis with all the object IDs related to your type. This SET is important because it's what the GetAll method relies on to serve back all the objects to you. The client.Store method does infer the ID automatically so you'll want to play around with it.
You'd change your GetDocument(int id) and SaveDocument(Document document) methods to use the client.GetById(string id) method, and you'd use client.Store(T value) method. You won't need your GetKeyByID() method anymore. I believe your Document object will need an "Id" property for the typed client to infer your object ID.
I have a class that I am using below. And I am using this class for my windows application. However, when I call the method from my application ReadInConfig() it successfully reads and fills the datatable, and assigns the _sip_ip address.
In my windows application I have the following. However, it doesn't give me the sip_ip that it has been assigned.
ConfigSIP readIn = new ConfigSIP();
readIn.ReadInConfig();
string sip_ip = readIn.sip_ip(); // Get nothing here.
I am thinking as the _sip_ip that has been assigned by the data table is a different object than doing this readIn.sip_ip();
Is there any way I can solve this problem?
Many thanks,
public class ConfigSIP
{
private string _sip_ip;
// Fill the data table and assign the sip ip.
public void ReadInConfig()
{
DataTable dt = new DataTable("Admin");
dt.ReadXmlSchema(#"C:\Config.xml");
dt.ReadXml(#"C:\Config.xml");
_sip_ip = dt.Rows[0]["Sip_ip"].ToString();
}
// Return the sip ip address.
public string sip_ip()
{
return _sip_ip;
}
}
You forgot to call ReadInConfig:
ConfigSIP readIn = new ConfigSIP();
readIn.ReadInConfig();
string sip_ip = readIn.sip_ip();
If your code is copied verbatim your client code isn't calling the ReadInConfig() method. So the string will never get populated.