I've been working on an ExcelDNA/C# add in for a while, and I'm at the final hurdle.
I can get a selection address, and I first of all need to check if the rows in that selection are simply two, e.g. in Excel they would be 8+9, two rows next to each other or any two consecutive numbers.
I then need to check that there are more than two columns, etc C to J (more than two spaces in the alphabet).
This all needs to be done from a string like this: Sheet1!$C$8:$J$9
What I am trying to do, is split a selection like this, which returns the above string, into two strings, in the case of the example, the desired end result would be
Sheet1!$C$8:$J$8 + Sheet1!$C$9:$J$9 in two different strings, perhaps I need more coffee, but if anyone has a less trashy way of doing this than I plan, I would be forever in your debt!
Do you have a reference to the Range object? If so, range.Rows.Count == 2 will tell you if there are two rows, and range.Columns.Count > 2 will tell you if there are more than two columns.
Then, to get the addresses of the two rows independently you can do something like:-
var address1 = range.Rows[1].Address(external:true);
var address2 = range.Rows[2].Address(external:true);
I presume this is running from a macro called from a CommandBar or Ribbon event handler.
On that case you might as well use the COM Automation interface from the start, and never bother the the C API (XlCall and ExcelReference).
To get the current selection you'd just say
Application xlApp = (Application)ExcelDnaUtil.Application;
Range selectedRange = xlApp.Selection as Range;
if (selectedRange != null)
{ .... do you further checking here ...}
The C API stuff is a bit more important if you are making user-defined functions or you want high-performance data transfers. But for regular macros the COM API is easier.
Related
Ive been struggling to figure out what the correct way to do this is.
I have a table in Excel that relates Motor horsepower size to the amount of space it takes up.
I would like to implement a lookup function of this information in a C# Windows Form that I am writing.
I want to create a function that has a single argument passed to it (double HP). It would lookup if the motor HP falls in-between "Low" and "High" and then it would return the value in the "Space Factor" column. I feel pretty silly for not knowing the best way to do this. I want to avoid IF/Else statements as it doesnt seem like the right approach. Additionally, interfacing with Excel to do the lookup also seems a little silly.
How would you interface with a data table like this?
You should create an class to represent your rows, you can then add each object to a list. If you have few items you can just use Linq:
var hp = 20;
var objects = new List<MyClass>(){...};
var foundObj = objects.FirstOrDefault(o => hp > o.HpLow && hp < o.HpHigh);
if(foundObj != null){
// Found object!
}
If you have lots of rows you can instead sort the list by the HpHigh, and use BinarySearch to find the item without iterating over all items, but it requires a custom comparer and is a bit more complex.
If hp-ranges can overlap for you need some way to determine which of the rows is the most fitting one, so I'm just going to assume ranges do not overlap.
I would personally do this with c# arrays, however it might not be best or most practical solution for your solution, which im not quite sure how it works, however array is quick and easy to setup.
For starters I'm very very new to writing code! :)
What I have so far...
So far I've used Xamarin.Forms to create a user interface for a sort of specialized calculator. I'm using a Grid Layout containing: a first column of Labels, a second column of Entries (that I have named in Xaml), and a third column of Steppers (so I can change the entries by typing or using the stepper). These 3 views on each row repeat for several rows with different label text on each row and at the bottom of the Grid Layout I have an Entry for the output.
The problem...
Basically, I want to buy a certain product at different weights and prices...among other criteria....and I want to quickly figure out how much money I'll make at a future possible sale price. Simply put... I'm trying to add/subtract/multiply/divide using Xamarin.Forms Entries. I've looked everywhere and can't seem find anyone giving an example of how to do this. I've tried different methods and usually end with an error of not being able to convert the Xamarin.Forms entry to a string...So I'm back to zero. Can I get an example of a Method where I would be able to add/subtract/multiply/divide 2 Xamarin.Forms Entry views together in the C# code behind? This seems very simple to me...what am I missing??? Is there a thread/article/video somewhere that I haven't found that covers this?? And like I said, I'm very new so the answer is probably very simple.
Thanks in advance!
Steven
Entries deal with strings, not numeric values, so you need to convert them before doing calculations.
var amount = Decimal.Parse(EntryA.Text);
var price = Decimal.Parse(EntryB.Text);
var total = amount * price;
// you can use a format string as an argument to ToString()
// to control the output - ie, how many decimals, commas, etc
LabelTotal.Text = total.ToString();
In a real app you will want to validate the input in case the user enters text instead of a value number (the Parse method will throw an exception if the input is bad);
I need a place in my code where variables auto-update when other variables change that they are dependent on.
The difference from spreadsheets is I don't want all the parsing stuff, I just want everything done in code as I don't need to update formulas/variables after the program has been compiled.
Basically there is input data, and a few "cells" from the spreadsheet part will listen in on those values, change, and the change will propagate through the "spreadsheet" part. Like a normal spreadsheet.
So I can then immediately after depend on all those values, instead of having to write all kinds of code to update all the variables correctly first, which would be very hard to do.
If anyone can help me with the thought process to implement this it would really help! I've been reading some spreadsheet source code but it will take a long time to understand that, and then understand how I would change those ideas to fit what I need to do.
Edit:
Right now I just have things in loops and structure everything correctly so it updates correctly like so:
A1 = 2;
B1 = A1 + 2;
Then this just loops again and again. But if I wanted things to update automatically, how would I store the calculations attached to the variables? So when updating B it would call A1 + 2 ?
By the way A1, and B1 are just random variables. I don't name things by cell names and there is no such structure.
One way of storing calculations is to use the elements of functional programming that have been built into C#.
A formula can be stored as a function, using one of the Func<T1,T2,...TResult> delegates.
var CalculationForX = new Func<double, double, double>(
(arg1, arg2) => arg1 * arg2
);
Here, CalculationForX specifies a function that takes two doubles, and returns a the product of the two doubles. Then using for example CalculationForX(5, 2) returns 10.
So, a straightforward way to keep track of all the dependencies would be to store an object for each dependent variable, containing its formula and inputs. These could be in the form of:
an array of strings that contain the property names of a given object
a PropertyInfo array
an Action<T1,T2> delegate represents the actual property setter
Whenever you receive a property changed event, retrieve the calculation and the inputs for all variables that depend on it, and update the target values.
(It's funny to me that you asked this question, as I asked a quite similar one yesterday on Programmers.)
I'm using code lines similar to the one below several times throughout the loop. It works all fine EXCEPT it doesn't follow the "no two dot rule", right?
wksheet.Cells(cell.Row, "J").Value
I can store cell.Row as an int, but where to go from there? Is there a function that lets me pass row number and column letter and get the value of that particular cell while still following the rule?
It would be a pain to declare and then set the range variable every time I want to get a particular cell's value inside the loop.
How do I properly clean up Excel interop objects?
^this link explains no two dot rule.
I guess you could break it down
var row = cell.Row;
var cell = wksheet.Cells(row, "J");
var value = cell.Value;
This is the scenario. You've got a web form and you want to prompt the customer to select their birth year.
a) hard code the values in the dropdown list?
b) Grab valid years from a DB table
I can see a maintenance nightmare with copying a set of years hard coded in .aspx files everywhere.
updated:
for loop is not ideal (maintenance nightmare and error prone). The user then has to sift through 120 years that haven't even got here yet.
I still like the DB approach:
* Single point of data
* No duplication of code
* Update the table as needed to add more years
* Year table values could be used for some other dropdown for some other purpose entirely for something other than Birth year
Simple as that. No need to go updating code everywhere. I feel for data that is universal like this, we shouldn't be hard coding this shiza into a bunch of pages which is totally going against reuse and error prone...really it's not pratical. I'd take the hit to the DB for this.
Updated (again...after thinking about this):
Here's my idea. Just create a utility or helper method called GetYears that runs that loop and returns a List<int> back and I can bind that to whatever I want (dropdownlist, etc.). And I like the web.config idea of maintaining that end year.
C) Use a for-loop to generate the years in a range of your choice.
Something as simple as this pseudocode:
for (int i = 1900 ; i < THIS_YEAR - 13 ; i++)
{
validyears.options.Add(i);
}
Neither - provide a centralized service which can decide which mechanism to use, then the application doesn't care, and you are free to choose hardcoding, sliding window or database mechanisms.
To expand, typically, I would do something like this:
Define IPopulatableYear interface which has a single AddYear method taking an int and constructing an appropriate ListItem or whatever.
Make MyYearListBox inherit from regular ListBox implement IPopulatableYear (this works for winForms or WebForms)
Create static method or singleton or method in your DAL or whatever.
Like this:
PopulateYears(IPopulatableYear pl) {
// Very simple implementation - change at will
for (int lp = 2009 ; lp < 2009 + 10 ; lp++) {
pl.Add(lp);
}
}
or
PopulateYears(IPopulatableYear pl) {
// A DB implementation
SQLDataReader dr = DAL.YearSet() ; // Your choice of mechanism here
while ( dr.Read() ) {
pl.Add(dr[YEAR]);
}
}
or
PopulateYears(IPopulatableYear pl) {
// A DB limits implementation with different ranges defined in database by key - key determined by control itself - IPopulatableYear needs to implement a .YearSetKey property
SQLDataReader dr = DAL.YearLimits(pl.YearSetKey) ; // Your choice of mechanism here
for ( int lp = dr[YEAR_MIN] ; lp <= dr[YEAR_MAX] ; lp++ ) {
pl.Add(lp);
}
}
The mechanism is now centrally managed.
Use MyYearListBox on your forms and call PopulateYears() on it. If your forms are smart, they can detect all MyYearListBox instances and call it, so you no longer have any new code - just drag it on.
Take a look at Enumerable.Range. I think making DB calls is FAR less performant than Enumerable.Range.
E) Use a text input box, because that will always work.
(Be sure to validate it, of course, as a number. Include "Y2K" and "The year World War II started" in a dictionary of years, of course.)
How you present the year selection in the web form is irrelevant. It's an interface decision. Your server should not trust the data coming in, and should validate it accordingly. It's trivial to emulate a form submission, so it doesn't matter how it's presented. Heck, you can generate the drop down with javascript so there is no load on the server.
You can validate with a rule on the backend, rather than a lookup.
Since you're raising this whole issue (and making a bunch of comments), maybe it's within your power to think long and hard this.
For the end user, it's hard to beat the ease-of-use of a text box. Yup, you're going to get bogus data, but computers are supposed to make things easier, not harder. Scrolling through a long list of years to find the year I know I was born is a nuisance. Especially with all those young whippersnappers and old farts who want to enter birth years that aren't anywhere close to mine!
But stepping back even further...do you really need to ask the user their birth year in the first place? Is it that important to your application? Could you avoid the issue entirely by letting somebody else deal with that? Say by using OpenID, Windows Live ID or Facebook Connect?