Browse Source

Implemented TextBox scrolling.

pull/12/head
Steven Kirk 11 years ago
parent
commit
542e81d129
  1. 21
      Perspex.Controls/Control.cs
  2. 2
      Perspex.Controls/Perspex.Controls.csproj
  3. 75
      Perspex.Controls/Presenters/ScrollContentPresenter.cs
  4. 27
      Perspex.Controls/Presenters/TextPresenter.cs
  5. 25
      Perspex.Controls/Primitives/IScrollInfo.cs
  6. 17
      Perspex.Controls/RequestBringIntoViewEventArgs.cs
  7. 152
      Perspex.Controls/ScrollViewer.cs
  8. 8
      Perspex.SceneGraph/IVisual.cs
  9. 7
      Perspex.SceneGraph/Point.cs
  10. 5
      Perspex.SceneGraph/Rect.cs
  11. 11
      Perspex.SceneGraph/Vector.cs
  12. 20
      Perspex.SceneGraph/Visual.cs
  13. 14
      Perspex.Themes.Default/ScrollViewerStyle.cs
  14. 19
      Perspex.Themes.Default/TextBoxStyle.cs
  15. 7
      TestApplication/Program.cs

21
Perspex.Controls/Control.cs

@ -11,6 +11,7 @@ namespace Perspex.Controls
using System.Linq;
using System.Reactive.Linq;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Media;
using Perspex.Rendering;
using Perspex.Styling;
@ -33,6 +34,9 @@ namespace Perspex.Controls
public static readonly PerspexProperty<ITemplatedControl> TemplatedParentProperty =
PerspexProperty.Register<Control, ITemplatedControl>("TemplatedParent", inherits: true);
public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>("RequestBringIntoView", RoutingStrategy.Bubble);
private Classes classes = new Classes();
private DataTemplates dataTemplates;
@ -155,6 +159,23 @@ namespace Perspex.Controls
internal set { this.SetValue(TemplatedParentProperty, value); }
}
public void BringIntoView()
{
this.BringIntoView(new Rect(this.ActualSize));
}
public void BringIntoView(Rect rect)
{
var ev = new RequestBringIntoViewEventArgs
{
RoutedEvent = RequestBringIntoViewEvent,
TargetObject = this,
TargetRect = rect,
};
this.RaiseEvent(ev);
}
protected static void PseudoClass(PerspexProperty<bool> property, string className)
{
PseudoClass(property, x => x, className);

2
Perspex.Controls/Perspex.Controls.csproj

@ -71,8 +71,10 @@
<Compile Include="ItemsPanelTemplate.cs" />
<Compile Include="Presenters\ItemsPresenter.cs" />
<Compile Include="Panel.cs" />
<Compile Include="Primitives\IScrollInfo.cs" />
<Compile Include="Primitives\Track.cs" />
<Compile Include="Primitives\Thumb.cs" />
<Compile Include="RequestBringIntoViewEventArgs.cs" />
<Compile Include="TextWrapping.cs" />
<Compile Include="Utils\StringUtils.cs" />
<Compile Include="Window.cs" />

75
Perspex.Controls/Presenters/ScrollContentPresenter.cs

@ -8,6 +8,8 @@ namespace Perspex.Controls.Presenters
{
using System;
using System.Linq;
using System.Reactive.Disposables;
using Perspex.Controls.Primitives;
using Perspex.Input;
using Perspex.Layout;
@ -22,11 +24,25 @@ namespace Perspex.Controls.Presenters
public static readonly PerspexProperty<Size> ViewportProperty =
ScrollViewer.ViewportProperty.AddOwner<ScrollContentPresenter>();
public static readonly PerspexProperty<bool> CanScrollHorizontallyProperty =
PerspexProperty.Register<ScrollContentPresenter, bool>("CanScrollHorizontally", true);
private IDisposable contentBindings;
static ScrollContentPresenter()
{
Control.AffectsArrange(OffsetProperty);
}
public ScrollContentPresenter()
{
this.GetObservable(ContentProperty).Subscribe(this.ContentChanged);
this.AddHandler(
Control.RequestBringIntoViewEvent,
new EventHandler<RequestBringIntoViewEventArgs>(this.BringIntoViewRequested));
}
public Size Extent
{
get { return this.GetValue(ExtentProperty); }
@ -45,13 +61,25 @@ namespace Perspex.Controls.Presenters
private set { this.SetValue(ViewportProperty, value); }
}
public bool CanScrollHorizontally
{
get { return this.GetValue(CanScrollHorizontallyProperty); }
}
protected override Size MeasureOverride(Size availableSize)
{
var content = this.Content as ILayoutable;
if (content != null)
{
content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
if (!this.CanScrollHorizontally)
{
measureSize = measureSize.WithWidth(availableSize.Width);
}
content.Measure(measureSize);
var size = content.DesiredSize.Value;
this.Extent = size;
return size.Constrain(availableSize);
@ -88,5 +116,50 @@ namespace Perspex.Controls.Presenters
e.Handled = true;
}
}
private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e)
{
var transform = e.TargetObject.TransformToVisual(this.GetVisualChildren().Single());
var rect = e.TargetRect * transform;
var offset = this.Offset;
if (rect.Bottom > offset.Y + this.Viewport.Height)
{
offset = offset.WithY(rect.Bottom - this.Viewport.Height);
}
if (rect.Y < offset.Y)
{
offset = offset.WithY(rect.Y);
}
if (rect.Right > offset.X + this.Viewport.Width)
{
offset = offset.WithX(rect.Right - this.Viewport.Width);
}
if (rect.X < offset.X)
{
offset = offset.WithX(rect.X);
}
this.Offset = offset;
}
private void ContentChanged(object content)
{
var scrollInfo = content as IScrollInfo;
if (this.contentBindings != null)
{
this.contentBindings.Dispose();
this.contentBindings = null;
}
if (scrollInfo != null)
{
this.contentBindings = this.Bind(CanScrollHorizontallyProperty, scrollInfo.CanScrollHorizontally);
}
}
}
}

