Excel Interop - Hiding Categories in Chart - c#

I'm looking for a way to hide/disable certain categories on a chart. (The Y Axis)
How this works with the filter:
Does anyone know how i can accomplish this in .NET With Excel Interop?

I hope I'm not too late to the party because this question is already a month old by now, but better late than never, I guess.
If you know the index of the category, you can use this code:
// Replace ActiveChart with your specific chart.
// The index of the ChartGroup is always 1,
// unless you have multiple chart groups in your chart.
var chartGroup = (ChartGroup)_application.ActiveChart.ChartGroups(1);
var category = (ChartCategory)chartGroup.FullCategoryCollection(Index: 2);
category.IsFiltered = true;
If you only know the name, you need to iterate over the categories:
// If you want to skip the already hidden categories,
// you can use .CategoryCollection() instead of .FullCategoryCollection() two times.
var categories = (CategoryCollection)chartGroup.FullCategoryCollection();
for (int i = 1; i <= categories.Count; i++)
{
var category = (ChartCategory)chartGroup.FullCategoryCollection(i);
if (category.Name == "W2 - 13/01/17")
category.IsFiltered = true;
}

Related

c# FlaUi get all the Values from DataGridView of another program

I'm trying to pull all the values from another program's DataGridBox. For that I'm using FlaUi. I made a code that does what I want. However, it is very slow. Is there a faster way to pull up all the values from another program's DataGridView using FlaUi?
my code:
var desktop = automation.GetDesktop();
var window = desktop.FindFirstDescendant(cf => cf.ByName("History: NEWLIFE")).AsWindow();
var table = window.FindFirstDescendant(cf => cf.ByName("DataGridView")).AsDataGridView();
int rowscount = (table.FindAllChildren(cf => cf.ByProcessId(30572)).Length) - 2;
// Remove the last row if we have the "add" row
for (int i = 0; i < rowscount; i++)
{
string string1 = "Row " + i;
string string2 = "Symbol Row " + i;
var RowX = table.FindFirstDescendant(cf => cf.ByName(string1));
var SymbolRowX = RowX.FindFirstDescendant(cf => cf.ByName(string2));
SCAN.Add("" + SymbolRowX.Patterns.LegacyIAccessible.Pattern.Value);
}
var message = string.Join(Environment.NewLine, SCAN);
MessageBox.Show(message);
Thank you in-advance
Searching for descendants is pretty slow as it will go thru all objects in the tree until it finds the desired control (or there are no controls left). It might be much faster to use the grid pattern to find the desired cells or get all rows at once and loop thru them.
Alternatively you could try caching as UIA uses inter process calls which are generally slow. So each Find method or value property does such a call. If you have a large grid, that can sum up pretty badly. For that exact case, using UIA Caching could make sense.
For that, you would get everything you need (all descendants of the table and the LegacyIAccessible pattern) in one go inside a cache request and then loop thru those elements in the code with CachedChildren and such.
A simple example for this can be found at the FlaUI wiki at https://github.com/FlaUI/FlaUI/wiki/Caching:
var grid = <FindGrid>.AsGrid();
var cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Descendants;
cacheRequest.Add(Automation.PropertyLibrary.Element.Name);
using (cacheRequest.Activate())
{
var rows = _grid.Rows;
foreach (var row in rows)
{
foreach (var cell in row.CachedChildren)
{
Console.WriteLine(cell.Name);
}
}
}

Create chart series, add data and set visibility

