Bindable Markdown View for use with MvvmCross on the Android platform.
Dependencies:
Keywords:
mvvmcross, mvvm cross, mvx, xamarin, android, monodroid, markdown, markdowndeep, binding, bindable, bind
Bindable Markdown View for use with MvvmCross on the Android platform.
Dependencies:
Keywords:
mvvmcross, mvvm cross, mvx, xamarin, android, monodroid, markdown, markdowndeep, binding, bindable, bind
| using System; | |
| using Android.Content; | |
| using Android.Util; | |
| using Android.Webkit; | |
| using MarkdownDeep; | |
| namespace FutureState.Droid.Ui.Controls | |
| { | |
| public class BindalbeMarkdownView : WebView | |
| { | |
| private readonly Markdown _markdownDeep; | |
| private string _markdown; | |
| public BindableMarkdownView(Context context, IAttributeSet attrs) | |
| : base(context, attrs) | |
| { | |
| _markdownDeep = new Markdown(); | |
| } | |
| public string Markdown | |
| { | |
| get { return _markdown; } | |
| set | |
| { | |
| if (string.IsNullOrEmpty(value)) return; | |
| _markdown = _markdownDeep.Transform(value); | |
| LoadData(_markdown, "text/html", "utf-8"); | |
| UpdatedHtmlContent(); | |
| } | |
| } | |
| public event EventHandler HtmlContentChanged; | |
| private void UpdatedHtmlContent() | |
| { | |
| var handler = HtmlContentChanged; | |
| if (handler != null) | |
| handler(this, EventArgs.Empty); | |
| } | |
| } | |
| } |
| using System; | |
| using System.Drawing; | |
| using System.Text; | |
| using MarkdownDeep; | |
| using MonoTouch.Foundation; | |
| using MonoTouch.UIKit; | |
| namespace FutureState.BreathingRoom.Touch.Ui.Controls | |
| { | |
| public class BindableUIMarkdownView : UILabel | |
| { | |
| private readonly Markdown _markdownDeep; | |
| private string _markdown; | |
| private string _style; | |
| public BindableUIMarkdownView() | |
| { | |
| _markdownDeep = new Markdown(); | |
| Insets = new UIEdgeInsets(0, 0, 0, 0); | |
| } | |
| public BindableUIMarkdownView(RectangleF frame) | |
| : base(frame) | |
| { | |
| _markdownDeep = new Markdown(); | |
| Insets = new UIEdgeInsets(0, 0, 0, 0); | |
| } | |
| [Obsolete("Font wont work because the markdown renders HTML. Please use the Style property.")] | |
| public new UIFont Font { get; set; } | |
| public string Style | |
| { | |
| get | |
| { | |
| return string.IsNullOrWhiteSpace(_style) ? DefaultStyle : _style; | |
| } | |
| set | |
| { | |
| _style = value; | |
| } | |
| } | |
| public UIEdgeInsets Insets { get; set; } | |
| public string Markdown | |
| { | |
| get { return _markdown; } | |
| set | |
| { | |
| if (string.IsNullOrEmpty(value) || _markdown == value) return; | |
| _markdown = _markdownDeep.Transform(value); | |
| var html = new StringBuilder(); | |
| html.Append("<html>"); | |
| html.Append(string.Format("<style>{0}</style>", Style)); | |
| html.Append("<body>"); | |
| html.Append(_markdown); | |
| html.Append("</body>"); | |
| html.Append("</html>"); | |
| var attr = new NSAttributedStringDocumentAttributes { DocumentType = NSDocumentType.HTML }; | |
| var nsError = new NSError(); | |
| var output = new NSAttributedString(html.ToString(), attr, ref nsError); | |
| AttributedText = output; | |
| HandleHtmlContentChanged(); | |
| } | |
| } | |
| public override void DrawText(RectangleF frame) | |
| { | |
| Layer.Bounds = new RectangleF(frame.X, frame.Y, frame.Width, frame.Height + Insets.Top + Insets.Bottom); | |
| base.DrawText(new RectangleF( | |
| frame.X + Insets.Left, | |
| frame.Y + Insets.Top, | |
| frame.Width - Insets.Left - Insets.Right, | |
| frame.Height)); | |
| } | |
| public event EventHandler HtmlContentChanged; | |
| private void HandleHtmlContentChanged() | |
| { | |
| var handler = HtmlContentChanged; | |
| if (handler != null) | |
| handler(this, EventArgs.Empty); | |
| } | |
| private const string DefaultStyle = @" | |
| body { | |
| font-family: Helvetica, arial, sans-serif; | |
| font-size: 14px; | |
| line-height: 1.6; | |
| padding-top: 10px; | |
| padding-bottom: 10px; | |
| background-color: white; | |
| padding: 30px; | |
| margin: 10px; | |
| color: #333; | |
| } | |
| body > *:first-child { | |
| margin-top: 0 !important; | |
| } | |
| body > *:last-child { | |
| margin-bottom: 0 !important; | |
| } | |
| a { | |
| color: #4183C4; | |
| text-decoration: none; | |
| } | |
| a.absent { | |
| color: #cc0000; | |
| } | |
| a.anchor { | |
| display: block; | |
| padding-left: 30px; | |
| margin-left: -30px; | |
| cursor: pointer; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| bottom: 0; | |
| } | |
| h1, h2, h3, h4, h5, h6 { | |
| margin: 20px 0 10px; | |
| padding: 0; | |
| font-weight: bold; | |
| -webkit-font-smoothing: antialiased; | |
| cursor: text; | |
| position: relative; | |
| } | |
| h2:first-child, h1:first-child, h1:first-child + h2, h3:first-child, h4:first-child, h5:first-child, h6:first-child { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { | |
| text-decoration: none; | |
| } | |
| h1 tt, h1 code { | |
| font-size: inherit; | |
| } | |
| h2 tt, h2 code { | |
| font-size: inherit; | |
| } | |
| h3 tt, h3 code { | |
| font-size: inherit; | |
| } | |
| h4 tt, h4 code { | |
| font-size: inherit; | |
| } | |
| h5 tt, h5 code { | |
| font-size: inherit; | |
| } | |
| h6 tt, h6 code { | |
| font-size: inherit; | |
| } | |
| h1 { | |
| font-size: 28px; | |
| color: black; | |
| } | |
| h2 { | |
| font-size: 24px; | |
| border-bottom: 1px solid #cccccc; | |
| color: black; | |
| } | |
| h3 { | |
| font-size: 18px; | |
| } | |
| h4 { | |
| font-size: 16px; | |
| } | |
| h5 { | |
| font-size: 14px; | |
| } | |
| h6 { | |
| color: #777777; | |
| font-size: 14px; | |
| } | |
| p, blockquote, ul, ol, dl, li, table, pre { | |
| margin: 15px 0; | |
| } | |
| hr { | |
| border: 0 none; | |
| color: #222222; | |
| height: 4px; | |
| padding: 0; | |
| } | |
| body > h2:first-child { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| body > h1:first-child { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| body > h1:first-child + h2 { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { | |
| margin-top: 0; | |
| } | |
| li p.first { | |
| display: inline-block; | |
| } | |
| ul, ol { | |
| padding-left: 30px; | |
| } | |
| ul :first-child, ol :first-child { | |
| margin-top: 0; | |
| } | |
| ul :last-child, ol :last-child { | |
| margin-bottom: 0; | |
| } | |
| dl { | |
| padding: 0; | |
| } | |
| dl dt { | |
| font-size: 14px; | |
| font-weight: bold; | |
| font-style: italic; | |
| padding: 0; | |
| margin: 15px 0 5px; | |
| } | |
| dl dt:first-child { | |
| padding: 0; | |
| } | |
| dl dt > :first-child { | |
| margin-top: 0; | |
| } | |
| dl dt > :last-child { | |
| margin-bottom: 0; | |
| } | |
| dl dd { | |
| margin: 0 0 15px; | |
| padding: 0 15px; | |
| } | |
| dl dd > :first-child { | |
| margin-top: 0; | |
| } | |
| dl dd > :last-child { | |
| margin-bottom: 0; | |
| } | |
| blockquote { | |
| border-left: 4px solid #dddddd; | |
| padding: 15px; | |
| margin: 15px; | |
| color: #777777; | |
| } | |
| blockquote > :first-child { | |
| margin-top: 0; | |
| } | |
| blockquote > :last-child { | |
| margin-bottom: 0; | |
| } | |
| table { | |
| padding: 0; | |
| } | |
| table tr { | |
| border-top: 1px solid #cccccc; | |
| background-color: white; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| table tr:nth-child(2n) { | |
| background-color: #f8f8f8; | |
| } | |
| table tr th { | |
| font-weight: bold; | |
| border: 1px solid #cccccc; | |
| text-align: left; | |
| margin: 0; | |
| padding: 6px 13px; | |
| } | |
| table tr td { | |
| border: 1px solid #cccccc; | |
| text-align: left; | |
| margin: 0; | |
| padding: 6px 13px; | |
| } | |
| table tr th :first-child, table tr td :first-child { | |
| margin-top: 0; | |
| } | |
| table tr th :last-child, table tr td :last-child { | |
| margin-bottom: 0; | |
| } | |
| img { | |
| max-width: 100%; | |
| } | |
| span.frame { | |
| display: block; | |
| overflow: hidden; | |
| } | |
| span.frame > span { | |
| border: 1px solid #dddddd; | |
| display: block; | |
| float: left; | |
| overflow: hidden; | |
| margin: 13px 0 0; | |
| padding: 7px; | |
| width: auto; | |
| } | |
| span.frame span img { | |
| display: block; | |
| float: left; | |
| } | |
| span.frame span span { | |
| clear: both; | |
| color: #333333; | |
| display: block; | |
| padding: 5px 0 0; | |
| } | |
| span.align-center { | |
| display: block; | |
| overflow: hidden; | |
| clear: both; | |
| } | |
| span.align-center > span { | |
| display: block; | |
| overflow: hidden; | |
| margin: 13px auto 0; | |
| text-align: center; | |
| } | |
| span.align-center span img { | |
| margin: 0 auto; | |
| text-align: center; | |
| } | |
| span.align-right { | |
| display: block; | |
| overflow: hidden; | |
| clear: both; | |
| } | |
| span.align-right > span { | |
| display: block; | |
| overflow: hidden; | |
| margin: 13px 0 0; | |
| text-align: right; | |
| } | |
| span.align-right span img { | |
| margin: 0; | |
| text-align: right; | |
| } | |
| span.float-left { | |
| display: block; | |
| margin-right: 13px; | |
| overflow: hidden; | |
| float: left; | |
| } | |
| span.float-left span { | |
| margin: 13px 0 0; | |
| } | |
| span.float-right { | |
| display: block; | |
| margin-left: 13px; | |
| overflow: hidden; | |
| float: right; | |
| } | |
| span.float-right > span { | |
| display: block; | |
| overflow: hidden; | |
| margin: 13px auto 0; | |
| text-align: right; | |
| } | |
| code, tt { | |
| margin: 0 2px; | |
| padding: 0 5px; | |
| white-space: nowrap; | |
| border: 1px solid #eaeaea; | |
| background-color: #f8f8f8; | |
| border-radius: 3px; | |
| } | |
| pre code { | |
| margin: 0; | |
| padding: 0; | |
| white-space: pre; | |
| border: none; | |
| background: transparent; | |
| } | |
| .highlight pre { | |
| background-color: #f8f8f8; | |
| border: 1px solid #cccccc; | |
| font-size: 13px; | |
| line-height: 19px; | |
| overflow: auto; | |
| padding: 6px 10px; | |
| border-radius: 3px; | |
| } | |
| pre { | |
| background-color: #f8f8f8; | |
| border: 1px solid #cccccc; | |
| font-size: 13px; | |
| line-height: 19px; | |
| overflow: auto; | |
| padding: 6px 10px; | |
| border-radius: 3px; | |
| } | |
| pre code, pre tt { | |
| background-color: transparent; | |
| border: none; | |
| }"; | |
| } | |
| } |
| <BindalbeMarkdownView | |
| android:id="@+id/MyWebContent" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginLeft="10dp" | |
| android:layout_marginRight="10dp" | |
| android:layout_marginBottom="10dp" | |
| android:padding="10dp" | |
| android:textColor="@color/breathingroom_mediumgray" | |
| local:MvxBind="Markdown MyMarkdownProperty/> |
| var myMarkdown = new BindableMarkdownView { | |
| TextColor = CustomUIColor.breathingroom_darkgray, | |
| BackgroundColor = UIColor.Clear, | |
| Lines = 0, | |
| Insets = new UIEdgeInsets(0, 5, 10, 5), | |
| }; | |
| myMarkdown.Layer.CornerRadius = 3; | |
| myMarkdown.Layer.BorderColor = UIColor.Gray.CGColor; | |
| myMarkdown.Layer.BackgroundColor = UIColor.White.CGColor; | |
| var set = this.CreateBindingSet<SomeView, SomeViewModel>(); | |
| set.Bind(myMarkdown).For(p => p.Markdown).To(vm => vm.MyMarkdownProperty); | |
| set.Apply(); |