I am reading a file for school and I am trying to ignore accents.
I used CultureInfo but it doesn't work for me is there another way??
(example .... Clémentine = clementine)
public static void SearchName()
{
string lineIn = String.Empty;
string[] BoatInfo = new string[5];
Console.WriteLine();
FileStream fs = new FileStream(#"FrenchMF.txt", FileMode.Open, FileAccess.Read);
StreamReader inputStream = new StreamReader(fs);
lineIn = inputStream.ReadLine();
string input = String.Empty;
Console.WriteLine();
Console.Write("Enter Vessel Name :");
input = Console.ReadLine().ToLower().Trim();
string CheckInput = String.Empty;
while(lineIn != null)
{
BoatInfo = lineIn.Split(',');
CheckInput = BoatInfo[0].ToLower();
if (input.Equals(CheckInput))
{
Console.WriteLine("its a Match" );
}
else
{
Console.WriteLine("No Match Found!! ");
return;
}
}
}
You can create an extension method for string:
public static string RemoveDiacritics(this string #this) {
var normalizedString = #this.Normalize(System.Text.NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString) {
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != UnicodeCategory.NonSpacingMark) {
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
And then you can say input.RemoveDiacritics().
In order for the extension method to work, you must put it in a static class:
public static class ScorableExtensions {
public static string RemoveDiacritics(this string #this) {
//the one above
}
}
string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace);
See this answer
Of course, you can create your own CultureInfo - you do not have to use the CurrentCulture:
string.Compare(s1, s2, new CultureInfo("fr-FR"), CompareOptions.IgnoreNonSpace);
Related
static void Main(string[] args) {
var file = File.Open(Directory.GetCurrentDirectory() + "/Net.pdf", FileMode.Open);
var pattern = new Regex("kullan", RegexOptions.IgnoreCase);
//this line is not working
TextExtractor textExtractor = new TextExtractor();
var dddd = ReadToEnd(file);
var textStrings = textExtractor.Extract(dddd);
var matches = pattern.Matches(textStrings.Text);
foreach (var item in matches)
{
Console.WriteLine(item);
}
}
You can try something like this:
File file = new File(myPDF);
//pattern
var pattern = new Regex("kullan", RegexOptions.IgnoreCase);
var textExtractor = new TextExtractor();
foreach (var page in file.Document.Pages)
{
var strings = textExtractor.Extract(page);
var matchingText = pattern.Matches(TextExtractor.ToString(strings));
}
Tika:
try
{
var result = new TextExtractor().Extract(yourPDF.pdf).Text;
Console.WriteLine(result.Text.Length);
foreach(var line in result)
{
if(line.contains("kullen"))
/** Do Something **/
}
}
catch(Exception e)
{
Console.WriteLine("Error occurred: " + e);
}
I did it like that, thank you your answer
namespace pro
{
class Program
{
static void Main(string[] args)
{
string b = pdfText(Directory.GetCurrentDirectory()+ "/Net.pdf");
string a= "kullan";
int sonuc;
sonuc = b.IndexOf(a,0, b.Length);
if(sonuc==-1)
{
Console.WriteLine("not found");
}
else
{
Console.WriteLine("found from " + sonuc.ToString() + ". character");
}
}
public static string pdfText(string path)
{
PdfReader reader = new PdfReader(path);
var dd = reader.GetPageContent(1);
string text = string.Empty;
for (int page = 1; page <= reader.NumberOfPages; page++)
{
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
text += PdfTextExtractor.GetTextFromPage(reader, page);
}
reader.Close();
return text;
}
}
}
I Want to develope an app I give Url of a specific website to it,and it extract all links from that Web page. For each extracted link I want to get the HTML content. I am based in the concept of deep crawling.
My purpose is to get all email addresses of website. Below is my source code:
static string ExtractEmails(string data)
{
//instantiate with this pattern
Regex emailRegex = new Regex(#"\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
//find items that matches with our pattern
MatchCollection emailMatches = emailRegex.Matches(data);
//StringBuilder sb = new StringBuilder();
string s = "";
foreach (Match emailMatch in emailMatches)
{
//sb.AppendLine(emailMatch.Value);
s += emailMatch.Value + ",";
}
return s;
}
static readonly List<ParsResult> _results = new List<ParsResult>();
static Int32 _maxDepth = 4;
static String Foo(String urlToCheck = null, Int32 depth = 0, ParsResult parent = null)
{
string email = "";
if (depth >= _maxDepth) return email;
String html;
using (var wc = new WebClient())
html = wc.DownloadString(urlToCheck ?? parent.Url);
var doc = new HtmlDocument();
doc.LoadHtml(html);
var aNods = doc.DocumentNode.SelectNodes("//a");
if (aNods == null || !aNods.Any()) return email;
foreach (var aNode in aNods)
{
var url = aNode.Attributes["href"];
if (url == null)
continue;
var wc2 = new WebClient();
String html2 = wc2.DownloadString(url.Value);
email = ExtractEmails(html2);
Console.WriteLine(email);
var result = new ParsResult
{
Depth = depth,
Parent = parent,
Url = url.Value
};
_results.Add(result);
Console.WriteLine("{0} - {1}", depth, result.Url);
Foo(depth: depth + 1, parent: result);
return email;
}
return email;
}
static void Main(string[] args)
{
String res = Foo("http://www.mobileridoda.com", 0);
Console.WriteLine("emails " + res);
}
I want to dispaly in console all emails extracted by all pages of all links that are inside DOM of Main page, But it dispalys no emails in console. How can I solve this issue ?
Thank you
Found a few things wrong but no worries, got the details on why and what to do to fix them.
In your foreach loop, when you go through the first URL, you are using a return statement at the end essentially breaking the loop and terminating. Use return only after you have processed ALL the URLs and accumulated the email addresses.
You are overwriting the email (i see it as a csv) when you go over the loop. Use += to continue adding. email = ExtractEmails(html2);
You are not returning anything when you call Foo within your forEach loop. You need to use email += Foo(xyz). Foo(depth: depth + 1, parent: result);
You are going through a URL that you have already processed... possibly causing an infinite cycle. I added a list of strings that keeps track of URLs you have already visited so as to prevent the infinite loop you might get into.
Here is a complete working solution.
static string ExtractEmails(string data)
{
//instantiate with this pattern
Regex emailRegex = new Regex(#"\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
//find items that matches with our pattern
MatchCollection emailMatches = emailRegex.Matches(data);
//StringBuilder sb = new StringBuilder();
string s = "";
foreach (Match emailMatch in emailMatches)
{
//sb.AppendLine(emailMatch.Value);
s += emailMatch.Value + ",";
}
return s;
}
static readonly List<ParsResult> _results = new List<ParsResult>();
static Int32 _maxDepth = 4;
static List<string> urlsAlreadyVisited = new List<string>();
static String Foo(String urlToCheck = null, Int32 depth = 0, ParsResult parent = null)
{
if (urlsAlreadyVisited.Contains(urlToCheck))
return string.Empty;
else
urlsAlreadyVisited.Add(urlToCheck);
string email = "";
if (depth >= _maxDepth) return email;
String html;
using (var wc = new WebClient())
html = wc.DownloadString(urlToCheck ?? parent.Url);
var doc = new HtmlDocument();
doc.LoadHtml(html);
var aNods = doc.DocumentNode.SelectNodes("//a");
if (aNods == null || !aNods.Any()) return email;
// Get Distinct URLs from all the URls on this page.
List<string> allUrls = aNods.ToList().Select(x => x.Attributes["href"].Value).Where(url => url.StartsWith("http")).Distinct().ToList();
foreach (string url in allUrls)
{
var wc2 = new WebClient();
try
{
email += ExtractEmails(wc2.DownloadString(url));
}
catch { /* Swallow Exception ... URL not found or other errors. */ continue; }
Console.WriteLine(email);
var result = new ParsResult
{
Depth = depth,
Parent = parent,
Url = url
};
_results.Add(result);
Console.WriteLine("{0} - {1}", depth, result.Url);
email += Foo(depth: depth + 1, parent: result);
}
return email;
}
public class ParsResult
{
public int Depth { get; set; }
public ParsResult Parent { get; set; }
public string Url { get; set; }
}
// ========== MAIN CLASS ==========
static void Main(string[] args)
{
String res = Foo("http://www.mobileridoda.com", 0);
Console.WriteLine("emails " + res);
}
First:I have to make a statistics from my csv( https://pastebin.com/jxNSzVYP ) by province. Somewhere you can find a province like this "HU-GD" then you have to see that like "HU" so first 2 letters. And i have to count how many SI/GA/etc province are there. After, i have to write out, when it is at least 3. (If the csv changes, i mean there will no more HU/SI/ etc, and there will a new one, the program have to count that.) [Check: Describe expected result]
Second: I have to make a new "newCNtunnels.csv" file. When there is a province like this "HU-GD" i have to separete them and write them with same datas, just the province is different. [Check: Describe expected result]
I am not a big programmer, so please do not over complicate this.
Sorry for my English. It is not my first language, but probably you have already noticed this.
Thanks for the help!
I am using windows 10 and visual studio. C# language
struct datas
{
public string name;
public int length;
public string date;
public string province;
}
datas[] tunnel = new datas[99];
int i = 0;
int howmanyrow= 0;
StreamReader sr = new StreamReader("CNtunnels.csv");
sr.ReadLine();
while (!sr.EndOfStream)
{
String[] onerow= sr.ReadLine().Split(';');
tunnel[i].name= onerow[0];
tunnel[i].length= Convert.ToInt32(onerow[1]);
tunnel[i].date= onerow[2];
tunnel[i].province= onerow[3];
i++;
howmanyrow++;
}
sr.Close();
Excepted results:
https://pastebin.com/EsQz16A0
See following solution :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string INPUT_CSV_FILENAME = #"c:\temp\test.csv";
const string OUTPUT_CSV_FILENAME = #"c:\temp\test1.csv";
const string OUTPUT_STATISTICS_FILENAME = #"c:\temp\statistic.txt";
static void Main(string[] args)
{
Data data = new Data(INPUT_CSV_FILENAME);
data.WriteStatistics(OUTPUT_STATISTICS_FILENAME);
data.WriteCSV(OUTPUT_CSV_FILENAME);
}
}
class Data
{
public static List<Data> datas = new List<Data>();
public string name;
public int length;
public string date;
public string province;
public Data() { }
public Data(string filename)
{
StreamReader reader = new StreamReader(filename);
string line = "";
int rowCount = 0;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
string[] splitArray = line.Split(new char[] { ';' });
if (++rowCount > 1)
{
string[] splitProvidence = splitArray[3].Split(new char[] { '-' });
foreach (string providence in splitProvidence)
{
Data newRow = new Data();
Data.datas.Add(newRow);
newRow.name = splitArray[0];
newRow.length = int.Parse(splitArray[1]);
newRow.date = splitArray[2];
newRow.province = providence;
}
}
}
}
}
public void WriteStatistics(string filename)
{
StreamWriter writer = new StreamWriter(filename);
var groups = datas.GroupBy(x => x.province).OrderByDescending(x => x.Count()).ToList();
foreach (var group in groups)
{
writer.WriteLine("{0} - {1}", group.Key, group.Count());
}
writer.Flush();
writer.Close();
}
public void WriteCSV(string filename)
{
StreamWriter writer = new StreamWriter(filename);
string header = string.Join(";", new string[] { "name","length","date","province"});
writer.WriteLine(header);
foreach (Data data in datas)
{
writer.WriteLine(string.Join(";",new string[] {
data.name,
data.length.ToString(),
data.date,
data.province
}));
}
writer.Flush();
writer.Close();
}
}
}
I'm trying to find executable files for games; however some are nested (For example Ark: Survival Evolved) (A:\Steam Games\steamapps\common\ARK\ShooterGame\Binaries\Win64\ShooterGame.exe)
I've spent ages trying to find a way to only find the executables which are relevant.
This link shows games when we don't search recursively. It finds most, but not all .exe's
This link shows games searching recursively, but also shows a bunch of binaries/redist exes.
Initially I tried excluding "bin","binary","binaries","redist" folders but that then didn't give me Ark: Survival Evolved for example.
I also considered filtering the .exe's based on their size, but Aperture Tag has a QC_Eyes.exe at 3055KB, but Tomb Raider II.exe is 892KB.
Here's the method I'm using to find the steam installation directory, and check the libraryfolders.vdf file for where the library locations are. For now I'm just writing to console so that I can see what the outputs are.
If anyone has any tips on how I can find the right files for the right games it would be much appreciated. Thanks
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\\VALVE\\";
string steam64 = "SOFTWARE\\Wow6432Node\\Valve\\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
foreach(string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach(var item in configLines)
{
Console.WriteLine("32: " + item);
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = #"[A-Z]:\\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\\\", "\\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\\steamapps\\common\\");
}
}
}
foreach(string item in steamGameDirs)
{
string GameTitle;
string[] Executables = new string[0];
string[] steamGames = Directory.GetDirectories(item);
foreach (var dir in steamGames)
{
string title = dir.Substring(dir.IndexOf("\\common\\"));
string[] titlex = title.Split('\\');
title = titlex[2].ToString();
GameTitle = title;
Console.WriteLine("Title: " + GameTitle);
Console.WriteLine("Directory: " + dir);
string[] executables = Directory.GetFiles(dir, "*.exe", SearchOption.AllDirectories);
int num = 0;
foreach (var ex in executables)
{
//add "ex" to Executables[] if poss
Console.WriteLine(ex);
num++;
}
}
}
}
I've managed to do what I can, so first I detect where steam is installed through the registry, then I check /steamapps/libraryfolders.vdf for where the users libraries are, then check those libraries for any "top level" executables. The program then lets the user select their own exe if one isn't found in the top directory.
Seems like the best solution for now as the steamfiles module isn't currently active.
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\\VALVE\\";
string steam64 = "SOFTWARE\\Wow6432Node\\Valve\\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
if (key64.ToString() == null || key64.ToString() == "")
{
foreach (string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
string driveRegex = #"[A-Z]:\\";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach (var item in configLines)
{
Console.WriteLine("32: " + item);
Match match = Regex.Match(item, driveRegex);
if (item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\\\", "\\");
item2 = item2.Replace("\"", "\\steamapps\\common\\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam32path + "\\steamapps\\common\\");
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = #"[A-Z]:\\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\\\", "\\");
item2 = item2.Replace("\"", "\\steamapps\\common\\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\\steamapps\\common\\");
}
}
}
}
Attached the code so others can see how I've done it. I know it's not the best but it's working
The keydata seems to be in the appinfo.vdf file. So a somewhat working code to get the correct EXE file for steam games is as follows. Where the game is an EA game with a pointer to an URL or there is no data in the appinfo.vdf a fallback to your previous solution could be used. If anyone got a good parser of the appinfo.vdf the code would be better and not as hacky as the "IndexOf" solution.
GetSteamPath() in the below code can be updated to fetch from registry.
I am going to use that code to fix those petchy steam links that goes back to that anonymous globe-url icon.
(Written in .Net 5.0 - seems IndexOf for .4.7.2 is quirky. doesnt like \x00 I think)
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace SteamAppsParser
{
class Program
{
static void Main(string[] args)
{
var libs = GetSteamLibs();
var apps = GetSteamApps(libs);
}
static List<AppInfo> GetSteamApps(List<string> steamLibs)
{
var apps = new List<AppInfo>();
foreach (var lib in steamLibs)
{
var appMetaDataPath = Path.Combine(lib, "SteamApps");
var files = Directory.GetFiles(appMetaDataPath, "*.acf");
foreach (var file in files)
{
var appInfo = GetAppInfo(file);
if (appInfo != null)
{
apps.Add(appInfo);
}
}
}
return apps;
}
static AppInfo GetAppInfo(string appMetaFile)
{
var fileDataLines = File.ReadAllLines(appMetaFile);
var dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var line in fileDataLines)
{
var match = Regex.Match(line, #"\s*""(?<key>\w+)""\s+""(?<val>.*)""");
if (match.Success)
{
var key = match.Groups["key"].Value;
var val = match.Groups["val"].Value;
dic[key] = val;
}
}
AppInfo appInfo = null;
if (dic.Keys.Count > 0)
{
appInfo = new AppInfo();
var appId = dic["appid"];
var name = dic["name"];
var installDir = dic["installDir"];
var path = Path.GetDirectoryName(appMetaFile);
var libGameRoot = Path.Combine(path, "common", installDir);
if (!Directory.Exists(libGameRoot)) return null;
appInfo.Id = appId;
appInfo.Name = name;
appInfo.Manifest = appMetaFile;
appInfo.GameRoot = libGameRoot;
appInfo.InstallDir = installDir;
appInfo.SteamUrl = $"steam://runsteamid/{appId}";
//if (appInfo.Name.StartsWith("Sid Meier"))
appInfo.Executable = GetExecutable(appInfo);
}
return appInfo;
}
static string _appInfoText = null;
static string GetExecutable(AppInfo appInfo)
{
if (_appInfoText == null)
{
var appInfoFile = Path.Combine(GetSteamPath(), "appcache", "appinfo.vdf");
var bytes = File.ReadAllBytes(appInfoFile);
_appInfoText = Encoding.UTF8.GetString(bytes);
}
var startIndex = 0;
int maxTries = 50;
var fullName = "";
do
{
var startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex);
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01gamedir\x00{appInfo.Name}\x00", startIndex); //Alternative1
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex); //Alternative2
if (startOfDataArea > 0)
{
startIndex = startOfDataArea + 10;
int nextLaunch = -1;
do
{
var executable = _appInfoText.IndexOf($"\x00\x01executable\x00", startOfDataArea);
if (executable>-1 && nextLaunch == -1)
{
nextLaunch = _appInfoText.IndexOf($"\x00\x01launch\x00", executable);
}
if ((nextLaunch <= 0 || executable < nextLaunch) && executable > 0)
{
if (executable > 0)
{
executable += 10;
string filename = "";
while (_appInfoText[executable] != '\x00')
{
filename += _appInfoText[executable];
executable++;
}
if (filename.Contains("://"))
{
//EA or other external
return filename; //Need to use other means to grab the EXE here.
}
fullName = Path.Combine(appInfo.GameRoot, filename);
startOfDataArea = executable + 1;
startIndex = startOfDataArea + 10;
}
}
else
{
break;
}
}
while (!File.Exists(fullName) && maxTries-- > 0);
}
else
{
return null;
}
} while (!File.Exists(fullName) && maxTries-- > 0);
if (File.Exists(fullName)) return fullName;
return null;
}
static List<string> GetSteamLibs()
{
var steamPath = GetSteamPath();
var libraries = new List<string>() { steamPath };
var listFile = Path.Combine(steamPath, #"steamapps\libraryfolders.vdf");
var lines = File.ReadAllLines(listFile);
foreach (var line in lines)
{
var match = Regex.Match(line, #"""(?<path>\w:\\\\.*)""");
if (match.Success)
{
var path = match.Groups["path"].Value.Replace(#"\\", #"\");
if (Directory.Exists(path))
{
libraries.Add(path);
}
}
}
return libraries;
}
static string GetSteamPath()
{
return #"C:\Spill\Steam";
}
class AppInfo
{
public string Id { get; internal set; }
public string Name { get; internal set; }
public string SteamUrl { get; internal set; }
public string Manifest { get; internal set; }
public string GameRoot { get; internal set; }
public string Executable { get; internal set; }
public string InstallDir { get; internal set; }
public override string ToString()
{
return $"{Name} ({Id}) - {SteamUrl} - {Executable}";
}
}
}
}
I am trying to format a string lets say
"Hello george, how are you?"
I just want "george" in red color. Is there any way I can use String.Format and FormattedString side by side?
I tried using:
var text = new FormattedString();
text.Spans.Add(new Span {
Text = Localize.GetString("irs", String.Empty),
ForegroundColor = Colors.RedColor
});
label.FormattedText = String.Format(
Localize.GetString("instructions", String.Empty),
text
);
However this does not work. Is there any proper way to actually do this. I want localization so I don't want to split the text into multiple localization strings.
You can use an intercept provider in string.Format.
For illustrative purposes, I choose XAML format as output, and then convert it into FormattedString (but more concise formats like JSON can also be used).
First, let's implement the intercept provider - which will convert your string.Format's output into Span(s):
public class InterceptProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(String format, Object obj, IFormatProvider provider)
{
string spanText;
// Use default for all other formatting.
if (obj is IFormattable)
spanText = ((IFormattable)obj).ToString(format, System.Globalization.CultureInfo.CurrentCulture);
else
spanText = obj.ToString();
return $"</Span><Span ForegroundColor=\"Red\">{spanText}</Span><Span>";
}
}
Add an extension method to integrate the interceptor with string.Format.
public static class FormatExtensions
{
static ColorTypeConverter _typeConverter = new ColorTypeConverter();
static InterceptProvider _interceptor = new InterceptProvider();
public static string InterceptFormat(this string sourceStr, params object[] args)
{
return $"<FormattedString><Span>{string.Format(_interceptor, sourceStr, args)}</Span></FormattedString>";
}
And, finally an helper method that converts XAML to FormattedString object.
public static FormattedString ToFormattedString(this string xamlStr)
{
var toReturn = new FormattedString();
if (string.IsNullOrWhiteSpace(xamlStr))
return toReturn;
Span span = null;
using(var strReader = new StringReader(xamlStr))
{
using(var xmlReader = XmlReader.Create(strReader))
{
while (xmlReader.Read())
{
if (xmlReader.IsStartElement())
{
switch (xmlReader.Name)
{
case "Span":
span = new Span();
while (xmlReader.MoveToNextAttribute())
{
switch (xmlReader.Name)
{
case "ForegroundColor":
var color = xmlReader.Value;
if (!string.IsNullOrEmpty(color))
span.ForegroundColor = (Color)_typeConverter.ConvertFromInvariantString(color);
break;
}
}
if (xmlReader.IsStartElement() || xmlReader.MoveToContent() == XmlNodeType.Element)
{
span.Text = xmlReader.ReadString();
toReturn.Spans.Add(span ?? new Span());
}
break;
}
}
}
}
}
return toReturn;
}
USAGE:
label.FormattedText = Localize.GetString("instructions", String.Empty)
.InterceptFormat(text).ToFormattedString();
Or,
lbl.FormattedText = "{0} It is now {1:d} at {1:t}"
.InterceptFormat("Morning!", DateTime.Now)
.ToFormattedString();