I'm trying to create a group of chart series, add data to the series and then show a few of the series by making them visible. The data changes and a user can select which series to view. I think this method will be better than clearing all series with chart.Series.Clear(); and then recreating the series in the same method.
For example a list of cars in a carpool with random mileages and then select which cars to show.
The code below doesn't work (I've commented where). The series aren't public and I think they need to be added to a public collection like a SeriesCollection but I'm not sure how.
Thanks for any help.
// create new chart series and add to a chartarea
ChartArea TestChartArea = new ChartArea();
public void CreateChartSeries()
{
List<string> lstCars = new List<string> { "Mazda", "Tesla", "Honda", "Jaguar", "Ford", "Toyota" };
foreach (string Car in lstCars)
{
// car series created correctly?
var Srs = new Series(Car);
Srs.ChartArea = TestChart.Name;
Srs.YAxisType = AxisType.Primary;
Srs.Color = Color.Red;
Srs.ChartType = SeriesChartType.Line;
TestChart.Series.Add(Srs);
}
}
// add data to chart series
public void SeriesData()
{
List<string> lstCars = new List<string> { "Mazda", "Tesla", "Honda", "Jaguar", "Ford", "Toyota" };
int[] Xseries = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] Milage = new int[10];
Random Random = new Random();
foreach (string Car in lstCars)
{
for (int M = 0; M < 10; M++)
Milage[M] = Random.Next(150, 15000);
// not sure how to call and add data to each series
Srs.Points.DataBindXY(Xseries, Milage);
}
}
// plot series - some visible
public void PlotCreatedSeries()
{
// not sure how to refer to each series
Mazda.Enabled = true;
Tesla.Enabled = false;
Honda.Enabled = true;
Jaguar.Enabled = false;
Ford.Enabled = true;
Toyota.Enabled = false;
}
The name 'Srs' you use to create the Series is only in scope i.e. usable within the loop. At the end of the loop you do add the newly created Series to your Chart:
TestChart.Series.Add(Srs);
The Series property is a public SeriesCollection. This is a bit confusing, as the singular type name and the plural property name are the same in this case, as oppose to, say, Legend(s) or ChartArea(s)..
From now on you can access it either by its index..
Series s = TestChart.Series[0] // the series you have added first
..or, more readable and more stable, by its Name property:
Series s = TestChart.Series["Mazda"] // the same series
TestChart.Series["Mazda"].Enabled = true;
Note that 'name' is also a tricky word:
When you declare a variable you give it a 'name'. Series s = new Series();
But many objects also have a property called Name: s.Name = "Volvo";
The former must be unique but the latter is just a string; do keep it unique as well, but the system will not guard you.
The former can never change, but, as you have seen, can go out of scope; the latter is just a string and you can change it.
Note that the variable itself doesn't go out of scop as long as it is still referenced somewhere, here as an element of the SeriesiesCollection Series..
Whether you want to add DataPoints bound or directly is up to you.
For the former there are many binding options.
For the latter you can use the Chart.Points methods..:
Add(DataPoint)
AddY(YValue)
AddXY(XValues, YValue(s))
Note that sometimes, especially with live charts it makes sense to insert a DataPoint using one of the InsertXXX methods!
Do look it up in MSDN! The middle version makes only sense if the x-values are either non-numeric or have to no real meaning, like, say, names.. - Note that adding meaningful x-values as numbers (or DateTimes) is crucial to use them for further goals, like tooltips, zoom or display ranges etc..
Failing to do so is probably the most common mistake newbies make. The Chart looks ok, but the data inside are broken, read lost.

C# Nested Tables in Word