27
Perspex.Controls/Presenters/TextPresenter.cs

@ -10,12 +10,13 @@ namespace Perspex.Controls
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls.Primitives;
using Perspex.Controls.Utils;
using Perspex.Input;
using Perspex.Media;
using Perspex.Threading;
public class TextPresenter : TextBlock
public class TextPresenter : TextBlock, IScrollInfo
{
public static readonly PerspexProperty<bool> AcceptsReturnProperty =
TextBox.AcceptsReturnProperty.AddOwner<TextPresenter>();
@ -36,6 +37,8 @@ namespace Perspex.Controls
private bool caretBlink;
private IObservable<bool> canScrollHorizontally;
static TextPresenter()
{
FocusableProperty.OverrideDefaultValue(typeof(TextPresenter), true);
@ -47,13 +50,16 @@ namespace Perspex.Controls
this.caretTimer.Interval = TimeSpan.FromMilliseconds(500);
this.caretTimer.Tick += this.CaretTimerTick;
this.canScrollHorizontally = this.GetObservable(TextWrappingProperty)
.Select(x => x == TextWrapping.NoWrap);
Observable.Merge(
this.GetObservable(SelectionStartProperty),
this.GetObservable(SelectionEndProperty))
.Subscribe(_ => this.InvalidateFormattedText());
this.GetObservable(TextBox.CaretIndexProperty)
.Subscribe(_ => this.CaretMoved());
this.GetObservable(TextPresenter.CaretIndexProperty)
.Subscribe(this.CaretIndexChanged);
}
public bool AcceptsReturn
@ -91,6 +97,16 @@ namespace Perspex.Controls
get { return base.FormattedText; }
}
IObservable<bool> IScrollInfo.CanScrollHorizontally
{
get { return this.canScrollHorizontally; }
}
IObservable<bool> IScrollInfo.IsHorizontalScrollBarVisible
{
get { return Observable.Never<bool>().StartWith(false); }
}
public int GetCaretIndex(Point point)
{
var hit = this.FormattedText.HitTestPoint(point);
@ -312,12 +328,15 @@ namespace Perspex.Controls
}
}
internal void CaretMoved()
internal void CaretIndexChanged(int caretIndex)
{
this.caretBlink = true;
this.caretTimer.Stop();
this.caretTimer.Start();
this.InvalidateVisual();
var rect = this.FormattedText.HitTestTextPosition(caretIndex);
this.BringIntoView(rect);
}
private void MoveHorizontal(int count, ModifierKeys modifiers)

