|
If you only need to support the new format files (*.xlsx ), then either EPPlus[^] or ClosedXML[^] should allow you to read the file.
If you need to support the older format (*.xls ) as well, then you can use NPOI[^].
If you're writing a web application, avoid using Office Interop; it's not supported in non-interactive programs[^], and will fail in interesting ways.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Further to the answer above, you can always save as csv or xml format and parse it with linq to XML.
OR even better, use ODBC to connect to any excel format and query it with SQL.
|
|
|
|
|
|
I have been trying to change the Category field of a property displayed in a PropertyGrid in runtime.
I know how to change DisplayName and Browseable in runtime and I thought it would be the same way. Just modifying the code. But nope...
The class I want to display in the PropertyGrid is this:
public class Person
{
int age = 10;
[DisplayName("Age")]
[Category("Fact")]
public int Age
{
get { return age; }
}
}
I have a Windows form application containing a PropertyGrid (propertyGrid1) and a button (button1) to switch Person.Age's category between "Fact" and "Info".
public Form1()
{
InitializeComponent();
propertyGrid1.SelectedObject = new Person();
}
private void button1_Click(object sender, EventArgs e)
{
SetCategory();
SetDisplayName();
}
To switch the DisplayName I have this method:
private void SetDisplayName()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person.GetType())["Age"];
DisplayNameAttribute nameAttribute = (DisplayNameAttribute)descriptor.Attributes[typeof(DisplayNameAttribute)];
FieldInfo displayName = nameAttribute.GetType().GetField("_displayName", BindingFlags.NonPublic | BindingFlags.Instance);
string name = displayName.GetValue(nameAttribute).ToString() == "Age" ? "The age" : "Age";
displayName.SetValue(nameAttribute, name);
propertyGrid1.SelectedObject = person;
}
This works well.
I "copied" the SetDisplayName method and made a SetCategory method like this:
private void SetCategory()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person.GetType())["Age"];
CategoryAttribute categoryAttribute = (CategoryAttribute)descriptor.Attributes[typeof(CategoryAttribute)];
FieldInfo category = categoryAttribute.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
string text = category.GetValue(categoryAttribute).ToString() == "Fact" ? "Info" : "Fact";
category.SetValue(categoryAttribute, text);
propertyGrid1.SelectedObject = person;
}
This works all well. When the category.SetValue() has been executed the intelisence of categoryAttribute show that the Category property is "Info" and not "Fact" as previous. Just as I want it.
When the method is finished the category of Person.Age has not been changed and the propertyGrid1 still show "Fact" as category.
I have tried to make any sense of this article which show that it's possible: Dynamic Properties for PropertyGrid[^]
But that example is to big to scope and I get confused.
Can anyone help me?
/Steffe
|
|
|
|
|
At run-time, all attributes are read-only: the can be changed during design-time only.
|
|
|
|
|
Absolutely not!
I can change DisplayName and Browseable attributes in runtime. Also the example article I linked to change many attributes during runtime, even the category which I have problem with.
/Steffe
|
|
|
|
|
Mc_Topaz wrote: This works well. No, it doesn't. It's a dirty hack, and not guaranteed to work. Attributes are meta-data on types, not their instances. The reason for the "Mandatory" comment in the dirty hack is that the propertygrid doesn't expect the category to change, since the definition of the type cannot change.
Mc_Topaz wrote: I have tried to make any sense of this article which show that it's possible Something being possible don't mean it's also a good idea.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
I working with a regular Windows Forms application and I would like to read/store user settings from/to an .ini-file. It is very easy to find numerous examples of this through a simple google search, but I can't find one that meets the following requirements:
• Not XML-based.
• The .ini-file should be fairly readable, i.e. contain plain text.
• Arrays of strings and double should be supported. Or even better, any array, struct or class should be supported (but I don't know if that's realistically possible since I want plain text and no serialization).
• Each field in the .ini-file should be easy to read/modify through simple get/set methods. And since the .ini-file is plain text, it is also possible to edit individual settings directly using, for example, Notepad.
Has anyone stumbled across something that meets those requirements?
|
|
|
|
|
an ini file, too my knowledge is never XML. See here[^] If you really want to use ini as standard, make sure to follow it.
From there it is basically just writing a serializer/deserializer to read in or write to the ini file. I would just quickly write on myself as this is not that difficult.
For arrays you can assign a delimeted value to a key and then split on the delimiter.
eg.:
mydoublearray=0.1,0.56,3.141592,54.7
mystringarray="blabla1","blabla2","andsoonandsoforth"
(in this case the "," is the delimeter, but it can be any character or combination of characters. the double pipe "||" is often used as it is rarely or never used in normal strings.)
hope this helps.
|
|
|
|
|
|
How would you want to have that byte[] in a text-file? If you add two structs of the same type, how would it differentiate between those two? Based on the section?
There's always the option of interopping to GetPrivateProfileString, the API that was used to read/write ini-files. The API got deprecated in favor of XML-ini files.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
For an INI file? No and probably not going to find it considering how configuration files are written now-a-days.
For XML files? This is easy to do and you really don't have to write that much code to do it.
|
|
|
|
|
Thanks for your comments. When I read the replies here I get the impression that .ini-files are standardized (by IEEE or something similiar). I didn't know that. What I need is a configuration file (which I intend to give the extension .ini, regardless of whether it meets certain standards or not) that fulfills the requirements in my bulleted list.
|
|
|
|
|
Please don´t. If you call a file *.ini, make sure it follows standards. Same for any other file: bmp, csv, ... whatever.
If anyone else needs to maintain your application, at least he has something to go on.
Nothing prevents you from creating your own standard with a new extension (or an extension without defined standards.)
|
|
|
|
|
|
Think that should be ok. You can have more then 3 letters
Look up "wiki cfg" in google to check...
|
|
|
|
|
Call it .CFG. If you use the .INI extension people will assume it follows a standard format.
There is no such expectation on the format of a .CFG file, only that it holds a configuration of some kind.
|
|
|
|
|
I have a form application with a button and a listbox. When I click the button, I launch about 10 tasks and then do a wait on each of the tasks. When I try to run this from Visual Studio 2013 in Debug mode, I get an exception "One or more errors occurred" at Task.Start() itself, but when I run this outside of the debugger, then it works fine. Is this a Visual Studio issue? Any light on this would be helpful. Please find the code snippet below:
private void SynchronousWait200ms()
{
Thread.Sleep(200);
listBox.Items.Add("Wait 200 ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
private void TaskRunButton_Click1(object sender, EventArgs e)
{
listBox.Items.Clear();
try
{
List<Task> tsk1 = new List<Task>();
for (int i = 0; i < 10; i++)
{
tsk1.Add(new Task(SynchronousWait200ms));
tsk1[i].Start();
tsk1[i].Wait();
}
listBox.Items.Add("------------");
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
|
|
|
|
|
No, you still get the problem, just you don't see it because the thread that fails isn't the main thread.
You can't update UI controls from a non UI thread - you are getting a cross threading exception. Use Invoke to move teh listBox update back onto the UI thread and it should be fine.
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
OriginalGriff wrote: Use Invoke to move teh listBox update back onto the UI thread and it should be fine.
Or it would be fine, if he wasn't blocking the UI thread waiting for the background thread to complete!
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Good point - since the Invoke call waits for the delegate to return a value, you should get a deadlock.
Those who fail to learn history are doomed to repeat it. --- George Santayana (December 16, 1863 – September 26, 1952)
Those who fail to clear history are doomed to explain it. --- OriginalGriff (February 24, 1959 – ∞)
|
|
|
|
|
Thanks to both of you for your valuable help. Changed it as shown below and got it working without loosing sequence and with corresponding thread id...
private void TaskRunButton_Click(object sender, EventArgs e)
{
listBox.Items.Clear();
Task demoTask = new Task(DemoTask);
demoTask.Start();
}
private void DemoTask()
{
try
{
List<Task> tsk1 = new List<Task>();
List<Task> tsk2 = new List<Task>();
for (int i = 0; i < 10; i++)
{
tsk1.Add(new Task(TaskDemoWait100ms));
tsk2.Add(new Task(TaskDemoWaitWait200ms));
}
for (int i = 0; i < 10; i++)
{
tsk1[i].Start();
tsk2[i].Start();
tsk2[i].Wait();
listBox.BeginInvoke(new separatorDelegate(() =>
{ listBox.Items.Add("------------"); }));
}
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
delegate void taskdelegate(int threadId);
delegate void separatorDelegate();
private void TaskDemoWait100ms()
{
Thread.Sleep(100);
object[] param = new object[1];
param[0] = (object)Thread.CurrentThread.ManagedThreadId;
listBox.BeginInvoke(new taskdelegate((int threadId) =>
{ listBox.Items.Add("Wait 100 ms, Thread Id = " + threadId); }), param);
}
private void TaskDemoWaitWait200ms()
{
Thread.Sleep(200);
object[] param = new object[1];
param[0] = (object)Thread.CurrentThread.ManagedThreadId;
listBox.BeginInvoke(new taskdelegate((int threadId) =>
{ listBox.Items.Add("Wait 200 ms, Thread Id = " + threadId); }), param);
}
|
|
|
|
|
I don't see any failing thread, because I am getting the values in the list box correctly, but as you mentioned, it may fail at any time.
Thanks very much for your valuable inputs. I will work on this and update further...
|
|
|
|
|
Why are you starting a new task and then immediately waiting for it? You're blocking the UI thread waiting for a background thread to complete, so you might as well just do the work on the UI thread.
I suspect the reason you're getting an exception is because you're updating a UI control from a non-UI thread. You will need to use the Invoke method to ensure that the updates only happen on the UI thread.
Unfortunately, since you've chosen to block the UI thread waiting for the background thread to complete, this won't work - the UI thread is blocked waiting for the background thread to complete, and the background thread is blocked waiting for the UI thread to process the Invoke message.
You might be able to work around the problem by using BeginInvoke instead, which won't block the background thread. However, this will change the behaviour of your code, as the list items will not be added in the same sequence.
If you're using .NET 4.5, try using async / await instead:
private async Task SynchronousWait200ms()
{
await Task.Delay(200);
listBox.Items.Add("Wait 200 ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
private async void TaskRunButton_Click1(object sender, EventArgs e)
{
listBox.Items.Clear();
for (int i = 0; i < 10; i++)
{
await SynchronousWait200ms();
}
listBox.Items.Add("------------");
}
That should work, but you'll get the same thread ID for each item, as the code will be marshalled back to the UI thread.
Perhaps if you explain what you're trying to achieve, we might be able to help.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
modified 19-Jun-14 10:19am.
|
|
|
|
|
Thanks very much for your thoughts.
Yes, Invoke also blocks as you mentioned. BeginInvoke solves the issue, but sequence goes wrong.
Regarding async / await, I am aware of it. In the project from where I took the code and posted here, it has async / await sample also. I am working on a sample for async / await. In that same sample I wanted to add Task.Start() as an example to demonstrate the use of Task.Wait() via another button on the UI. That's the reason I am doing wait immediately after Task.Start()
|
|
|
|