I'm trying to design a table that has 3 additional tables in the last cell. Like this.
I've managed to get the first nested table into row 4, but my second nested table is going into cell(1,1) of the first table.
var wordApplication = new Word.Application();
wordApplication.Visible = true;
var wordDocument = wordApplication.Documents.Add();
var docRange = wordDocument.Range();
docRange.Tables.Add(docRange, 4, 1);
var mainTable = wordDocument.Tables[1];
mainTable.set_Style("Table Grid");
mainTable.Borders.Enable = 0;
mainTable.PreferredWidthType = Word.WdPreferredWidthType.wdPreferredWidthPercent;
mainTable.PreferredWidth = 100;
docRange.Collapse(Word.WdCollapseDirection.wdCollapseStart);
var phoneRange = mainTable.Cell(4, 1).Range;
phoneRange.Collapse(Word.WdCollapseDirection.wdCollapseStart);
phoneRange.Tables.Add(phoneRange, 3, 2);
var phoneTable = mainTable.Cell(4, 1).Tables[1];
phoneTable.set_Style("Table Grid");
phoneTable.Borders.Enable = 0;
phoneTable.AutoFitBehavior(Word.WdAutoFitBehavior.wdAutoFitContent);
phoneTable.Rows.RelativeHorizontalPosition = Word.WdRelativeHorizontalPosition.wdRelativeHorizontalPositionMargin;
phoneRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
I've tried collapsing the range, adding in a paragraph then collapsing the range again. No luck. I found this post and many similar ones, but I must be missing something.
Thanks for your time.
It usually helps in situation like these to add a line in your code: phoneRange.Select(); and having code execution end with that. Take a look at where the Range actually is. Now you can test using the keyboard where the Range needs to be in order to insert the next table successfully.
Since you say phoneRange selects outside the third row, rather than working with phoneRange try setting a new Range object to phoneTable.Range then collapse it to its end-point.

take one value from multiple loops

I have a list of arrays, of which i want to take one value from each array and build up a JSON structure. Currently for every managedstrategy the currency is always the last value in the loop. How can i take the 1st, then 2nd value etc while looping the names?
List<managedstrategy> Records = new List<managedstrategy>();
int idcnt = 0;
foreach (var name in results[0])
{
managedstrategy ms = new managedstrategy();
ms.Id = idcnt++;
ms.Name = name.ToString();
foreach (var currency in results[1]) {
ms.Currency = currency.ToString();
}
Records.Add(ms);
}
var Items = new
{
total = results.Count(),
Records
};
return Json(Items, JsonRequestBehavior.AllowGet);
JSON structure is {Records:[{name: blah, currency: gbp}]}
Assuming that I understand the problem correctly, you may want to look into the Zip method provided by Linq. It's used to "zip" together two different lists, similar to how a zipper works.
A related question can be found here.
Currently, you are nesting the second loop in the first, resulting in it always returning the last currency, you have to put it all in one big for-loop for it to do what you want:
for (int i = 0; i < someNumber; i++)
{
// some code
ms.Name = results[0][i].ToString();
ms.Currency = results[1][i].ToString();
}

Why am I getting index out of bounds error from database

