void FormatLabelTextToWrapLayout(WrapLayout wrapLayout, string text, Color color, FontAttributes fontAttributes = FontAttributes.None, LineBreakMode lineBreakMode = LineBreakMode.NoWrap, TapGestureRecognizer tap = null, string urlImg = null) { foreach (var line in text.Trim().Split(' ')) { if (string.IsNullOrWhiteSpace(line)) { continue; } if (text == "img") { Image img = new Image(); img.Source = urlImg; if (tap != null) { img.GestureRecognizers.Add(tap); } wrapLayout.Orientation = StackOrientation.Vertical; wrapLayout.Children.Add(img); } else { Label lb = new Label() { FontAttributes = fontAttributes, LineBreakMode = lineBreakMode, FontSize = PlatformInvoke.IsWindows ? 15 : 16, TextColor = color, Text = line }; if (tap != null) { lb.GestureRecognizers.Add(tap); } wrapLayout.Children.Add(lb); } } }
void RenderText_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (IsRenderer || MessageHtml == null) { return; } IsRenderer = true; // Старт string SplitBbCode = "_SplitBbCode_"; Color textColor = Color.FromHex("#464646"); #region Начальный текст - (вырезаем весь код форматирования текста и заменяем часть кода на свой) string message_html = Regex.Replace(MessageHtml, "(<br />|<a [^>]+>([^<]+|<[^<]+>|<[a-z] +[^<]+</[a-z]>)</a>|<img [^>]+>|<[^>]+>|\\<|\\>|\\ |\\")", rg => { // Форматирование switch (rg.Groups[0].Value) { case """: return("\""); case "<": return("<"); case ">": return(">"); case "</p>": case "</li>": case "</code>": case "<hr>": case "<br>": case "<br />": return("<br />"); case "<em>": return($"{SplitBbCode}em:"); case "<strong>": return($"{SplitBbCode}strong:"); case "</em>": case "</strong>": return($"{SplitBbCode}"); } // Текста в <h*>*</h*> if (Regex.IsMatch(rg.Groups[0].Value, "</h[0-9]+>")) { return("<br />"); } // Изображение if (rg.Groups[0].Value.Contains("src=")) { return($"{SplitBbCode}img:{new Regex("src=\"([^\"]+)\"").Match(rg.Groups[0].Value).Groups[1].Value}{SplitBbCode}"); } // Ссылка if (rg.Groups[0].Value.Contains("href=")) { var gLink = new Regex("href=\"([^\"]+)\"([^>]+)?>(.*)</a>").Match(rg.Groups[0].Value).Groups; string title = gLink[3].Value; title = Regex.Replace(title, "<i +class=\"fa +fa-([^\"]+)\"></i>", "$1"); title = Regex.Replace(title, "<img +src=\"/static/images/fugu/balloon(-|_)([a-z]+)\\..*", "$2"); return($"{SplitBbCode}link:{gLink[1].Value},{title}{SplitBbCode}"); } // Удаляем return(""); }); #endregion // Дебаг //Debug.WriteLine(MessageHtml); #region Форматирования текста bool IsNewLineTwo = false; var MassItem = Regex.Split(message_html, "<br />"); for (int i = 0; i < MassItem.Length; i++) { // Достаем строку string line = MassItem[i]; // Максимум два \n if (string.IsNullOrWhiteSpace(line)) { if (IsNewLineTwo || i == MassItem.Length - 1 || string.IsNullOrWhiteSpace(MassItem[i + 1])) { continue; } IsNewLineTwo = true; this.Children.Add(new Label()); continue; } else { IsNewLineTwo = false; } // Костыльный StackLayout WrapLayout wrapLayout = new WrapLayout { Orientation = StackOrientation.Horizontal, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.Start, Padding = 0, Spacing = 5 }; #region Формируем текст foreach (var splitLine in Regex.Split(line, SplitBbCode)) { if (string.IsNullOrWhiteSpace(splitLine)) { continue; } // BB code if (Regex.IsMatch(splitLine, "^(em|strong|link|img):")) { var grp = new Regex("^(em|strong|link|img):(.*)$").Match(splitLine).Groups; if (string.IsNullOrWhiteSpace(grp[2].Value)) { continue; } // Стили для текста switch (grp[1].Value) { case "em": case "strong": { FormatLabelTextToWrapLayout(wrapLayout, grp[2].Value, textColor, grp[1].Value == "em" ? FontAttributes.Italic : FontAttributes.Bold); break; } case "img": { string url = grp[2].Value.Split(',')[0]; if (!url.Contains("/static/images/fugu/")) { goto case "link"; } wrapLayout.Children.Add(new Image() { Source = url.Contains("freelancehunt.com") ? url : $"https://freelancehunt.com/{url}", HeightRequest = 17, WidthRequest = 17, MinimumHeightRequest = 17, MinimumWidthRequest = 17, Margin = new Thickness(0, 4, 0, 0), HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.Start, }); break; } case "link": { string url = grp[2].Value.Split(',')[0]; if (!Regex.IsMatch(url, "^https?://")) { url = "https://freelancehunt.com/" + Regex.Replace(url, "^/", ""); } TapGestureRecognizer tap = new TapGestureRecognizer(); tap.Tapped += (s, et) => { Device.OpenUri(new Uri(url)); }; FormatLabelTextToWrapLayout(wrapLayout, (grp[1].Value == "img" ? "img" : grp[2].Value.Split(',')[1]), Color.FromHex("#6ea6eb"), lineBreakMode: LineBreakMode.TailTruncation, tap: tap, urlImg: url); break; } } } // Обычный текст else { FormatLabelTextToWrapLayout(wrapLayout, splitLine, textColor); } } this.Children.Add(wrapLayout); #endregion } #endregion }