I have an application written in C# that needs to be converted to Python, since I have recently switched to Linux. It's a simple GUI application to manage unknown words while learning a new language. Nevertheless, I need remove_item() function for which I also need find_word() function.
In C#, I would create two following methods:
void Remove()
{
Word word = new Word();
try { word = FindWord(listView1.SelectedItems[0].Text); }
catch { return; }
if (listView1.SelectedItems.Count > 0)
{
try
{
foreach (ListViewItem eachItem in listView1.SelectedItems)
{
words.RemoveAll(x => x.WordOrPhrase == eachItem.Text);
listView1.Items[listView1.Items.Count - 1].Selected = true;
listView1.Items.Remove(eachItem);
}
}
catch { }
ClearAll();
ReadOnlyON();
}
else
{
MessageBox.Show("You have not selected any words!", "Notification", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
ReadOnlyOFF();
WordCount();
Sync();
}
private Word FindWord(string word)
{
return words.Find(x => x.WordOrPhrase == word);
}
...but I'm still a newbie when it comes to Python, so any help would be appreciated. Here is what I have so far:
When it comes to the FindWord() method, it could be rewritten as following:
def FindWord(word):
for x in words:
if x.WordOrPhrase == word:
return x
or
def FindWord(word):
return next((x for x in words if x.WordOrPhrase == word), None)
or
def FindWord(word):
return next(filter(lambda x: x.WordOrPhrase == word, words), None)
...but I'm struggling to rewrite Remove() method. Here is one way:
def remove_item(self):
word = self.listBox.get(ACTIVE)
new_word_list = [] # initialize empty list
delete_idxs = []
for idx, item in enumerate(self.words):
if item.wordorphrase == word:
delete_idxs.append(idx)
else:
new_word_list.append(item)
self.words = new_word_list # overwrite the old word_list with the new one
for idx in reversed(delete_idxs):
self.listBox.delete(idx)
...what I would like most is converting my C# method to Python. Here is what I have so far:
def remove_item(self):
word = Word()
try:
word = find_word(self.listBox.curselection())
except:
return
if self.listBox.len(curselection()) > 0:
try:
for item in self.listBox.curselection():
self.words.remove(lambda x: x.wordorphrase == item.text)
# listView1.Items[listView1.Items.Count - 1].Selected = true;
self.listBox.remove(item)
except:
pass
self.clear_all()
else:
pass
# show messagebox
I don't know how to access:
listView1.SelectedItems[0].Text
listView1.SelectedItems.Count > 0
listView1.SelectedItems
listView1.Items[listView1.Items.Count - 1].Selected
Here is what I have done so far:
# Vocabulary.py
# GUI program to manage unknown words
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import xml.etree.ElementTree as ET
import os
class Word:
def __init__(self, wordorphrase, explanation, translation, example):
self.wordorphrase = wordorphrase
self.explanation = explanation
self.example = example
self.translation = translation
class Vocabulary(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.master = master
self.master.resizable(width = False, height = False)
self.master.title("Vocabulary")
self.create_widgets()
self.words = []
self.load_words()
def on_closing(self):
self.save_all()
if messagebox.askokcancel("Quit", "Do you want to quit?"):
self.master.destroy()
def create_widgets(self):
self.buttons_frame = Frame(self.master)
self.buttons_frame.grid(row = 10, sticky = W)
self.search_frame = Frame(self.master)
self.search_frame.grid(row = 1, sticky = W, columnspan = 2)
self.comboBox = ttk.Combobox(self.search_frame,
width = 3)
self.comboBox.grid(row = 0, column = 14, sticky = W)
self.comboBox['values'] = ( 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' )
self.btn_Add = Button(self.buttons_frame,
text = 'Add',
command = self.add_item)
self.btn_Add.grid(row = 0, sticky = W)
self.btn_Remove = Button(self.buttons_frame,
text = 'Remove',
command = self.remove_item)
self.btn_Remove.grid(row = 0, column = 1, sticky = W)
self.btn_Edit = Button(self.buttons_frame,
text = 'Edit',
command = self.edit_item)
self.btn_Edit.grid(row = 0, column = 2, sticky = W)
self.btn_Save = Button(self.buttons_frame,
text = 'Save',
command = self.save_item)
self.btn_Save.grid(row = 0, column = 3, sticky = W)
self.btn_Refresh = Button(self.buttons_frame,
text = 'Refresh',
command = self.refresh_all)
self.btn_Refresh.grid(row = 0, column = 4, sticky = W)
self.lblSearch = Label(self.search_frame, text = 'SEARCH: ')
self.lblSearch.grid(row = 0, column = 5, sticky = W)
self.txt_Search = Text(self.search_frame,
height = 1,
width = 70)
self.txt_Search.grid(row = 0, column = 6, columnspan = 3, sticky = W)
self.lblWordsOrPhrases = Label(self.master, text = 'WORDS/PHRASES:')
self.lblWordsOrPhrases.grid(row = 2, column = 0)
self.lblWordOrPhrase = Label(self.master, text = 'Word or phrase:')
self.lblWordOrPhrase.grid(row = 2, column = 1, sticky = W)
self.listBox = Listbox(self.master,
selectmode='extended',
height = 34,
width = 38)
self.listBox.grid(row = 3, column = 0, rowspan = 7, sticky = W)
self.txt_WordOrPhrase = Text(self.master,
height = 1,
width = 40)
self.txt_WordOrPhrase.grid(row = 3, column = 1, sticky = N)
self.lblExplanation = Label(self.master, text = 'Explanation:')
self.lblExplanation.grid(row = 4, column = 1, sticky = W)
self.txt_Explanation = Text(self.master,
height = 10,
width = 40)
self.txt_Explanation.grid(row = 5, column = 1, sticky = N)
self.lblTranslation = Label(self.master, text = 'Translation:')
self.lblTranslation.grid(row = 6, column = 1, sticky = W)
self.txt_Translation = Text(self.master,
height = 10,
width = 40)
self.txt_Translation.grid(row = 7, column = 1, sticky = N)
self.lblExamples = Label(self.master, text = 'Example(s):')
self.lblExamples.grid(row = 8, column = 1, sticky = W)
self.txt_Example = Text(self.master,
height = 10,
width = 40)
self.txt_Example.grid(row = 9, column = 1, sticky = S)
def load_words(self):
self.listBox.delete(0, END)
self.words.clear()
path = os.path.expanduser('~/Desktop')
vocabulary = os.path.join(path, 'Vocabulary', 'Words.xml')
if not os.path.exists(vocabulary):
if not os.path.exists(os.path.dirname(vocabulary)):
os.mkdir(os.path.dirname(vocabulary))
doc = ET.Element('Words')
tree = ET.ElementTree(doc)
tree.write(vocabulary)
else:
tree = ET.ElementTree(file=vocabulary)
for node in tree.findall('WordOrPhrase'):
w = Word(node.find('Word').text, node.find('Explanation').text, node.find('Translation').text,
node.find('Examples').text)
self.words.append(w)
self.listBox.insert(END, w.wordorphrase)
def save_all(self):
path = os.path.expanduser('~/Desktop')
vocabulary = os.path.join(path, 'Vocabulary', 'Words.xml')
tree = ET.ElementTree(file=vocabulary)
for xNode in tree.getroot().findall('WordOrPhrase'):
tree.getroot().remove(xNode)
for w in self.words:
xTop = ET.Element('WordOrPhrase')
xWord = ET.Element('Word')
xExplanation = ET.Element('Explanation')
xTranslation = ET.Element('Translation')
xExamples = ET.Element('Examples')
xWord.text = w.wordorphrase
xExplanation.text = w.explanation
xTranslation.text = w.translation
xExamples.text = w.example
xTop.append(xWord)
xTop.append(xExplanation)
xTop.append(xTranslation)
xTop.append(xExamples)
tree.getroot().append(xTop)
tree.write(vocabulary)
def add_item(self):
w = Word(self.get_word(), self.get_explanation(), self.get_translation(), self.get_example())
self.words.append(w)
self.listBox.insert(END, w.wordorphrase)
self.clear_all()
self.save_all()
def remove_item(self):
word = Word()
try:
word = find_word(self.listBox.curselection())
except:
return
if self.listBox.len(curselection()) > 0:
try:
for item in self.listBox.curselection():
self.words.remove(lambda x: x.wordorphrase == item.text)
# listView1.Items[listView1.Items.Count - 1].Selected = true;
self.listBox.remove(item)
except:
pass
self.clear_all()
else:
pass
# show messagebox
def edit_item(self):
pass
def save_item(self):
pass
def clear_all(self):
self.txt_WordOrPhrase.delete('1.0', END)
self.txt_Explanation.delete('1.0', END)
self.txt_Translation.delete('1.0', END)
self.txt_Example.delete('1.0', END)
def refresh_all(self):
pass
def get_word(self):
return self.txt_WordOrPhrase.get('1.0', '1.0 lineend')
def get_explanation(self):
return self.txt_Explanation.get('1.0', '1.0 lineend')
def get_translation(self):
return self.txt_Translation.get('1.0', '1.0 lineend')
def get_example(self):
return self.txt_Example.get('1.0', '1.0 lineend')
def find_word(word):
for x in self.words:
if x.wordorphrase == word:
return x
def main():
root = Tk()
gui = Vocabulary(root)
root.protocol('WM_DELETE_WINDOW', gui.on_closing)
root.mainloop()
if __name__ == '__main__':
main()
For my listboxes, I generally have the mode set to EXTENDED so that users can select one or more items and remove all at once. I do so via the following method:
# Function with Tk.Listbox passed as arg
def remove_from(list_box):
# Tuple of currently selected items in arg
selected_items = list_box.curselection()
# Initialize a 'repositioning' variable
pos = 0
for item in selected_items:
# Set index of each item selected
idx = int(item) - pos
# Deletes only that index in the listbox
list_box.delete(idx, idx)
# Increments to account for shifts
pos += 1
For example, lets say in my listbox I had 4 items. I then select the first item and the third item. By calling list_box.curselection() I've received the following:
selected_items = (0, 2)
Where 0 is the first item's position in the listbox, and 2 is the third item's position. Then for each item in my tuple, I establish its index.
Walking through this, for the first item the following takes place:
idx = 0 - 0
list_box.delete(0, 0)
pos = 1
So now I have deleted the item at position 0 (e.g. the first item) and my listbox has shifted! So the second is now the first, third is the second and fourth is the third. However, my tuple has not changed as it was the positions in the listbox of the original selection. This is key. What happens next is:
idx = 2 - 1
list_box.delete(1, 1)
pos = 2
Since the listbox shifted, position 1 now corresponds to the item that was originally in the third position of the listbox. This can continue for n positions.
For self.words removal
You could try the following:
# Function with Tk.Listbox and self.words[] passed as args
def remove_from(list_box, list):
# Tuple of currently selected items in arg
selected_items = list_box.curselection()
# List of Words already constructed
word_list = list
# Initialize a 'repositioning' variable
pos = 0
for item in selected_items:
# Set index of each item selected
idx = int(item) - pos
# Deletes only that index in the listbox
list_box.delete(idx, idx)
# Gets the string value of the given index
word = list_box.get(idx, idx)
# Removes the word from the list
if any(word in x for x in word_list):
word_list.remove(word)
# Increments to account for shifts
pos += 1
Related
First. I create a group of 5 labels using a for
for (byte fila = 0; fila < 5; fila++)
{
Label letras = new Label();
letras.Height = alto;
letras.Width = ancho;
letras.BackColor = Color.Blue;
letras.ForeColor = Color.AntiqueWhite;
letras.Font = new Font("Arial", 60);
letras.TextAlign = ContentAlignment.MiddleCenter;
letras.BorderStyle = BorderStyle.FixedSingle;
letras.Left = 200 + (ancho + 20) * fila;
letras.Top = 60;
letras.Text = null;
letras.Visible = false;
letras.MouseMove += new
System.Windows.Forms.MouseEventHandler(this.letras_MouseMove);
this.Controls.Add(letras)
Second. I have a list of words declared as list of strings of 5 characters.
List <string> palabras = new List <string> { "limón", "bufón", "leche", "video",
"guiso",
"dulce", "gusto", "audaz", "vacío", "huevo", "avena", "guapo", "ozono",
"audio",
"zanja", "pausa", "freno", "débil", "hurto", "otoño", "libro", "pluma",
"disco",
"fruta", "melón", "papel", "mapeo", "nuevo", "banco", "tigre", "mundo",
"hueco",
"cable", "funda", "lápiz", "botón", "tonto", "regla", "tipas", "canal" };
I need to call one of them (words) and put each of the characters in a label of the group of labels I created first, in an altered order (5, 3, 1, 2, 4). I ´ve tried with substrings function but I always have errors. ¿Any suggestions?
string is basically an array of char, so you can access specific chars by their index, e.g.
string text = "Hello world!";
// this will write 'H'
Console.WriteLine(text[0]);
I have the following code which is supposed to add a bulleted list to a word document that I'm generating automatically. From other answers I believe the code is correct, but the result doesn't produce any bullet points at all, it doesn't seem to apply the indent either.
Any Ideas?
Microsoft.Office.Interop.Word.Paragraph assets;
assets = doc.Content.Paragraphs.Add(Type.Missing);
// Some code to generate the text
foreach (String asset in assetsList)
{
assetText = assetText + asset + "\n";
}
assets.Range.ListFormat.ApplyBulletDefault(Type.Missing);
// Add it to the document
assets.Range.ParagraphFormat.LeftIndent = -1;
assets.Range.Text = assetText;
assets.Range.InsertParagraphAfter();
This happens because you're adding multiple paragraphs to the range after the range (it seems that setting the Text property is equivalent to InsertAfter). You want to InsertBefore the range so that the formatting you set gets applied.
Paragraph assets = doc.Content.Paragraphs.Add();
assets.Range.ListFormat.ApplyBulletDefault();
string[] bulletItems = new string[] { "One", "Two", "Three" };
for (int i = 0; i < bulletItems.Length; i++)
{
string bulletItem = bulletItems[i];
if (i < bulletItems.Length - 1)
bulletItem = bulletItem + "\n";
assets.Range.InsertBefore(bulletItem);
}
Notice that we add an End of Paragraph mark to all items except the last one. You will get an empty bullet if you add one to the last.
This is based on Tergiver's answer. The difference is it inserts the list items in the correct order after the initially created paragraph. For your own use make the starting range equal to the item you want to insert the list after.
Paragraph assets = doc.Content.Paragraphs.Add();
rng = assets.Range;
rng.InsertAfter("\n");
start = rng.End;
end = rng.End;
rng = _oDoc.Range(ref start, ref end);
object listType = 0;
rng.ListFormat.ApplyBulletDefault(ref listType);
string[] bulletItems = new string[] { "One", "Two", "Three" };
for (int i = 0; i < bulletItems.Length; i++)
{
string bulletItem = bulletItems[i];
if (i < RowCount - 1)
bulletItem = bulletItem + "\n";
rng.InsertAfter(bulletItem);
}
Please note I don't really understand what I'm doing with the range here. This solution was arrived at after considerable trial and error. I suspect it may have to do with the fact that I'm reusing the same range and Tergiver's solution is grabbing a new range each time the range is accessed. I particularly don't understand the following lines:
rng.InsertAfter("\n");
start = rng.End;
end = rng.End;
rng = _oDoc.Range(ref start, ref end);
Generally any alterations to the above code and the list gets intermingled with the previous element. If somebody could explain why this works, I'd be grateful.
You can try below code block if you want list-sublist relations:
static void Main(string[] args)
{
try
{
Application app = new Application();
Document doc = app.Documents.Add();
Range range = doc.Range(0, 0);
range.ListFormat.ApplyNumberDefault();
range.Text = "Birinci";
range.InsertParagraphAfter();
ListTemplate listTemplate = range.ListFormat.ListTemplate;
//range.InsertAfter("Birinci");
//range.InsertParagraphAfter();
//range.InsertAfter("İkinci");
//range.InsertParagraphAfter();
//range.InsertAfter("Üçüncü");
//range.InsertParagraphAfter();
Range subRange = doc.Range(range.StoryLength - 1);
subRange.ListFormat.ApplyBulletDefault();
subRange.ListFormat.ListIndent();
subRange.Text = "Alt Birinci";
subRange.InsertParagraphAfter();
ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate;
Range subRange2 = doc.Range(subRange.StoryLength - 1);
subRange2.ListFormat.ApplyListTemplate(sublistTemplate);
subRange2.ListFormat.ListIndent();
subRange2.Text = "Alt İkinci";
subRange2.InsertParagraphAfter();
Range range2 = doc.Range(range.StoryLength - 1);
range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true);
WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate);
range2.Text = "İkinci";
range2.InsertParagraphAfter();
Range range3 = doc.Range(range2.StoryLength - 1);
range3.ListFormat.ApplyListTemplate(listTemplate);
range3.Text = "Üçüncü";
range3.InsertParagraphAfter();
string path = Environment.CurrentDirectory;
int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count();
path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1));
app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument);
doc.Close();
Process.Start(path);
}
catch (Exception exception)
{
throw;
}
}
Attention this point: If you don't know input length, you must not define the end of range value like this:
static void Main(string[] args)
{
try
{
Application app = new Application();
Document doc = app.Documents.Add();
Range range = doc.Range(0, 0);
range.ListFormat.ApplyNumberDefault();
range.Text = "Birinci";
range.InsertParagraphAfter();
ListTemplate listTemplate = range.ListFormat.ListTemplate;
//range.InsertAfter("Birinci");
//range.InsertParagraphAfter();
//range.InsertAfter("İkinci");
//range.InsertParagraphAfter();
//range.InsertAfter("Üçüncü");
//range.InsertParagraphAfter();
Range subRange = doc.Range(range.StoryLength - 1, range.StoryLength - 1);
subRange.ListFormat.ApplyBulletDefault();
subRange.ListFormat.ListIndent();
subRange.Text = "Alt Birinci";
subRange.InsertParagraphAfter();
ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate;
Range subRange2 = doc.Range(subRange.StoryLength - 1, range.StoryLength - 1);
subRange2.ListFormat.ApplyListTemplate(sublistTemplate);
subRange2.ListFormat.ListIndent();
subRange2.Text = "Alt İkinci";
subRange2.InsertParagraphAfter();
Range range2 = doc.Range(range.StoryLength - 1, range.StoryLength - 1);
range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true);
WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate);
range2.Text = "İkinci";
range2.InsertParagraphAfter();
Range range3 = doc.Range(range2.StoryLength - 1, range.StoryLength - 1);
range3.ListFormat.ApplyListTemplate(listTemplate);
range3.Text = "Üçüncü";
range3.InsertParagraphAfter();
string path = Environment.CurrentDirectory;
int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count();
path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1));
app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument);
doc.Close();
Process.Start(path);
}
catch (Exception exception)
{
throw;
}
}
You just need to keep track of the start and end positions of the list and then apply the list format.
Application wordApp = new Application() {
Visible = true
};
Document doc = wordApp.Documents.Add();
Range range = doc.Content;
range.Text = "Hello world!";
range.InsertParagraphAfter();
range = doc.Paragraphs.Last.Range;
// start of list
int startOfList = range.Start;
// each \n character adds a new paragraph...
range.Text = "Item 1\nItem 2\nItem 3";
// ...or insert a new paragraph...
range.InsertParagraphAfter();
range = doc.Paragraphs.Last.Range;
range.Text = "Item 4\nItem 5";
// end of list
int endOfList = range.End;
// insert the next paragraph before applying the format, otherwise
// the format will be copied to the suceeding paragraphs.
range.InsertParagraphAfter();
// apply list format
Range listRange = doc.Range(startOfList, endOfList);
listRange.ListFormat.ApplyBulletDefault();
range = doc.Paragraphs.Last.Range;
range.Text = "Bye for now!";
range.InsertParagraphAfter();
I have a dictionary collection that stores the starting position and the charecter values of a text file.
For example: a sample text file (a.txt) may contain text like "how are you? how do you do?"
I have indexed the above text as follows
Dictionary<long,string> charLocation = new Dictionary<long,string>();
charLocation[0] = "how"
charLocation[1] = "ow"
charLocation[2] = "w"
charLocation[4] = "are"
charLocation[6] = "e"
charLocation[5] = "re"
charLocation[11] = "?"
charLocation[9] = "ou?"
charLocation[10] = "u?"
charLocation[8] = "you?"
charLocation[13] = "how"
charLocation[14] = "ow"
charLocation[15] = "w"
charLocation[17] = "do"
charLocation[18] = "o"
charLocation[21] = "ou"
charLocation[22] = "u"
charLocation[20] = "you"
charLocation[26] = "?"
charLocation[24] = "do?"
charLocation[25] = "o?"
Now, I want to highlight each occurrence of "how" or "do" in the text file.
For this I want to first do a lookup in the dictionary collection and find each occurrence of the string, then open the text file and highlight the text for each occurrence.
How can i do this?
Not tested, but this should works.
public string HighLight (int startPoint, string text, string word)
{
if (startPoint > = 0)
{
int startIndex = text.indexOf (word, startPoint);
if (startIndex >= 0)
{
StringBuilder builder = new StringBuilder ();
builder.Append (text.Substring ( 0, startIndex));
builder.Append ("<strong>");
builder.Append (text.Substring (startIndex + 1, word.Length));
builder.Append ("</strong>");
builder.Append (text.Substring (startIndex + word.Length + 1));
return HighLight ((startIndex + "<strong>".Length + "</strong>".Length + word.Length, builder.ToString (), word);
}
}
//Word not found.
return text;
}
So you could do :
string myText = "how are you? how do you do?";
string hightLightedText = HighLight (0, myText, "how");
And if my code has no errors, that would returns "<strong>how</strong> are you? <strong>how</strong> do you do?";
Then you can repalce <strong> and </strong> with wathever you want to "highlight" your text.
I want to have mutple format types in one cell in my workbook. For example I want my A1 cell to display " Name: Aaron Kruger ". When I programmatically add the name "Aaron Kruger" to the template, it automatically makes it bold. So instead it looks like this " Name:Aaron Kruger ". So I want Bold and non-bold both in the same cell. And maybe in the future I will want two different text sizes in the same cell.
Thanks,
Aaron Kruger
Here is the function I created:
public void inputData(int row, int column, string cellName, System.Windows.Forms.TextBox textBox, Excel.Worksheet sheet)
{
sheet.Cells[row, column] = sheet.get_Range(cellName, Type.Missing).Text + " " + textBox.Text; // adds value to sheet
}
Here are the arguments I pass in:
inputData(5, 1, "A5", tbTagNumber, xlSheet);
inputData(6, 1, "A6", tbCustomer, xlSheet);
inputData(7, 1, "A5", tbDataFile, xlSheet);
inputData(3, 6, "F3", tbJobNumber, xlSheet);
inputData(4, 6, "F4", tbMeterSN, xlSheet);
inputData(6, 6, "F6", tbPO, xlSheet);
inputData(7, 6, "F7", tbFlowplate, xlSheet);
inputData(4, 9, "I4", tbElectronicSN, xlSheet);
Range rng1 = ws.getRange("A1","E10");
for(int i=0;i<10;i++)
{
Range rngTst=rng.cells[i,i];
for(int j=0;j<rngTst.get_characters().count;j++)
{
rngTst.application.activecell.get_characters(j,j).font.color
}
}
or
int sFirstFoundAddress = currentFind.FormulaR1C1Local.ToString().IndexOf("NOT FOR CIRCULATION ");
get_Range(excel.Cells[1, 1],
excel.Cells[1, dtData.Columns.Count])
.get_Characters(sFirstFoundAddress, 20).Font.Color =
System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
Use Microsoft.Office.Interop.Excel.Range which gives you each character as Characters type. Now use its Font and other properties to style them.
Refer an example here: http://www.bloodforge.com/post/Extract-Formatted-Text-From-Excel-Cell-With-C.aspx
Microsoft.Office.Interop.Excel.Range Range = (Microsoft.Office.Interop.Excel.Range)Cell;
int TextLength = Range.Text.ToString().Length;
for (int CharCount = 1; CharCount <= TextLength; CharCount++)
{
Microsoft.Office.Interop.Excel.Characters charToTest = Range.get_Characters(CharCount, 1);
bool IsBold = (bool)charToTest.Font.Bold;
bool IsItalic = (bool)charToTest.Font.Italic;
// other formatting tests here
}
I recorded a macro, it shouldn't be hard to translate it to C#:
ActiveCell.FormulaR1C1 = "Test test"
Range("A1").Select
ActiveCell.FormulaR1C1 = "Test test"
With ActiveCell.Characters(Start:=1, Length:=5).Font
.Name = "Calibri"
.FontStyle = "Regular"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
With ActiveCell.Characters(Start:=6, Length:=4).Font
.Name = "Calibri"
.FontStyle = "Bold"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
I have 3 strings. The first set of strings are:
"1.0536"
"2.1"
"2"
The second is something like:
"Round"
"Square"
"Hex"
And the last are:
"6061-T6"
"T351"
"ASF.3.4.5"
I need to combine the three strings together with identical spacing in between each string. I can't use \t for tabbing as after I combine the strings, I send them to an Access Database.
When I combine the strings they look like:
"1.0536 Round 6061-T6"
"2.1 Square T351"
"2 Hex ASF.3.4.5"
I would really like them to look like this with the same exact amount of spacing in between each string:
"1.0536 Round 6061-T6"
"2.1 Square T351"
"2 Hex ASF.3.4.5"
How can I do this with C#?
You can use advanced features of string.Format:
string.Format("{0,-10}{1,-10}{2}", ...)
You can do the same thing by writing str.PadRight(10)
If you know the maximum lengths of each column then do the following:
String result = String.Format("{0} {1} {2}", strCol1.PadRight(10), strCol2.PadRight(9), strCol3.PadRight(9));
To make life easier, utility methods:
Usage
var data = new[] {
new[] { "ID", "NAME", "DESCRIPTION" },
new[] { "1", "Frank Foo", "lorem ipsum sic dolor" },
new[] { "2", "Brandon Bar", "amet forthrightly" },
new[] { "3", "B. Baz", "Yeehah!" }
};
var tabbedData = EvenColumns(20, data);
var tabbedData2 = string.Join("\n", EvenColumns(20, false, data)); // alternate line separator, alignment
Results
ID NAME DESCRIPTION
1 Frank Foo lorem ipsum sic dolor
2 Brandon Bar amet forthrightly
3 B. Baz Yeehah!
ID NAME DESCRIPTION
1 Frank Foolorem ipsum sic dolor
2 Brandon Bar amet forthrightly
3 B. Baz Yeehah!
Code
public string EvenColumns(int desiredWidth, IEnumerable<IEnumerable<string>> lists) {
return string.Join(Environment.NewLine, EvenColumns(desiredWidth, true, lists));
}
public IEnumerable<string> EvenColumns(int desiredWidth, bool rightOrLeft, IEnumerable<IEnumerable<string>> lists) {
return lists.Select(o => EvenColumns(desiredWidth, rightOrLeft, o.ToArray()));
}
public string EvenColumns(int desiredWidth, bool rightOrLeftAlignment, string[] list, bool fitToItems = false) {
// right alignment needs "-X" 'width' vs left alignment which is just "X" in the `string.Format` format string
int columnWidth = (rightOrLeftAlignment ? -1 : 1) *
// fit to actual items? this could screw up "evenness" if
// one column is longer than the others
// and you use this with multiple rows
(fitToItems
? Math.Max(desiredWidth, list.Select(o => o.Length).Max())
: desiredWidth
);
// make columns for all but the "last" (or first) one
string format = string.Concat(Enumerable.Range(rightOrLeftAlignment ? 0 : 1, list.Length-1).Select( i => string.Format("{{{0},{1}}}", i, columnWidth) ));
// then add the "last" one without Alignment
if(rightOrLeftAlignment) {
format += "{" + (list.Length-1) + "}";
}
else {
format = "{0}" + format;
}
return string.Format(format, list);
}
Specific to the Question
// for fun, assume multidimensional declaration rather than jagged
var data = new[,] {
{ "1.0536", "2.1", "2" },
{ "Round", "Square", "Hex" },
{ "6061-T6", "T351", "ASF.3.4.5" },
};
var tabbedData = EvenColumns(20, Transpose(ToJaggedArray(data)));
with Transpose:
public T[][] Transpose<T>(T[][] original) {
// flip dimensions
var h = original.Length;
var w = original[0].Length;
var result = new T[h][];
for (var r = 0; r < h; r++) {
result[r] = new T[w];
for (var c = 0; c < w; c++)
{
result[r][c] = original[c][r];
}
}
return result;
}
And multidimensional arrays (source):
public T[][] ToJaggedArray<T>(T[,] multiArray) {
// via https://stackoverflow.com/questions/3010219/jagged-arrays-multidimensional-arrays-conversion-in-asp-net
var h = multiArray.GetLength(0);
var w = multiArray.GetLength(1);
var result = new T[h][];
for (var r = 0; r < h; r++) {
result[r] = new T[w];
for (var c = 0; c < w; c++) {
result[r][c] = multiArray[r, c];
}
}
return result;
}
I know this has long since been answered, but there is a new way as of C# 6.0
string[] one = new string[] { "1.0536", "2.1", "2" };
string[] two = new string[] { "Round", "Square", "Hex" };
string[] three = new string[] { "1.0536 Round 6061-T6", "2.1 Square T351", "2 Hex ASF.3.4.5" };
for (int i = 0; i < 3; i++) Console.WriteLine($"{one[i],-10}{two[i],-10}{three[i],-10}");
The $"{one[i],-10}{two[i],-10}{three[i],-10}" is the new replacement for string.format . I have found it very useful in many of my projects. Here is a link to more information about string interpolation in c# 6.0:
https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation
Use String.Format("{0,10}", myString)
Where 10 is the number of characters you want
To do it more dynamically you could do something like this: (hardcoding ahead!)
int padding = 3;
int maxCol0width = "Hello World!".Length;
int maxCol1width = "90.345".Length;
int maxCol2width = "value".Length;
string fmt0 = "{0,-" + (maxCol0width + padding) + "}";
string fmt1 = "{1,-" + (maxCol1width + padding) + "}";
string fmt2 = "{2,-" + (maxCol2width + padding) + "}";
string fmt = fmt0 + fmt1 + fmt2;
Console.WriteLine(fmt, "Hello World!", 90.345, "value");
Console.WriteLine(fmt, "Hi!", 1.2, "X");
Console.WriteLine(fmt, "Another", 100, "ZZZ");
You will of course need to figure out your max word widths by looping through each column's values. Also the creation of the format string could be significantly cleaned up and shortened.
Also note that you will need to use a non-proportional font for display, otherwise your columns will still not line up properly. Where are you displaying this data? There may be better ways of getting tabular output.