25
Perspex.Controls/Primitives/IScrollInfo.cs

@ -0,0 +1,25 @@
// -----------------------------------------------------------------------
// <copyright file="IScrollInfo.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Primitives
{
using System;
/// <summary>
/// Allows the child of a <see cref="ScrollViewer"/> to communicate with the scroll viewer.
/// </summary>
/// <remarks>
/// Note that this interface has a different purpose to IScrollInfo in WPF! I would give it
/// a different name, but it does what it suggests - give information about the scrolling
/// requirements of a control.
/// </remarks>
public interface IScrollInfo
{
IObservable<bool> CanScrollHorizontally { get; }
IObservable<bool> IsHorizontalScrollBarVisible { get; }
}
}

17
Perspex.Controls/RequestBringIntoViewEventArgs.cs

@ -0,0 +1,17 @@
// -----------------------------------------------------------------------
// <copyright file="TextPresenter.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using Perspex.Interactivity;
public class RequestBringIntoViewEventArgs : RoutedEventArgs
{
public IVisual TargetObject { get; set; }
public Rect TargetRect { get; set; }
}
}

152
Perspex.Controls/ScrollViewer.cs

@ -22,11 +22,34 @@ namespace Perspex.Controls
public static readonly PerspexProperty<Size> ViewportProperty =
PerspexProperty.Register<ScrollViewer, Size>("Viewport");
private ScrollContentPresenter presenter;
public static readonly PerspexProperty<bool> CanScrollHorizontallyProperty =
PerspexProperty.Register<ScrollViewer, bool>("CanScrollHorizontally", false);
private ScrollBar horizontalScrollBar;
public static readonly PerspexProperty<bool> IsHorizontalScrollBarVisibleProperty =
PerspexProperty.Register<ScrollViewer, bool>("IsHorizontalScrollBarVisible");
private ScrollBar verticalScrollBar;
public static readonly PerspexProperty<double> HorizontalScrollBarMaximumProperty =
PerspexProperty.Register<ScrollViewer, double>("HorizontalScrollBarMaximum");
public static readonly PerspexProperty<double> HorizontalScrollBarValueProperty =
PerspexProperty.Register<ScrollViewer, double>("HorizontalScrollBarValue");
public static readonly PerspexProperty<double> HorizontalScrollBarViewportSizeProperty =
PerspexProperty.Register<ScrollViewer, double>("HorizontalScrollBarViewportSize");
public static readonly PerspexProperty<bool> IsVerticalScrollBarVisibleProperty =
PerspexProperty.Register<ScrollViewer, bool>("IsVerticalScrollBarVisible");
public static readonly PerspexProperty<double> VerticalScrollBarMaximumProperty =
PerspexProperty.Register<ScrollViewer, double>("VerticalScrollBarMaximum");
public static readonly PerspexProperty<double> VerticalScrollBarValueProperty =
PerspexProperty.Register<ScrollViewer, double>("VerticalScrollBarValue");
public static readonly PerspexProperty<double> VerticalScrollBarViewportSizeProperty =
PerspexProperty.Register<ScrollViewer, double>("VerticalScrollBarViewportSize");
private IDisposable contentBindings;
static ScrollViewer()
{
@ -34,6 +57,54 @@ namespace Perspex.Controls
AffectsCoercion(ViewportProperty, OffsetProperty);
}
public ScrollViewer()
{
var extentAndViewport = Observable.CombineLatest(
this.GetObservable(ExtentProperty),
this.GetObservable(ViewportProperty))
.Select(x => new { Extent = x[0], Viewport = x[1] });
this.Bind(
IsHorizontalScrollBarVisibleProperty,
extentAndViewport.Select(x => x.Extent.Width > x.Viewport.Width),
BindingPriority.Style);
this.Bind(
HorizontalScrollBarMaximumProperty,
extentAndViewport.Select(x => x.Extent.Width - x.Viewport.Width));
this.Bind(
HorizontalScrollBarViewportSizeProperty,
extentAndViewport.Select(x => (x.Viewport.Width / x.Extent.Width) * (x.Extent.Width - x.Viewport.Width)));
this.Bind(
IsVerticalScrollBarVisibleProperty,
extentAndViewport.Select(x => x.Extent.Height > x.Viewport.Height),
BindingPriority.Style);
this.Bind(
VerticalScrollBarMaximumProperty,
extentAndViewport.Select(x => x.Extent.Height - x.Viewport.Height));
this.Bind(
VerticalScrollBarViewportSizeProperty,
extentAndViewport.Select(x => (x.Viewport.Height / x.Extent.Height) * (x.Extent.Height - x.Viewport.Height)));
this.GetObservable(OffsetProperty).Subscribe(x =>
{
this.SetValue(HorizontalScrollBarValueProperty, x.X);
this.SetValue(VerticalScrollBarValueProperty, x.Y);
});
var scrollBarOffset = Observable.CombineLatest(
this.GetObservable(HorizontalScrollBarValueProperty),
this.GetObservable(VerticalScrollBarValueProperty))
.Select(x => new Vector(x[0], x[1]))
.Subscribe(x => this.Offset = x);
this.GetObservable(ContentProperty).Subscribe(this.ContentChanged);
}
public Size Extent
{
get { return this.GetValue(ExtentProperty); }
@ -52,62 +123,15 @@ namespace Perspex.Controls
private set { this.SetValue(ViewportProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
public bool CanScrollHorizontally
{
return base.MeasureOverride(availableSize);
get { return this.GetValue(CanScrollHorizontallyProperty); }
set { this.SetValue(CanScrollHorizontallyProperty, value); }
}
protected override void OnTemplateApplied()
protected override Size MeasureOverride(Size availableSize)
{
this.presenter = this.GetTemplateChild<ScrollContentPresenter>("presenter");
this.horizontalScrollBar = this.GetTemplateChild<ScrollBar>("horizontalScrollBar");
this.verticalScrollBar = this.GetTemplateChild<ScrollBar>("verticalScrollBar");
this[!ExtentProperty] = this.presenter[!ExtentProperty];
this[!ViewportProperty] = this.presenter[!ViewportProperty];
this.presenter[!OffsetProperty] = this[!OffsetProperty];
var extentAndViewport = Observable.CombineLatest(
this.GetObservable(ExtentProperty).StartWith(this.Extent),
this.GetObservable(ViewportProperty).StartWith(this.Viewport))
.Select(x => new { Extent = x[0], Viewport = x[1] });
this.horizontalScrollBar.Bind(
Visual.IsVisibleProperty,
extentAndViewport.Select(x => x.Extent.Width > x.Viewport.Width));
this.horizontalScrollBar.Bind(
ScrollBar.MaximumProperty,
extentAndViewport.Select(x => x.Extent.Width - x.Viewport.Width));
this.horizontalScrollBar.Bind(
ScrollBar.ViewportSizeProperty,
extentAndViewport.Select(x => (x.Viewport.Width / x.Extent.Width) * (x.Extent.Width - x.Viewport.Width)));
this.verticalScrollBar.Bind(
Visual.IsVisibleProperty,
extentAndViewport.Select(x => x.Extent.Height > x.Viewport.Height));
this.verticalScrollBar.Bind(
ScrollBar.MaximumProperty,
extentAndViewport.Select(x => x.Extent.Height - x.Viewport.Height));
this.verticalScrollBar.Bind(
ScrollBar.ViewportSizeProperty,
extentAndViewport.Select(x => (x.Viewport.Height / x.Extent.Height) * (x.Extent.Height - x.Viewport.Height)));
var offset = Observable.CombineLatest(
this.horizontalScrollBar.GetObservable(ScrollBar.ValueProperty),
this.verticalScrollBar.GetObservable(ScrollBar.ValueProperty))
.Select(x => new Vector(x[0], x[1]));
this.presenter.GetObservable(ScrollContentPresenter.OffsetProperty).Subscribe(x =>
{
this.horizontalScrollBar.Value = x.X;
this.verticalScrollBar.Value = x.Y;
});
this.Bind(OffsetProperty, offset);
return base.MeasureOverride(availableSize);
}
private static double Clamp(double value, double min, double max)
@ -132,5 +156,23 @@ namespace Perspex.Controls
return value;
}
}
private void ContentChanged(object content)
{
var scrollInfo = content as IScrollInfo;
if (this.contentBindings != null)
{
this.contentBindings.Dispose();
this.contentBindings = null;
}
if (scrollInfo != null)
{
this.contentBindings = this.Bind(
IsHorizontalScrollBarVisibleProperty,
scrollInfo.IsHorizontalScrollBarVisible);
}
}
}
}