I know what index out of bounds is all about. When I debug I see why as well. basically what is happening is I do a filter on my database to look for records that are potential/pending. I then gather a array of those numbers send them off to another server to check to see if those numbers have been upgraded to a sale. If it has been upgraded to a sale the server responds back with the new Sales Order ID and my old Pending Sales Order ID (SourceID). I then do a for loop on that list to filter it down that specific SourceID and update the SourceID to be the Sales Order ID and change a couple of other values. Problem is is that when I use that filter on the very first one it throws a index out of bounds error. I check the results returned by the filter and it says 0. Which i find kind of strange because I took the sales order number from the list so it should be there. So i dont know what the deal is. Here is the code in question that throws the error. And it doesn't do it all the time. Like I just ran the code this morning and it didn't throw the error. But last night it did before I went home.
filter.RowFilter = string.Format("Stage = '{0}'", Potential.PotentialSale);
if (filter.Count > 0)
{
var Soids = new int[filter.Count];
Console.Write("Searching for Soids - (");
for (int i = 0; i < filter.Count; i++)
{
Console.Write(filter[i][1].ToString() + ",");
Soids[i] = (int)filter[i][1];
}
Console.WriteLine(")");
var pendingRecords = Server.GetSoldRecords(Soids);
var updateRecords = new NameValueCollection();
for (int i = 0; i < pendingRecords.Length; i++)
{
filter.RowFilter = "Soid = " + pendingRecords[i][1];
filter[0].Row["Soid"] = pendingRecords[i][0];
filter[0].Row["SourceId"] = pendingRecords[i][1];
filter[0].Row["Stage"] = Potential.ClosedWon;
var potentialXML = Potential.GetUpdatePotentialXML(filter[0].Row["Soid"].ToString(), filter[0].Row["Stage"].ToString());
updateRecords.Add(filter[0].Row["ZohoID"].ToString(), potentialXML);
}
if i'm counting right line 17 is the error where the error is thrown. pendingRecords is a object[][] array. pendingRecords[i] is the individual records. pendingRecords[i][0] is the new Sales OrderID (SOID) and pendingRecords[i][1] is the old SOID (now the SourceID)
Any help on this one? is it because i'm changing the SOID to the new SOID, and the filter auto updates itself? I just don't know
Well I ended up changing how it worked all together and it actually sorts it a bit nicer now. The code i am about to post has a bunch of hard coded numbers due to the structure of my table that is returned. Sorry about that. I have learned since then to not do that, but i am working on a different project now and will change that when I have to change the program. But here is the solution.
var potentials = Server.GetNewPotentials(); //loads all records from server
for (int i = 0; i < potentials.Length; i++)
{
var filter = AllPotentials.DefaultView;
var result1 = CheckSoidOrSource(potentials[i].Soid, true);
var result2 = CheckSoidOrSource(potentials[i].SourceID,false) ;
//This potential can't be found at all so let's add it to our table
if (result1+result2==0)
{
Logger.WriteLine("Found new record. Adding it to DataTable and sending it to Zoho");
AllPotentials.Add(potentials[i]);
filter.RowFilter = string.Format("Soid = '{0}'", potentials[i].SourceID);
var index = AllPotentials.Rows.IndexOf(filter[0].Row);
ZohoPoster posterInsert = new ZohoPoster(Zoho.Fields.Potentials, Zoho.Calls.insertRecords);
AllPotentials.Rows[index]["ZohoID"] = posterInsert.PostNewPotentialRecord(3, filter[0].Row);
}
//This potential is not found, but has a SourceId that matches a Soid of another record.
if (result1==0 && result2 == 1)
{
Logger.WriteLine("Found a record that needs to be updated on Zoho");
ZohoPoster posterUpdate = new ZohoPoster(Zoho.Fields.Potentials, Zoho.Calls.updateRecords);
filter.RowFilter = string.Format("Soid = '{0}'", potentials[i].SourceID);
var index = AllPotentials.Rows.IndexOf(filter[0].Row);
AllPotentials.Rows[index]["Soid"] = potentials[i].Soid;
AllPotentials.Rows[index]["SourceId"] = potentials[i].SourceID;
AllPotentials.Rows[index]["PotentialStage"] = potentials[i].PotentialStage;
AllPotentials.Rows[index]["UpdateRecord"] = true;
AllPotentials.Rows[index]["Amount"] = potentials[i].Amount;
AllPotentials.Rows[index]["ZohoID"] = posterUpdate.UpdatePotentialRecord(3, filter[0].Row);
}
}
AllPotentials.AcceptChanges();
}
private int CheckSoidOrSource(string Soid, bool checkSource)
{
var filter = AllPotentials.DefaultView;
if (checkSource)
filter.RowFilter = string.Format("Soid = '{0}' OR SourceId = '{1}'",Soid, Soid);
else
filter.RowFilter = string.Format("Soid = '{0}'", Soid);
return filter.Count;
}
basically what is happening is that i noticed something about my data when I filter it this way. The two results would only return the following results (0,0) (0,1) and (1,0) (0,0) means that the record doesn't exist at all in this table so I need to add it. (1,0) means that the Sales Order ID (Soid) matches another Soid in the table so it already exists. Lastly (0,1) means that the Soid doesn't exist in this table but i found a record that has the Soid as it's source...which to me means that the one that had it as a source has been upgraded from a potential to a sale, which in turn means i have to update the record and Zoho. This worked out to much less work for me because now I don't have to search for won and lost records, i only have to search for lost records. less code same results is always a good thing :)

Categories

Resources