I know, that I can change color of all text in shape this way:
Shape.CellsU["Char.Color"].FormulaForceU = "RGB(255,255,255)";
There is also way to change color of certain characters in shape this way:
Characters.Begin = 2;
Characters.End = 5;
Characters.set_CharProps((short)MSVisio.VisCellIndices.visCharacterColor, (short)MSVisio.VisDefaultColors.visRed;);
But I can't find any way to pass custom RGB (or HEX or any type) value of color to just certain characters in shape. I don't want to cut shape into little shapes.
Can you please help? Thanks
The CharProps property allows you to set an index into the predefined document colors collection. For a custom RGB you can set the corresponding cell formula by first getting the row index with CharPropsRow like this:
var shp = vApp.ActiveWindow.Selection.PrimaryItem;
var shpChars = shp.Characters;
shpChars.Begin = 2;
shpChars.End = 5;
//shpChars.set_CharProps((short)Visio.VisCellIndices.visCharacterColor, (short)Visio.VisDefaultColors.visRed);
var targetRow = shpChars.CharPropsRow[0];
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionCharacter,
targetRow,
(short)Visio.VisCellIndices.visCharacterColor].FormulaU = "RGB(40,220,40)";
and that should give you similar results to this:
[Update] The above assumes you're targeting existing formating and changing it. To add new runs you can use CharProps first to add the row and then the CharPropsRow to target that new run. So you can run this code against a new page:
var vPag = vApp.ActivePage;
var shp = vPag.DrawRectangle(3,3,5,4);
shp.Text = "GoodMorning";
var shpChars = shp.Characters;
shpChars.Begin = 0;
shpChars.End = 4;
var targetRow = shpChars.CharPropsRow[(short)Visio.VisCharsBias.visBiasLetVisioChoose];
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionCharacter,
targetRow,
(short)Visio.VisCellIndices.visCharacterColor].FormulaU = "RGB(220,40,40)";
shpChars.Begin = 4;
shpChars.End = 11;
shpChars.set_CharProps((short)Visio.VisCellIndices.visCharacterColor, (short)Visio.VisDefaultColors.visBlack);
targetRow = shpChars.CharPropsRow[(short)Visio.VisCharsBias.visBiasLetVisioChoose];
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionCharacter,
targetRow,
(short)Visio.VisCellIndices.visCharacterColor].FormulaU = "RGB(40,200,40)";
...and this should result in the following:
Related
My goal is to highlight a section of an Excel Range (which I can do with conditional formatting) but now it also needs to add up-arrows to each cell within that aforementioned range.
How does one do that?
What I have tried is something like this
var wb = Globals.ThisAddIn.Application.ActiveWorkbook;
var formatIcon = rngExistingColumns.FormatConditions.AddIconSetCondition() as Excel.IconSetCondition;
formatIcon.Formula = "=1";
formatIcon.IconSet = wb.IconSets[Excel.XlIconSet.xl3Arrows];
var crit1 = formatIcon.IconCriteria;
crit1[1].Type = Excel.XlConditionValueTypes.xlConditionValueFormula;
crit1[1].Operator = (int) Excel.XlFormatConditionOperator.xlGreater;
crit1[1].Value = 0;
But I do not get any results and only get com exceptions thrown.
Do not manually set the rule for red icon crit1[1] because the red icon is the last rule against green/yellow icon rules.
My sample code works, and it just sets green/yellow rules. Value 60 will be yellow , value 80 and 90 will be green, the other will be red.
var wb = Globals.ThisAddIn.Application.ActiveWorkbook;
wb.ActiveSheet.Range["A1"].Value2 = "20";
wb.ActiveSheet.Range["A2"].Value2 = "80";
wb.ActiveSheet.Range["A3"].Value2 = "60";
wb.ActiveSheet.Range["A4"].Value2 = "40";
wb.ActiveSheet.Range["A5"].Value2 = "30";
wb.ActiveSheet.Range["A6"].Value2 = "90";
// Add an icon set condition to the range
Excel.IconSetCondition iconSetCondition1 =
(Excel.IconSetCondition)
wb.ActiveSheet.Range["A1", "A6"].
FormatConditions.AddIconSetCondition();
iconSetCondition1.SetFirstPriority();
iconSetCondition1.ShowIconOnly = false;
iconSetCondition1.IconSet =
Excel.XlIconSet.xl3Arrows;
var yellowIcon = iconSetCondition1.IconCriteria[2];
yellowIcon.Type = Excel.XlConditionValueTypes.xlConditionValueNumber;
yellowIcon.Operator = (int)Excel.XlFormatConditionOperator.xlGreaterEqual;
yellowIcon.Value = Convert.ToDouble(60);
var greenIcon = iconSetCondition1.IconCriteria[3];
greenIcon.Type = Excel.XlConditionValueTypes.xlConditionValueNumber;
greenIcon.Operator = (int)Excel.XlFormatConditionOperator.xlGreaterEqual;
greenIcon.Value = Convert.ToDouble(80);
2020/08/25 Update
If you want the top rule is red, second rule is green, the last is yellow, you can set Icon to change the rule orders.
For my example, Value 60 will be green, value 80 and 90 will be red, the other will be yellow.
var greenIcon = iconSetCondition1.IconCriteria[2];
greenIcon.Icon = Excel.XlIcon.xlIconGreenCircle;
greenIcon.Type = Excel.XlConditionValueTypes.xlConditionValueNumber;
greenIcon.Operator = (int)Excel.XlFormatConditionOperator.xlGreaterEqual;
greenIcon.Value = Convert.ToDouble(60);
var redIcon = iconSetCondition1.IconCriteria[3];
redIcon.Icon = Excel.XlIcon.xlIconRedCircle;
redIcon.Type = Excel.XlConditionValueTypes.xlConditionValueNumber;
redIcon.Operator = (int)Excel.XlFormatConditionOperator.xlGreaterEqual;
redIcon.Value = Convert.ToDouble(80);
var yellowIcon = iconSetCondition1.IconCriteria[1];
yellowIcon.Icon = Excel.XlIcon.xlIconYellowCircle;
I have a table with sensors data. There are sensors with long names that do not fit into a cell.
I want to add three points to end of the long sensor names (like text-overflow: ellipsis in css). I want to do it flexible without hardcoded values. Because in the future number of columns may be different.
How can I do it?
I create table like this:
var table = new PdfPTable(columns.Length);
var widths = new List<float>();
for (var i = 0; i < columns.Length; i++)
{
widths.Add(1f);
}
table.SetWidths(widths.ToArray());
And fill it:
for (var i = 0; i < columns.Length; i++)
{
var cell = new PdfPCell(new Phrase(columns[i], tableDataFont));
cell.UseAscender = true;
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
cell.BackgroundColor = new Color(204, 204, 204);
cell.MinimumHeight = 20f;
table.AddCell(cell);
}
Something like this should work...
var text = "testtesttesttesttest";
var maxLength = 7;
var displayname = text;
if (text.Length > maxLength)
{
displayname = text.Substring(0, maxLength) + "...";
}
Considering you didn't post any code of your own, I can only provide some logic.
What I do here is just take the real value (insert that into text).
Declare a maximum length you want the text in your fields to be (maxLength).
Create a storage variable to for manipulation of the name without losing the original data (in case you want to keep that).
Then check if the text is longer than the maximumlength and replace all that is too long with "...".
You can then return "displayname" to whatever field you want it to be in.
This most likely isn't what you're looking for, but it does answer your question as it is right now.
So in using Microsoft.Office.Interop.Word to generate letters automatically, the issue I am having is the header section. Whenever I run the code below it just replaces the first Item in the header on all pages instead of having both the client name and client address on the header of all pages on different lines, I just get the client address
foreach (Section section in document.Sections)
{
//Get the header range and add the header details.
var headerRange = section.Headers[WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
var headerRange1 = section.Headers[WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
headerRange.Fields.Add(headerRange, WdFieldType.wdFieldPage);
headerRange1.Fields.Add(headerRange, WdFieldType.wdFieldPage);
headerRange.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphLeft;
headerRange1.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphLeft;
headerRange.Font.ColorIndex = WdColorIndex.wdBlack;
headerRange1.Font.ColorIndex = WdColorIndex.wdBlack;
headerRange.Font.Size = 12;
headerRange1.Font.Size = 12;
headerRange.Font.Name = "Arial";
headerRange1.Font.Name = "Arial";
headerRange.Font.Bold = 1;
headerRange1.Font.Bold = 1;
headerRange.Text = ClientNameBox.Text;
headerRange.InsertParagraphAfter();
headerRange1.Text = ClientsAddressBox.Text;
headerRange.ParagraphFormat.Alignment = Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphLeft;
headerRange1.ParagraphFormat.Alignment = Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphLeft;
}
Think of a Range like an invisible selection: if you type something when content is selected, what you type replaces what was selected. So if you assign something to a Range that has content, what you assign replaces the content. It doesn't matter whether you assign the same content to two separate Range objects - since they both encompass the same start and end points, changing the one changes the other.
The trick with a Range, as with a Selection, is to "collapse" it. For a selection, you press an arrow key; for a Range there is a Collapse method, where you specify the direction: either to the Start or to the End.
foreach (Section section in document.Sections)
{
//Get the header range and add the header details.
var headerRange = section.Headers[WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
headerRange.Fields.Add(headerRange, WdFieldType.wdFieldPage);
headerRange.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphLeft;
headerRange.Font.ColorIndex = WdColorIndex.wdBlack;
headerRange.Font.Size = 12;
headerRange.Font.Name = "Arial";
headerRange.Font.Bold = 1;
headerRange.Text = ClientNameBox.Text;
headerRange.InsertParagraphAfter();
object oCollapseEnd = WdCollapseDirection.wdCollapseEnd;
headerRange.Collapse(ref oCollapseEnd);
headerRange.Text = ClientsAddressBox.Text;
headerRange.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphLeft;
}
I'm using epplus to create excel in my program!
I need insert a column chart.
This is my code:
//Add the chart to the sheet
var chart = sheet.Drawings.AddChart(chartTitle, eChartType.ColumnStacked3D);
chart.SetPosition(positionRow, 2, positionCol, 2);
chart.Title.Text = chartTitle;
chart.Title.Font.Bold = true;
chart.Title.Font.Size = 18;
chart.SetSize(width, height);
//Set the data range
chart.Series.Add("D17:D22", "B17:B22");
chart.Series.Add("P17:P22", "B17:B22");
And I get result:
But I want result as:
After I created excel file from program, I open it and change the chart:
Right click in the chart/Select data/Switch row/column.
How can I Switch row/column in my code? Or how to insert the chart like the below picture?
Sorry for not good in English
Thank you very much!
That button in excel just switches the data and rebuilds the chart. Rather then try to mimic it better to build the chart the right way from the start.
What I mean is your original chart is treating the data as 2 data series but what you really want is 6 series.
The only problem is values in the x axis - there is no direct way with Epplus it seems to get to the category (horizontal) axis labels of the series. So you have to do it through XML manipulation as below.
So change your code to this:
//Set the data range
//chart.Series.Add("D17:D22", "B17:B22");
//chart.Series.Add("P17:P22", "B17:B22");
for (var i = 0; i < opt.Count; i++)
{
var datarange = sheet.Cells[$"Bar!D{17 + i},Bar!P{17 + i}"];
var ser = chart.Series.Add(datarange.Address, $"B{17 + i}:B{17 + i}");
ser.HeaderAddress = sheet.Cells[$"$B{17 + i}"];
}
//have to remove cat nodes from each series so excel autonums 1 and 2 in xaxis
var chartXml = chart.ChartXml;
var nsm = new XmlNamespaceManager(chartXml.NameTable);
var nsuri = chartXml.DocumentElement.NamespaceURI;
nsm.AddNamespace("c", nsuri);
//Get the Series ref and its cat
var serNodes = chartXml.SelectNodes("c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser", nsm);
foreach (XmlNode serNode in serNodes)
{
//Cell any cell reference and replace it with a string literal list
var catNode = serNode.SelectSingleNode("c:cat", nsm);
catNode.RemoveAll();
//Create the string list elements
var ptCountNode = chartXml.CreateElement("c:ptCount", nsuri);
ptCountNode.Attributes.Append(chartXml.CreateAttribute("val", nsuri));
ptCountNode.Attributes[0].Value = "2";
var v0Node = chartXml.CreateElement("c:v", nsuri);
v0Node.InnerText = "opening";
var pt0Node = chartXml.CreateElement("c:pt", nsuri);
pt0Node.AppendChild(v0Node);
pt0Node.Attributes.Append(chartXml.CreateAttribute("idx", nsuri));
pt0Node.Attributes[0].Value = "0";
var v1Node = chartXml.CreateElement("c:v", nsuri);
v1Node.InnerText = "closing";
var pt1Node = chartXml.CreateElement("c:pt", nsuri);
pt1Node.AppendChild(v1Node);
pt1Node.Attributes.Append(chartXml.CreateAttribute("idx", nsuri));
pt1Node.Attributes[0].Value = "1";
//Create the string list node
var strLitNode = chartXml.CreateElement("c:strLit", nsuri);
strLitNode.AppendChild(ptCountNode);
strLitNode.AppendChild(pt0Node);
strLitNode.AppendChild(pt1Node);
catNode.AppendChild(strLitNode);
}
pck.Save();
Which gives this as the output in my unit test (made up the numbers):
I'm using System.Windows.Forms.DataVisualization.Charting in my app and I want to add different label types on my X axis which is representing a timeline. For example, I want to use "HH:mm" format for labels but when it's 00:00 I'd like to show "dd.MM" format instead. I tried to add cutom labels but it has no effect at all.
var area = new ChartArea();
area.AxisX.LabelStyle.Format = "HH:mm";
area.AxisX.Interval = 1 / 24.0;
area.AxisX.CustomLabels.Add(1.0, DateTimeIntervalType.Days, "dd.MM");
Adding CustomLabels will help; however if you want them to show an individual format you will have to add them individually to each DataPoint!
Doing so is not quite as simple as one could wish for; there are several overloads but none is really easy to use. The simplest way, imo, is to use one with a FromPosition and a ToPosition; these should then to be set in a way that they hit right between the DataPoints; this way they will be centered nicely..
Note that as the X-Values are really DateTimes, but as always in a chart, converted to doubles we need to convert them back to DateTime for correct formatting and also use their values to calculate the middle or rather half an interval..
Here is an example:
// prepare the test chart..
chart1.ChartAreas.Clear();
ChartArea CA = chart1.ChartAreas.Add("CA1");
Random R = new Random(123);
chart1.Series.Clear();
CA.AxisX.MajorTickMark.Interval = 1;
Series S = chart1.Series.Add("S1");
S.Points.Clear();
S.ChartType = SeriesChartType.Column;
S.SetCustomProperty("PixelPointWidth", "10");
// some random data:
DateTime dt0 = new DateTime(2015, 05, 01);
for (int i = 0; i< 40; i++)
{
int p = S.Points.AddXY(dt0.AddHours(i), R.Next(100));
}
// each custom label will be placed centered in a range
// so we need an amount of half an interval..
// this assumes equal spacing..
double ih = (S.Points[0].XValue - S.Points[1].XValue) / 2d;
// now we add a custom label to each data point
for (int i = 0; i < S.Points.Count; i++)
{
DataPoint pt = S.Points[i];
string s = (DateTime.FromOADate(pt.XValue)).ToString("HH:mm");
bool midnite = s == "00:00";
if (midnite) s = DateTime.FromOADate(pt.XValue).ToString("dd.MM.yyyy");
CustomLabel CL = CA.AxisX.CustomLabels.Add(pt.XValue - ih, pt.XValue + ih, s);
CL.ForeColor = midnite ? Color.Blue : Color.Black;
}