8
Perspex.SceneGraph/IVisual.cs

@ -63,5 +63,13 @@ namespace Perspex
/// </summary>
/// <param name="context">The context.</param>
void Render(IDrawingContext context);
/// <summary>
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified <paramref name="visual"/>.
/// </summary>
/// <param name="visual"></param>
/// <returns>A <see cref="Matrix"/> containing the transform.</returns>
Matrix TransformToVisual(IVisual visual);
}
}

7
Perspex.SceneGraph/Point.cs

@ -82,6 +82,13 @@ namespace Perspex
return new Point(a.x - b.x, a.y - b.y);
}
public static Point operator *(Point point, Matrix matrix)
{
return new Point(
(point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.OffsetX,
(point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.OffsetY);
}
public static implicit operator Vector(Point p)
{
return new Vector(p.x, p.y);

5
Perspex.SceneGraph/Rect.cs

@ -226,6 +226,11 @@ namespace Perspex
height);
}
public static Rect operator *(Rect rect, Matrix matrix)
{
return new Rect(rect.TopLeft * matrix, rect.BottomRight * matrix);
}
public static Rect operator /(Rect rect, Vector scale)
{
double centerX = rect.x + (rect.width / 2);

11
Perspex.SceneGraph/Vector.cs

@ -6,6 +6,7 @@
namespace Perspex
{
using System;
using System.Globalization;
/// <summary>
@ -78,5 +79,15 @@ namespace Perspex
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", this.x, this.y);
}
public Vector WithX(double x)
{
return new Vector(x, this.y);
}
public Vector WithY(double y)
{
return new Vector(this.x, y);
}
}
}

