I am trying to port some existing VBA code to C#. One routine controls the indentation of bullet items, and is roughly:
indentStep = 13.5
For Each parag In shp.TextRange.Paragraphs()
parag.Parent.Ruler.Levels(parag.IndentLevel).FirstMargin = indentStep * (parag.IndentLevel - 1)
parag.Parent.Ruler.Levels(parag.IndentLevel).LeftMargin = indentStep * (parag.IndentLevel)
Next parag
The code works, but appears to be spooky black magic. In particular, each time a particular ruler's margins are set ALL NINE rulers margins are actually set.
But somehow the appropriate information is being set. Unfortunately, when you do the same thing in C#, the results change. The following code has no visible effect:
const float kIndentStep = 13.5f;
foreach (PowerPoint.TextRange pg in shp.TextFrame.TextRange.Paragraphs())
{
pg.Parent.Ruler.Levels[pg.IndentLevel].FirstMargin = kIndentStep * (pg.IndentLevel - 1);
pg.Parent.Ruler.LevelS[pg.IndentLevel].LeftMargin = kIndentStep * pg.IndentLevel;
}
This appears to be a limitation/bug when automating PowerPoint from C#. I confirm it works with VBA.
I do see an effect after the code runs: it changes the first level with each run so that, at the end, the first level has the settings that should have been assigned to the last level to be processed, but none of the other levels appear to be affected, visibly. I do see a change in the values returned during code execution, but that's all.
If the code changes only one, specific level for the text frame, it works. The problem occurs only when attempting to change multiple levels.
I tried various approaches, including late-binding (PInvoke) and putting the change in a separate procedure, but the result was always the same.
Here's my last iteration
Microsoft.Office.Interop.PowerPoint.Application pptApp = (Microsoft.Office.Interop.PowerPoint.Application) System.Runtime.InteropServices.Marshal.GetActiveObject("Powerpoint.Application"); // new Microsoft.Office.Interop.PowerPoint.Application();
//Change indent level of text
const float kIndentStep = 13.5f;
Microsoft.Office.Interop.PowerPoint.Shape shp = pptApp.ActivePresentation.Slides[2].Shapes[2];
Microsoft.Office.Interop.PowerPoint.TextFrame tf = shp.TextFrame;
object oTf = tf;
int indentLevelLast = 0;
foreach (Microsoft.Office.Interop.PowerPoint.TextRange pg in tf.TextRange.Paragraphs(-1, -1))
{
int indentLevel = pg.IndentLevel;
if (indentLevel > indentLevelLast)
{
Microsoft.Office.Interop.PowerPoint.RulerLevel rl = tf.Ruler.Levels[indentLevel];
object oRl = rl;
System.Diagnostics.Debug.Print(pg.Text + ": " + indentLevel + ", " + rl.FirstMargin.ToString() + ", " + rl.LeftMargin.ToString()) ;
object fm = oRl.GetType().InvokeMember("FirstMargin", BindingFlags.SetProperty, null, oRl, new object[] {kIndentStep * (indentLevel - 1)});
//rl.FirstMargin = kIndentStep * (indentLevel - 1);
object lm = oRl.GetType().InvokeMember("LeftMargin", BindingFlags.SetProperty, null, oRl, new object[] { kIndentStep * (indentLevel) });
//rl.LeftMargin = kIndentStep * indentLevel;
indentLevelLast = indentLevel;
System.Diagnostics.Debug.Print(pg.Text + ": " + indentLevel + ", " + tf.Ruler.Levels[indentLevel].FirstMargin.ToString() + ", " + tf.Ruler.Levels[indentLevel].LeftMargin.ToString()) ;
rl = null;
}
}
FWIW neither code snippet provided in the question compiles. The VBA snippet is missing .TextFrame. The C# snippet doesn't like Parent.Ruler so I had to change it to TextFrame.Ruler.
Related
I am using DotNetCore.NPOI (1.2.1) in order to read an MS Excel file.
Some of the cells are of type text and contain formatted strings (e.g. some words in bold).
How do I get the formatted cell value? My final goal: Retrieve the cell text as HTML.
I tried
var cell = row.GetCell(1);
var richStringCellValue = cell.RichStringCellValue;
But this won't let me access the formatted string (just the plain string without formattings).
Does anybody have an idea or solution?
I think you'll have to take longer route in this case. First you'll have to maintain the formatting of cell value like date, currency etc and then extract the style from cell value and embed the cell value under that style.
best option is to write extenstion method to get format and style value.
To get the fomat Please see this link How to get the value of cell containing a date and keep the original formatting using NPOI
For styling first you'll have to check and find the exact style of running text and then return the value inside the html tag , below method will give you idea to extract styling from cell value. Code is untested , you may have to include missing library.
public void GetStyleOfCellValue()
{
XSSFWorkbook wb = new XSSFWorkbook("YourFile.xlsx");
ISheet sheet = wb.GetSheetAt(0);
ICell cell = sheet.GetRow(0).GetCell(0);
XSSFRichTextString richText = (XSSFRichTextString)cell.RichStringCellValue;
int formattingRuns = cell.RichStringCellValue.NumFormattingRuns;
for (int i = 0; i < formattingRuns; i++)
{
int startIdx = richText.GetIndexOfFormattingRun(i);
int length = richText.GetLengthOfFormattingRun(i);
Console.WriteLine("Text: " + richText.String.Substring(startIdx, startIdx + length));
if (i == 0)
{
short fontIndex = cell.CellStyle.FontIndex;
IFont font = wb.GetFontAt(fontIndex);
Console.WriteLine("Bold: " + (font.IsBold)); // return string <b>my string</b>.
Console.WriteLine("Italics: " + font.IsItalic + "\n"); // return string <i>my string</i>.
Console.WriteLine("UnderLine: " + font.Underline + "\n"); // return string <u>my string</u>.
}
else
{
IFont fontFormat = richText.GetFontOfFormattingRun(i);
Console.WriteLine("Bold: " + (fontFormat.IsBold)); // return string <b>my string</b>.
Console.WriteLine("Italics: " + fontFormat.IsItalic + "\n");// return string <i>my string</i>.
}
}
}
Font formatting in XLSX files are stored according to schema http://schemas.openxmlformats.org/spreadsheetml/2006/main which has no direct relationship to HTML tags. Therefore your task is not that much straight forward.
style = cell.getCellStyle();
font = style.getFont(); // or style.getFont(workBook);
// use Font object to query font attributes. E.g. font.IsItalic
Then you will have to build the HTML by appending relevant HTML tags.
newRow("OrderReference") = line.Substring(line.IndexOf("*1003") + 5, line.IndexOf("*", line.IndexOf("*1003") + 5) - line.IndexOf("*1003") - 5)
There you have it. Very long and ugly. I was thinking about this:
Dim indexPlus = line.IndexOf("*1003") + 5
Dim indexMinus = line.IndexOf("*1003") - 5
newRow("OrderReference") = line.Substring(indexPlus, line.IndexOf("*", indexPlus) - indexMinus)
But that introduces new and meaningless vars. Unsatisfying.
Maybe RegEx is the savior here?
Unfortunately I mustn't change the input data :-(
The input data consist of the BWA-format (popular with books). Here you can see the part in question:
All codes in this example set are required. Only corresponding values change.
I don't even think your second code works. It seems more like this.
Dim index = line.IndexOf("*1003") + 5
newRow("OrderReference") = line.Substring(index, line.IndexOf("*", indexPlus) - index)
10 - 5 - 2 isn't the same as 10 - (5 - 2) but instead it's the same as 10 - (5 + 2).
Next time, check out the codereview stack exchange.
Given that your data is always constant, and what you're looking for always begins with "*1003", you don't need to use Regex (Even though you could). Just use what you're already using but with some corrections.
using System;
public class Program
{
public static void Main()
{
string input = "L10113540 VD44444 VD2002100234949 000116161 04201261\r\n";
input += " KN00010000000129000LPEUR003000001*1003A.Muller-Schulz*1017Bastei\r\n";
input += "Lubbe.61204 Laund.Meine Schuld*1019KL*102990300*1030NO*1032EUR*1131KT";
int start = input.IndexOf("*1003");
int end = input.IndexOf("*", start + 1);
string result = input.Substring(start + 5, end - start - 5);
Console.WriteLine(result);
// Your code
start = input.IndexOf("*1003") + 5;
end = input.IndexOf("*1003") - 5;
result = input.Substring(start, input.IndexOf("*", start) - end);
Console.WriteLine(result);
}
}
Result
A.Muller-Schulz
A.Muller-Schulz*1017Baste
You can see that what you posted in your question, doesn't give the results you want. All you're really looking for is just the next asterisk after the first "*1003". You can see the difference between your code and what I've given.
.NET Fiddle Example
I am just starting to dabble in PL/SQL so this question may be very straightforward. Here is the scenario:
I have several checkboxes which carry a weighted numeric value. For example:
Checkbox I --> Value '5'
Checkbox II --> Value '10'
Checkbox III --> Value '15'
etc.
The form would have 15 checkboxes in total and the end-user can select anywhere from 0 to all 15. As they select the checkboxes, the total weight would get calculated and a final numeric value would be displayed. For example. checking off 3 Checkbox I & 2 Checkbox III would = 45 points.
Now the total value of 45 would equal to a separate value. Example:
At 0 points, value = 'Okay'
1-15 points, value = 'Error'
16-30 points, value = 'Warning'
31+ points, value = 'Critical'
The form itself is built within Oracle APEX and I can do it using Dynamic Actions but using PL/SQL may be a better solution.
In summary, I'd like the hidden field to first calculate the total from the checked checkboxes and then use that total to figure out the value of either Okay, Error, Warning, or Critical.
Any assistance is much appreciated!
In my experience, it is better if we're going to use javascript on your case since we have to manipulate DOM and events of the checkboxes. If you want to display/change item values and get values at runtime, then javascript is better in doing that than PLSQL unless you want to submit your page every time you check/uncheck a box in your page which is not advisable.
Here is my solution for your question.
First, create a Display Only item on your page. This is where the values "Okay", "Error", "Warning", or "Critical" will appear. And its very important to set it's default value to 0. Then inside your page's "Function and Global Declaration" part, put the following functions:
function getcheck(checkbox_id,displayOnly_id){
var chkboxName = document.getElementById(checkbox_id + "_0").getAttribute("name");
var chks = document.getElementsByName(chkboxName);
var howmanychecks = chks.length;
var currentSum=0;
var v_remarks = "";
for(x=0;x<howmanychecks;x++){
chks[x].setAttribute("onchange","checkIfchecked(this,\'" + displayOnly_id + "\')");
if(chks[x].checked){
currentSum = currentSum + Number(chks[x].value);
}
}
if(currentSum==0){
v_remarks = "Okay";
}
else if(currentSum>0 && currentSum<=15){
v_remarks = "Error";
}
else if(currentSum>15 && currentSum<=30){
v_remarks = "Warning";
}
else{
v_remarks = "Critical";
}
document.getElementById(displayOnly_id).value = currentSum;
document.getElementById(displayOnly_id + "_DISPLAY").innerHTML = currentSum + ": " + v_remarks;
}
function checkIfchecked(p_element, displayOnly_id){
var v_difference;
var v_sum = Number($v(displayOnly_id));
var displayOnly_display = displayOnly_id + "_DISPLAY";
var v_remarks = "";
if(p_element.checked){
v_sum = v_sum + Number(p_element.value);
$("#" + displayOnly_id).val(v_sum);
}
else{
v_difference=Number($("#" + displayOnly_id).val())-Number(p_element.value);
if(v_difference<0){
v_difference=0;
}
$("#" + displayOnly_id).val(v_difference);
}
if($("#" + displayOnly_id).val()==0){
v_remarks = "Okay";
}
else if($("#" + displayOnly_id).val()>0 && $("#" + displayOnly_id).val()<=15){
v_remarks = "Error";
}
else if($("#" + displayOnly_id).val()>15 && $("#" + displayOnly_id).val()<=30){
v_remarks = "Warning";
}
else{
v_remarks = "Critical";
}
document.getElementById(displayOnly_display).innerHTML=$("#" + displayOnly_id).val() + ": " + v_remarks;
}
The above functions will get the sum of the values of those boxes that are checked. A value of a box will be taken out of the current sum if it is unchecked as well. It will also display the remarks for the current checked points whether if it is "Okay", "Error", "Warning", or "Critical".
In your "Execute when Page Loads" part of your page, add the following line:
getcheck(nameofyourcheckboxitem,nameofyourdisplayonlyitem);
where nameofyourcheckboxitem is the name of your Check Box and nameofyourdisplayonlyitem is the name of the Display Only item you have just created.
Here's a sample line on how to use the function that I've given you:
getcheck("P1_MYCHECKBOX","P1_MYDISPLAYONLY");
consider the following:
gridID = datagridID;
//column headers
domConstruct.place("<div class=\"gridheaderrow\" data-type =\"BolingerGridHeaderRow\" ></div>", gridID, "first");
var node = query("div[data-type=\"BolingerGridRow\"]", gridID);
var headerNode = query("div[data-type=\"BolingerGridHeaderRow\"]", gridID);
var cells = query("div[data-type=\"BolingerGridCell\"]", node[0]);
for (var i = 0; i < cells.length; i++)
{
var columnname;
columnname = attr.get(cells[i], "data-columnname");
var headernode = domConstruct.place("<div class=\"gridheadercell\" data-type=\"BolingerGridHeaderCell\">" + columnname + "</div>", headerNode[0], "last");
var sortup = domConstruct.place("<div id=column'" + i + "' data-columnupid = '" + i + "' data-type='sortuparrow' style='display:inline; cursor:pointer'>▲</div>", headernode, "last");
var sortdown = domConstruct.place("<div id=column'" + i + "' data-columndownid = '" + i + "' data-type='sortdownarrow' style='display:inline; cursor:pointer'>▼</div>", headernode, "last");
}
for (var i = 0; i < cells.length; i++)
{
var sortupnode = query("[data-columnupid = '" + i + "']", gridID)[0];
var sortdownnode = query("[data-columndownid = '" + i + "']", gridID)[0];
on(sortupnode, "click", function (e) {
var num = attr.get(sortupnode, "data-columnupid");
sort(true, num);
});
on(sortdownnode, "click", function (e) {
var num = attr.get(sortdownnode, "data-columndownid");
sort(false, num);
});
}
This code places little up and down arrows above each column and attaches on click events to them, which calls the sort function. I'm quite sure I'm attaching the events each up or down arrow once. Yet, no matter what arrow I click on the handler that handles it belongs to the arrow of the last column. Why is this? I'm figuring it has something to do with attaching handlers to nodes I just placed. Thoughts?
Variables do not have block scope in JavaScript. You are expecting that each iteration through your second for loop has its own sortupnode and sortdownnode variables, but in fact each time through the loop, the same variable is being redeclared and its value is being replaced. Your on handlers are continuing to reference the same sortupnode and sortdownnode variables, which by the time they run, will always reference the very last nodes iterated.
In this case the absolute simplest fix would likely be to replace sortupnode and sortdownnode inside your event handlers with this, which should reference the element that the handler fired for. However, you should be able to avoid this issue completely and hook up these event handlers much more efficiently using event delegation. Something along the lines of:
on(document.getElementById(gridID), '[data-columnupid]:click', function (event) {
// Inside delegated event handlers registered with dojo/on,
// `this` references the element that matched the selector
var num = this.getAttribute('data-columnupid');
sort(true, num);
});
Addendum
In response to your second comment: the problem you are facing has no direct correlation to event handling; it is purely related to how scope works in JavaScript.
To attempt to better illustrate how the variables in your loops are actually working, bear in mind that this:
for (var i = 0; i < ...; i++) {
var foo = ...;
...
}
... is essentially equivalent to this, because JavaScript variables do not have block scope:
var i;
var foo;
for (i = 0; i < ...; i++) {
foo = ...;
...
}
That is to say, the variable foo exists in the scope of the surrounding function, not the for loop. The same foo variable has its value modified each time through the loop.
Any code that looks at foo after the loop finishes running will see the last value foo was assigned in the loop. You are defining event handler callbacks in each iteration through your loop which have access to foo from the containing function's scope, but those callbacks are only actually called way later when the user performs an action. "Way later" = after the loop finished running = foo is always going to be the value it was set to during the last iteration.
I have been working on a project where I need to interact with 3D objects using a hand joint, now I do that using distance formula but however . . i'm using hand joint (X,Y,Z) and using a formula to convert them to 3D space to be able to have the interact with the Models.
Instead of using a formula to do the conversion . . do i actually need to use the function MapSkeletalpoint to color image point??
To give an idea about what i'm doing:
var rightHands = playerSkeleton.Joints[JointType.HandRight];
var rightHandsX = rightHands.Position.X;
var rightHandsY = rightHands.Position.Y;
var rightHandsZ = rightHands.Position.Z;
HandX = rightHands.Position.X;
HandY = rightHands.Position.Y;
HandZ = rightHands.Position.Z;
foreach (_3DModel s in Solar)
{
x_mod = (float)Math.Floor(((HandX * 0.5f)) * maxWidth);
y_mod = (float)Math.Floor(((HandY * -0.5f)) * maxHeight);
z_mod = (float)Math.Floor((HandZ) / 4 * 20000);
if (Math.Sqrt(Math.Pow(x_mod - s.modelPosition.X, 2) + Math.Pow(y_mod- s.modelPosition.Y, 2) + Math.Pow(z_mod - s.modelPosition.Z,2)) < 20)
{
sound.Play();
Console.WriteLine("1" + "handx:" + x + "," + " " + "modelPos.X:" + s.modelPosition.X + "," + " " + "handY:" + y + "modelPos.Y:" + s.modelPosition.Y);
}
Actually, it's not easy to create effective hit test especially when there is larger number of objects.
The thing you need is a collision system. My recommendation to you is that you should use some existing, for example BEPU might be helpful:
http://bepu.squarespace.com/
You should bind the position of an object with the position of the hand (reported by Kinect) and BEPU will do the rest (it will trigger a handler in case of collision).
Just a small advice - it's always good to use relative position of the hand (e.g. to the shoulder). Using absolute coordinates reported by Kinect is not always desired in Kinect-based games (but you know better what you want)