20
Perspex.SceneGraph/Visual.cs

@ -104,6 +104,13 @@ namespace Perspex
Contract.Requires<ArgumentNullException>(context != null);
}
public Matrix TransformToVisual(IVisual visual)
{
var thisOffset = GetOffsetFromRoot(this);
var thatOffset = GetOffsetFromRoot(visual);
return Matrix.Translation(-thisOffset) * Matrix.Translation(thatOffset);
}
protected static void AffectsRender(PerspexProperty property)
{
property.Changed.Subscribe(AffectsRenderInvalidate);
@ -177,6 +184,19 @@ namespace Perspex
}
}
private static Vector GetOffsetFromRoot(IVisual v)
{
var result = new Vector();
while (!(v is IRenderRoot))
{
result = new Vector(result.X + v.Bounds.X, result.Y + v.Bounds.Y);
v = v.VisualParent;
}
return result;
}
private void SetVisualParent(Visual value)
{
if (this.visualParent != value)

14
Perspex.Themes.Default/ScrollViewerStyle.cs

@ -48,18 +48,30 @@ namespace Perspex.Themes.Default
new ScrollContentPresenter
{
Id = "presenter",
[~ContentPresenter.ContentProperty] = control[~ScrollViewer.ContentProperty],
[~ScrollContentPresenter.ContentProperty] = control[~ScrollViewer.ContentProperty],
[~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty],
[~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty],
[~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty],
[~ScrollContentPresenter.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
},
new ScrollBar
{
Id = "horizontalScrollBar",
Orientation = Orientation.Horizontal,
[~ScrollBar.IsVisibleProperty] = control[~ScrollViewer.IsHorizontalScrollBarVisibleProperty],
[~ScrollBar.MaximumProperty] = control[~ScrollViewer.HorizontalScrollBarMaximumProperty],
[~~ScrollBar.ValueProperty] = control[~~ScrollViewer.HorizontalScrollBarValueProperty],
[~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.HorizontalScrollBarViewportSizeProperty],
[Grid.RowProperty] = 1,
},
new ScrollBar
{
Id = "verticalScrollBar",
Orientation = Orientation.Vertical,
[~ScrollBar.IsVisibleProperty] = control[~ScrollViewer.IsVerticalScrollBarVisibleProperty],
[~ScrollBar.MaximumProperty] = control[~ScrollViewer.VerticalScrollBarMaximumProperty],
[~~ScrollBar.ValueProperty] = control[~~ScrollViewer.VerticalScrollBarValueProperty],
[~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.VerticalScrollBarViewportSizeProperty],
[Grid.ColumnProperty] = 1,
},
},

19
Perspex.Themes.Default/TextBoxStyle.cs

@ -47,15 +47,18 @@ namespace Perspex.Themes.Default
[~Border.BackgroundProperty] = control[~TextBox.BackgroundProperty],
[~Border.BorderBrushProperty] = control[~TextBox.BorderBrushProperty],
[~Border.BorderThicknessProperty] = control[~TextBox.BorderThicknessProperty],
Content = new TextPresenter
Content = new ScrollViewer
{
[~TextPresenter.AcceptsReturnProperty] = control[~TextBox.AcceptsReturnProperty],
[~TextPresenter.AcceptsTabProperty] = control[~TextBox.AcceptsTabProperty],
[~TextPresenter.CaretIndexProperty] = control[~TextBox.CaretIndexProperty],
[~TextPresenter.SelectionStartProperty] = control[~TextBox.SelectionStartProperty],
[~TextPresenter.SelectionEndProperty] = control[~TextBox.SelectionEndProperty],
[~TextPresenter.TextProperty] = control[~TextBox.TextProperty],
[~TextPresenter.TextWrappingProperty] = control[~TextBox.TextWrappingProperty],
Content = new TextPresenter
{
[~TextPresenter.AcceptsReturnProperty] = control[~TextBox.AcceptsReturnProperty],
[~TextPresenter.AcceptsTabProperty] = control[~TextBox.AcceptsTabProperty],
[~TextPresenter.CaretIndexProperty] = control[~TextBox.CaretIndexProperty],
[~TextPresenter.SelectionStartProperty] = control[~TextBox.SelectionStartProperty],
[~TextPresenter.SelectionEndProperty] = control[~TextBox.SelectionEndProperty],
[~TextPresenter.TextProperty] = control[~TextBox.TextProperty],
[~TextPresenter.TextWrappingProperty] = control[~TextBox.TextWrappingProperty],
}
}
};

7
TestApplication/Program.cs

@ -234,8 +234,12 @@ namespace TestApplication
},
new TextBox
{
Text = "A wrapping text box. Lorem ipsum dolor sit amet.",
AcceptsReturn = true,
Text = "A wrapping text box. " +
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin venenatis dui quis libero suscipit tincidunt. " +
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin venenatis dui quis libero suscipit tincidunt.",
TextWrapping = TextWrapping.Wrap,
MaxHeight = 100,
},
}
},
@ -268,6 +272,7 @@ namespace TestApplication
{
Width = 200,
Height = 200,
CanScrollHorizontally = true,
Content = new Image
{
Source = new Bitmap("github_icon.png"),

Loading…
Cancel
Save