Quellcode durchsuchen

Merge branch 'master' into fixes-line-measure

Jumar Macato vor 7 Jahren
Ursprung
Commit
fa364d85a7
33 geänderte Dateien mit 387 neuen und 105 gelöschten Zeilen
  1. 2 2
      readme.md
  2. 2 2
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  3. 4 1
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  4. 1 1
      src/Avalonia.Controls/AutoCompleteBox.cs
  5. 39 56
      src/Avalonia.Controls/Calendar/Calendar.cs
  6. 1 1
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  7. 15 16
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  8. 1 1
      src/Avalonia.Controls/Calendar/DatePicker.cs
  9. 6 7
      src/Avalonia.Controls/DropDownItem.cs
  10. 1 1
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
  11. 5 0
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  12. 1 1
      src/Avalonia.Controls/Platform/InProcessDragSource.cs
  13. 15 1
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  14. 16 0
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 37 0
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  16. 53 0
      src/Avalonia.Controls/Primitives/ScrollEventType.cs
  17. 2 1
      src/Avalonia.Controls/ToolTip.cs
  18. 14 0
      src/Avalonia.Controls/WindowBase.cs
  19. 4 0
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  20. 4 0
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  21. 3 2
      src/Avalonia.Themes.Default/Calendar.xaml
  22. 3 3
      src/Avalonia.Visuals/Media/FontFamily.cs
  23. 5 1
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  24. 2 0
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  25. 1 1
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  26. 2 1
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  27. 1 0
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  28. 26 4
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  29. 8 0
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  30. 1 1
      src/Windows/Avalonia.Win32/SystemDialogImpl.cs
  31. 18 1
      src/Windows/Avalonia.Win32/WindowImpl.cs
  32. 35 0
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  33. 59 0
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

+ 2 - 2
readme.md

@@ -18,7 +18,7 @@ Avalonia is a WPF-inspired cross-platform XAML-based UI framework providing a fl
 
 ## Getting Started
 
-Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (<a href="http://avaloniaui.net/tutorial/images/add-dialogs.png">screenshot</a>). Now you can write code and markup that will work on multiple platforms!
+Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (<a href="http://avaloniaui.net/docs/quickstart/images/new-project-dialog.png">screenshot</a>). Now you can write code and markup that will work on multiple platforms!
 
 Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed))
 
@@ -52,7 +52,7 @@ Please read the [contribution guidelines](http://avaloniaui.net/contributing/con
 ### Contributors
 
 This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing/contributing)].
-<a href="graphs/contributors"><img src="https://opencollective.com/Avalonia/contributors.svg?width=890&button=false" /></a>
+<a href="https://github.com/AvaloniaUI/Avalonia/graphs/contributors"><img src="https://opencollective.com/Avalonia/contributors.svg?width=890&button=false" /></a>
 
 
 ### Backers

+ 2 - 2
samples/ControlCatalog/DecoratedWindow.xaml.cs

@@ -20,7 +20,7 @@ namespace ControlCatalog
             ctl.Cursor = new Cursor(cursor);
             ctl.PointerPressed += delegate
             {
-                PlatformImpl.BeginResizeDrag(edge);
+                PlatformImpl?.BeginResizeDrag(edge);
             };
         }
 
@@ -29,7 +29,7 @@ namespace ControlCatalog
             AvaloniaXamlLoader.Load(this);
             this.FindControl<Control>("TitleBar").PointerPressed += delegate
             {
-                PlatformImpl.BeginMoveDrag();
+                PlatformImpl?.BeginMoveDrag();
             };
             SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West);
             SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East);

+ 4 - 1
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@@ -110,7 +110,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         {
             //Not supported
         }
-        
 
+        public void SetTopmost(bool value)
+        {
+            //Not supported
+        }
     }
 }

+ 1 - 1
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -2150,7 +2150,7 @@ namespace Avalonia.Controls
             }
 
             // Update the view
-            if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
+            if ((e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace) && e.OldItems != null)
             {
                 for (int index = 0; index < e.OldItems.Count; index++)
                 {

+ 39 - 56
src/Avalonia.Controls/Calendar/Calendar.cs

@@ -769,7 +769,7 @@ namespace Avalonia.Controls
         }
         private static void UpdateDisplayDate(Calendar c, DateTime addedDate, DateTime removedDate)
         {
-            Debug.Assert(c != null, "c should not be null!");
+            Contract.Requires<ArgumentNullException>(c != null);
 
             // If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart
             if (DateTime.Compare(addedDate, c.DisplayDateRangeStart) < 0)
@@ -1016,8 +1016,6 @@ namespace Avalonia.Controls
 
         internal CalendarDayButton FindDayButtonFromDay(DateTime day)
         {
-            CalendarDayButton b;
-            DateTime? d;
             CalendarItem monthControl = MonthControl;
 
             // REMOVE_RTM: should be updated if we support MultiCalendar
@@ -1028,14 +1026,16 @@ namespace Avalonia.Controls
                 {
                     for (int childIndex = ColumnsPerMonth; childIndex < count; childIndex++)
                     {
-                        b = monthControl.MonthView.Children[childIndex] as CalendarDayButton;
-                        d = b.DataContext as DateTime?;
-
-                        if (d.HasValue)
+                        if (monthControl.MonthView.Children[childIndex] is CalendarDayButton b)
                         {
-                            if (DateTimeHelper.CompareDays(d.Value, day) == 0)
+                            var d = b.DataContext as DateTime?;
+
+                            if (d.HasValue)
                             {
-                                return b;
+                                if (DateTimeHelper.CompareDays(d.Value, day) == 0)
+                                {
+                                    return b;
+                                }
                             }
                         }
                     }
@@ -1044,20 +1044,6 @@ namespace Avalonia.Controls
             return null;
         }
 
-        private void Calendar_SizeChanged(object sender, EventArgs e)
-        {
-            Debug.Assert(sender is Calendar, "The sender should be a Calendar!");
-
-            var size = Bounds.Size;
-            RectangleGeometry rg = new RectangleGeometry();
-            rg.Rect = new Rect(0, 0, size.Width, size.Height);
-
-            if (Root != null)
-            {
-                Root.Clip = rg;
-            }
-        }
-
         private void OnSelectedMonthChanged(DateTime? selectedMonth)
         {
             if (selectedMonth.HasValue)
@@ -1090,7 +1076,6 @@ namespace Avalonia.Controls
 
         internal void ResetStates()
         {
-            CalendarDayButton d;
             CalendarItem monthControl = MonthControl;
             int count = RowsPerMonth * ColumnsPerMonth;
             if (monthControl != null)
@@ -1099,7 +1084,7 @@ namespace Avalonia.Controls
                 {
                     for (int childIndex = ColumnsPerMonth; childIndex < count; childIndex++)
                     {
-                        d = monthControl.MonthView.Children[childIndex] as CalendarDayButton;
+                        var d = (CalendarDayButton)monthControl.MonthView.Children[childIndex];
                         d.IgnoreMouseOverState();
                     }
                 }
@@ -1190,8 +1175,6 @@ namespace Avalonia.Controls
             if (HoverEnd != null && HoverStart != null)
             {
                 int startIndex, endIndex, i;
-                CalendarDayButton b;
-                DateTime? d;
                 CalendarItem monthControl = MonthControl;
 
                 // This assumes a contiguous set of dates:
@@ -1201,18 +1184,20 @@ namespace Avalonia.Controls
 
                     for (i = startIndex; i <= endIndex; i++)
                     {
-                        b = monthControl.MonthView.Children[i] as CalendarDayButton;
-                        b.IsSelected = true;
-                        d = b.DataContext as DateTime?;
-
-                        if (d.HasValue && DateTimeHelper.CompareDays(HoverEnd.Value, d.Value) == 0)
+                        if (monthControl.MonthView.Children[i] is CalendarDayButton b)
                         {
-                            if (FocusButton != null)
+                            b.IsSelected = true;
+                            var d = b.DataContext as DateTime?;
+
+                            if (d.HasValue && DateTimeHelper.CompareDays(HoverEnd.Value, d.Value) == 0)
                             {
-                                FocusButton.IsCurrent = false;
+                                if (FocusButton != null)
+                                {
+                                    FocusButton.IsCurrent = false;
+                                }
+                                b.IsCurrent = HasFocusInternal;
+                                FocusButton = b;
                             }
-                            b.IsCurrent = HasFocusInternal;
-                            FocusButton = b;
                         }
                     }
                 }
@@ -1228,8 +1213,6 @@ namespace Avalonia.Controls
             if (HoverEnd != null && HoverStart != null)
             {
                 CalendarItem monthControl = MonthControl;
-                CalendarDayButton b;
-                DateTime? d;
 
                 if (HoverEndIndex != null && HoverStartIndex != null)
                 {
@@ -1240,15 +1223,17 @@ namespace Avalonia.Controls
                     {
                         for (i = startIndex; i <= endIndex; i++)
                         {
-                            b = monthControl.MonthView.Children[i] as CalendarDayButton;
-                            d = b.DataContext as DateTime?;
-
-                            if (d.HasValue)
+                            if (monthControl.MonthView.Children[i] is CalendarDayButton b)
                             {
-                                if (!SelectedDates.Contains(d.Value))
+                                var d = b.DataContext as DateTime?;
+
+                                if (d.HasValue)
                                 {
-                                    b.IsSelected = false;
-                                }
+                                    if (!SelectedDates.Contains(d.Value))
+                                    {
+                                        b.IsSelected = false;
+                                    }
+                                } 
                             }
                         }
                     }
@@ -1257,7 +1242,7 @@ namespace Avalonia.Controls
                         // It is SingleRange
                         for (i = startIndex; i <= endIndex; i++)
                         {
-                            (monthControl.MonthView.Children[i] as CalendarDayButton).IsSelected = false;
+                            ((CalendarDayButton)monthControl.MonthView.Children[i]).IsSelected = false;
                         }
                     }
                 }
@@ -1628,16 +1613,15 @@ namespace Avalonia.Controls
                 e.Handled = true;
             }
         }
-        internal void Calendar_KeyDown(object sender, KeyEventArgs e)
-        {
-            Calendar c = sender as Calendar;
-            Debug.Assert(c != null, "c should not be null!");
 
-            if (!e.Handled && c.IsEnabled)
+        internal void Calendar_KeyDown(KeyEventArgs e)
+        {
+            if (!e.Handled && IsEnabled)
             {
                 e.Handled = ProcessCalendarKey(e);
             }
         }
+
         internal bool ProcessCalendarKey(KeyEventArgs e)
         {
             if (DisplayMode == CalendarMode.Month)
@@ -1976,7 +1960,7 @@ namespace Avalonia.Controls
                     }
             }
         }
-        private void Calendar_KeyUp(object sender, KeyEventArgs e)
+        private void Calendar_KeyUp(KeyEventArgs e)
         {
             if (!e.Handled && (e.Key == Key.LeftShift || e.Key == Key.RightShift))
             {
@@ -2083,6 +2067,9 @@ namespace Avalonia.Controls
             DisplayDateProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateChanged);
             DisplayDateStartProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateStartChanged);
             DisplayDateEndProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateEndChanged);
+            KeyDownEvent.AddClassHandler<Calendar>(x => x.Calendar_KeyDown);
+            KeyUpEvent.AddClassHandler<Calendar>(x => x.Calendar_KeyUp);
+            
         }
 
         /// <summary>
@@ -2122,10 +2109,6 @@ namespace Avalonia.Controls
                     month.Owner = this;
                 }
             }
-
-            LayoutUpdated += Calendar_SizeChanged;
-            KeyDown += Calendar_KeyDown;
-            KeyUp += Calendar_KeyUp;
         }
 
     }

+ 1 - 1
src/Avalonia.Controls/Calendar/CalendarDateRange.cs

@@ -66,7 +66,7 @@ namespace Avalonia.Controls
         /// <returns>Inherited code: Requires comment 2.</returns>
         internal bool ContainsAny(CalendarDateRange range)
         {
-            Debug.Assert(range != null, "range should not be null!");
+            Contract.Requires<ArgumentNullException>(range != null);
 
             int start = DateTime.Compare(Start, range.Start);
 

+ 15 - 16
src/Avalonia.Controls/Calendar/CalendarItem.cs

@@ -517,7 +517,7 @@ namespace Avalonia.Controls.Primitives
             for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
             {
                 CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
-                Debug.Assert(childButton != null, "childButton should not be null!");
+                Contract.Requires<ArgumentNullException>(childButton != null);
 
                 childButton.Index = childIndex;
                 SetButtonState(childButton, dateToAdd);
@@ -554,7 +554,7 @@ namespace Avalonia.Controls.Primitives
                     for (int i = childIndex; i < count; i++)
                     {
                         childButton = MonthView.Children[i] as CalendarDayButton;
-                        Debug.Assert(childButton != null, "childButton should not be null!");
+                        Contract.Requires<ArgumentNullException>(childButton != null);
                         // button needs a content to occupy the necessary space
                         // for the content presenter
                         childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
@@ -650,7 +650,7 @@ namespace Avalonia.Controls.Primitives
             foreach (object child in YearView.Children)
             {
                 CalendarButton childButton = child as CalendarButton;
-                Debug.Assert(childButton != null, "childButton should not be null!");
+                Contract.Requires<ArgumentNullException>(childButton != null);
                 // There should be no time component. Time is 12:00 AM
                 DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
                 childButton.DataContext = day;
@@ -746,7 +746,7 @@ namespace Avalonia.Controls.Primitives
             foreach (object child in YearView.Children)
             {
                 CalendarButton childButton = child as CalendarButton;
-                Debug.Assert(childButton != null, "childButton should not be null!");
+                Contract.Requires<ArgumentNullException>(childButton != null);
                 year = decade + count;
 
                 if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
@@ -826,7 +826,7 @@ namespace Avalonia.Controls.Primitives
                 {
                     Owner.Focus();
                 }
-                Button b = sender as Button;
+                Button b = (Button)sender;
                 DateTime d;
 
                 if (b.IsEnabled)
@@ -863,7 +863,7 @@ namespace Avalonia.Controls.Primitives
                     Owner.Focus();
                 }
 
-                Button b = sender as Button;
+                Button b = (Button)sender;
                 if (b.IsEnabled)
                 {
                     Owner.OnPreviousClick();
@@ -878,7 +878,7 @@ namespace Avalonia.Controls.Primitives
                 {
                     Owner.Focus();
                 }
-                Button b = sender as Button;
+                Button b = (Button)sender;
 
                 if (b.IsEnabled)
                 {
@@ -891,8 +891,7 @@ namespace Avalonia.Controls.Primitives
         {
             if (Owner != null)
             {
-                CalendarDayButton b = sender as CalendarDayButton;
-                if (_isMouseLeftButtonDown && b != null && b.IsEnabled && !b.IsBlackout)
+                if (_isMouseLeftButtonDown && sender is CalendarDayButton b && b.IsEnabled && !b.IsBlackout)
                 {
                     // Update the states of all buttons to be selected starting
                     // from HoverStart to b
@@ -918,7 +917,7 @@ namespace Avalonia.Controls.Primitives
                                 Debug.Assert(b.DataContext != null, "The DataContext should not be null!");
                                 Owner.UnHighlightDays();
                                 Owner.HoverEndIndex = b.Index;
-                                Owner.HoverEnd = (DateTime)b.DataContext;
+                                Owner.HoverEnd = (DateTime?)b.DataContext;
                                 // Update the States of the buttons
                                 Owner.HighlightDays();
                                 return;
@@ -931,7 +930,7 @@ namespace Avalonia.Controls.Primitives
         {
             if (_isMouseLeftButtonDown)
             {
-                CalendarDayButton b = sender as CalendarDayButton;
+                CalendarDayButton b = (CalendarDayButton)sender;
                 // The button is in Pressed state. Change the state to normal.
                 if (e.Device.Captured == b)
                     e.Device.Capture(null);
@@ -973,7 +972,7 @@ namespace Avalonia.Controls.Primitives
                     if (b.IsEnabled && !b.IsBlackout)
                     {
                         DateTime selectedDate = (DateTime)b.DataContext;
-                        Debug.Assert(selectedDate != null, "selectedDate should not be null!");
+                        Contract.Requires<ArgumentNullException>(selectedDate != null);
                         _isMouseLeftButtonDown = true;
                         // null check is added for unit tests
                         if (e != null)
@@ -1149,7 +1148,7 @@ namespace Avalonia.Controls.Primitives
                 if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
                 {
                     CalendarDayButton b = sender as CalendarDayButton;
-                    Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+                    Contract.Requires<ArgumentNullException>(b != null);
 
                     if (b.IsSelected)
                     {
@@ -1169,7 +1168,7 @@ namespace Avalonia.Controls.Primitives
         private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
         {
             CalendarButton b = sender as CalendarButton;
-            Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+            Contract.Requires<ArgumentNullException>(b != null);
 
             _isMouseLeftButtonDownYearView = true;
 
@@ -1208,7 +1207,7 @@ namespace Avalonia.Controls.Primitives
             if (_isMouseLeftButtonDownYearView)
             {
                 CalendarButton b = sender as CalendarButton;
-                Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+                Contract.Requires<ArgumentNullException>(b != null);
                 UpdateYearViewSelection(b);
             }
         }
@@ -1217,7 +1216,7 @@ namespace Avalonia.Controls.Primitives
         {
             if (_isMouseLeftButtonDownYearView)
             {
-                CalendarButton b = sender as CalendarButton;
+                CalendarButton b = (CalendarButton)sender;
                 // The button is in Pressed state. Change the state to normal.
                 if (e.Device.Captured == b)
                     e.Device.Capture(null);

+ 1 - 1
src/Avalonia.Controls/Calendar/DatePicker.cs

@@ -842,7 +842,7 @@ namespace Avalonia.Controls
         private void Calendar_KeyDown(object sender, KeyEventArgs e)
         {
             Calendar c = sender as Calendar;
-            Debug.Assert(c != null, "The Calendar should not be null!");
+            Contract.Requires<ArgumentNullException>(c != null);
 
             if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
             {

+ 6 - 7
src/Avalonia.Controls/DropDownItem.cs

@@ -22,14 +22,13 @@ namespace Avalonia.Controls
         static DropDownItem()
         {
             FocusableProperty.OverrideDefaultValue<DropDownItem>(true);
-            IsFocusedProperty.Changed.Subscribe(x =>
-            {
-                var sender = x.Sender as IControl;
+        }
 
-                if (sender != null)
-                {
-                    ((IPseudoClasses)sender.Classes).Set(":selected", (bool)x.NewValue);
-                }
+        public DropDownItem()
+        {
+            this.GetObservable(DropDownItem.IsFocusedProperty).Subscribe(focused =>
+            {
+                PseudoClasses.Set(":selected", focused);                
             });
         }
 

+ 1 - 1
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs

@@ -57,7 +57,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
         Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
         public void Dispose()
         {
-            PlatformImpl.Dispose();
+            PlatformImpl?.Dispose();
         }
     }
 }

+ 5 - 0
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@@ -72,6 +72,11 @@ namespace Avalonia.Platform
         /// 
         void SetMinMaxSize(Size minSize, Size maxSize);
 
+        /// <summary>
+        /// Sets whether this window appears on top of all other windows
+        /// </summary>
+        void SetTopmost(bool value);
+
         /// <summary>
         /// Gets platform specific display information
         /// </summary>

+ 1 - 1
src/Avalonia.Controls/Platform/InProcessDragSource.cs

@@ -62,7 +62,7 @@ namespace Avalonia.Platform
 
             RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects);
             var tl = root.GetSelfAndVisualAncestors().OfType<TopLevel>().FirstOrDefault();
-            tl.PlatformImpl.Input(rawEvent);
+            tl.PlatformImpl?.Input(rawEvent);
 
             var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers);
             UpdateCursor(root, effect);

+ 15 - 1
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@@ -126,7 +126,21 @@ namespace Avalonia.Controls.Presenters
                         generator.Clear();
                         Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
 
-                        MoveToPage(-1, SelectedIndex >= 0 ? SelectedIndex : 0);
+                        var newIndex = SelectedIndex;
+
+                        if(SelectedIndex < 0)
+                        {
+                            if(Items != null && Items.Count() > 0)
+                            {
+                                newIndex = 0;
+                            }
+                            else
+                            {
+                                newIndex = -1;
+                            }
+                        }
+                        
+                        MoveToPage(-1, newIndex);
                     }
                     break;     
             }

+ 16 - 0
src/Avalonia.Controls/Primitives/Popup.cs

@@ -70,6 +70,12 @@ namespace Avalonia.Controls.Primitives
         public static readonly StyledProperty<bool> StaysOpenProperty =
             AvaloniaProperty.Register<Popup, bool>(nameof(StaysOpen), true);
 
+        /// <summary>
+        /// Defines the <see cref="Topmost"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> TopmostProperty =
+            AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
+
         private bool _isOpen;
         private PopupRoot _popupRoot;
         private TopLevel _topLevel;
@@ -84,6 +90,7 @@ namespace Avalonia.Controls.Primitives
             IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false);
             ChildProperty.Changed.AddClassHandler<Popup>(x => x.ChildChanged);
             IsOpenProperty.Changed.AddClassHandler<Popup>(x => x.IsOpenChanged);
+            TopmostProperty.Changed.AddClassHandler<Popup>((p, e) => p.PopupRoot.Topmost = (bool)e.NewValue);
         }
 
         /// <summary>
@@ -194,6 +201,15 @@ namespace Avalonia.Controls.Primitives
             set { SetValue(StaysOpenProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets whether this popup appears on top of all other windows
+        /// </summary>
+        public bool Topmost
+        {
+            get { return GetValue(TopmostProperty); }
+            set { SetValue(TopmostProperty, value); }
+        }
+
         /// <summary>
         /// Gets the root of the popup window.
         /// </summary>

+ 37 - 0
src/Avalonia.Controls/Primitives/ScrollBar.cs

@@ -6,9 +6,21 @@ using System.Reactive;
 using System.Reactive.Linq;
 using Avalonia.Data;
 using Avalonia.Interactivity;
+using Avalonia.Input;
 
 namespace Avalonia.Controls.Primitives
 {
+    public class ScrollEventArgs : EventArgs
+    {
+        public ScrollEventArgs(ScrollEventType eventType, double newValue)
+        {
+            ScrollEventType = eventType;
+            NewValue = newValue;
+        }
+        public double NewValue { get; private set; }
+        public ScrollEventType ScrollEventType { get; private set; }
+    }
+
     /// <summary>
     /// A scrollbar control.
     /// </summary>
@@ -44,6 +56,9 @@ namespace Avalonia.Controls.Primitives
         {
             PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
             PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
+
+            Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble);
+            Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble);
         }
 
         /// <summary>
@@ -88,6 +103,8 @@ namespace Avalonia.Controls.Primitives
             set { SetValue(OrientationProperty, value); }
         }
 
+        public event EventHandler<ScrollEventArgs> Scroll;
+
         /// <summary>
         /// Calculates whether the scrollbar should be visible.
         /// </summary>
@@ -140,6 +157,8 @@ namespace Avalonia.Controls.Primitives
             _pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
             _pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
 
+
+
             if (_lineUpButton != null)
             {
                 _lineUpButton.Click += LineUpClick;
@@ -184,21 +203,39 @@ namespace Avalonia.Controls.Primitives
         private void SmallDecrement()
         {
             Value = Math.Max(Value - SmallChange * ViewportSize, Minimum);
+            OnScroll(ScrollEventType.SmallDecrement);
         }
 
         private void SmallIncrement()
         {
             Value = Math.Min(Value + SmallChange * ViewportSize, Maximum);
+            OnScroll(ScrollEventType.SmallIncrement);
         }
 
         private void LargeDecrement()
         {
             Value = Math.Max(Value - LargeChange * ViewportSize, Minimum);
+            OnScroll(ScrollEventType.LargeDecrement);
         }
 
         private void LargeIncrement()
         {
             Value = Math.Min(Value + LargeChange * ViewportSize, Maximum);
+            OnScroll(ScrollEventType.LargeIncrement);
+        }
+
+        private void OnThumbDragDelta(VectorEventArgs e)
+        {
+            OnScroll(ScrollEventType.ThumbTrack);
+        }
+        private void OnThumbDragComplete(VectorEventArgs e)
+        {
+            OnScroll(ScrollEventType.EndScroll);
+        }
+
+        protected void OnScroll(ScrollEventType scrollEventType)
+        {
+            Scroll?.Invoke(this, new ScrollEventArgs(scrollEventType, Value));
         }
     }
 }

+ 53 - 0
src/Avalonia.Controls/Primitives/ScrollEventType.cs

@@ -0,0 +1,53 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+namespace Avalonia.Controls.Primitives
+{
+    /// <summary>    
+    /// Specifies the type of Avalonia.Controls.Primitives.ScrollBar.Scroll event
+    /// that occurred.
+    /// </summary>
+    public enum ScrollEventType
+    {
+        /// <summary>    
+        /// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
+        /// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.SmallChange.
+        /// The Avalonia.Controls.Primitives.Thumb moved to the left for a horizontal
+        /// Avalonia.Controls.Primitives.ScrollBar or upward for a vertical Avalonia.Controls.Primitives.ScrollBar.
+        /// </summary>
+        SmallDecrement = 0,
+        /// <summary>    
+        /// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
+        /// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.SmallChange.
+        /// The Avalonia.Controls.Primitives.Thumb moved to the right for a horizontal
+        /// Avalonia.Controls.Primitives.ScrollBar or downward for a vertical Avalonia.Controls.Primitives.ScrollBar.
+        /// </summary>
+        SmallIncrement = 1,
+        /// <summary>    
+        /// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
+        /// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.LargeChange.
+        /// The Avalonia.Controls.Primitives.Thumb moved to the left for a horizontal
+        /// Avalonia.Controls.Primitives.ScrollBar or upward for a vertical Avalonia.Controls.Primitives.ScrollBar.
+        /// </summary>
+        LargeDecrement = 2,
+        /// <summary>    
+        /// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
+        /// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.LargeChange.
+        /// The Avalonia.Controls.Primitives.Thumb moved to the right for a horizontal
+        /// Avalonia.Controls.Primitives.ScrollBar or downward for a vertical Avalonia.Controls.Primitives.ScrollBar.
+        /// </summary>
+        LargeIncrement = 3,
+        /// <summary>    
+        /// The Avalonia.Controls.Primitives.Thumb was dragged and caused a Avalonia.UIElement.MouseMove
+        /// event. A Avalonia.Controls.Primitives.ScrollBar.Scroll event of this Avalonia.Controls.Primitives.ScrollEventType
+        /// may occur more than one time when the Avalonia.Controls.Primitives.Thumb
+        /// is dragged in the Avalonia.Controls.Primitives.ScrollBar.
+        /// </summary>
+        ThumbTrack = 4,
+        /// <summary>    
+        /// Specifies that the Avalonia.Controls.Primitives.Thumb was dragged to a
+        /// new position and is now no longer being dragged by the user.
+        /// </summary>
+        EndScroll = 5
+    }
+}

+ 2 - 1
src/Avalonia.Controls/ToolTip.cs

@@ -234,11 +234,12 @@ namespace Avalonia.Controls
         {
             Close();
 
-            _popup = new PopupRoot { Content = this };
+            _popup = new PopupRoot { Content = this,  };
             ((ISetLogicalParent)_popup).SetParent(control);
             _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
                 GetHorizontalOffset(control), GetVerticalOffset(control));
             _popup.Show();
+            _popup.SnapInsideScreenEdges();
         }
 
         private void Close()

+ 14 - 0
src/Avalonia.Controls/WindowBase.cs

@@ -38,6 +38,9 @@ namespace Avalonia.Controls
                 o => o.Owner,
                 (o, v) => o.Owner = v);
 
+        public static readonly StyledProperty<bool> TopmostProperty =
+            AvaloniaProperty.Register<WindowBase, bool>(nameof(Topmost));
+
         private bool _hasExecutedInitialLayoutPass;
         private bool _isActive;
         private bool _ignoreVisibilityChange;
@@ -52,6 +55,8 @@ namespace Avalonia.Controls
             MinHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight)));
             MaxWidthProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight)));
             MaxHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue)));
+            
+            TopmostProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue));
         }
 
         public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
@@ -124,6 +129,15 @@ namespace Avalonia.Controls
             set { SetAndRaise(OwnerProperty, ref _owner, value); }
         }
 
+        /// <summary>
+        /// Gets or sets whether this window appears on top of all other windows
+        /// </summary>
+        public bool Topmost
+        {
+            get { return GetValue(TopmostProperty); }
+            set { SetValue(TopmostProperty, value); }
+        }
+
         /// <summary>
         /// Activates the window.
         /// </summary>

+ 4 - 0
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -102,5 +102,9 @@ namespace Avalonia.DesignerSupport.Remote
         public void CanResize(bool value)
         {
         }
+
+        public void SetTopmost(bool value)
+        {
+        }
     }
 }

+ 4 - 0
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -104,6 +104,10 @@ namespace Avalonia.DesignerSupport.Remote
         public void CanResize(bool value)
         {
         }
+
+        public void SetTopmost(bool value)
+        {
+        }
     }
 
     class ClipboardStub : IClipboard

+ 3 - 2
src/Avalonia.Themes.Default/Calendar.xaml

@@ -15,9 +15,10 @@
     <Setter Property="Template">
       <ControlTemplate>
         <StackPanel Name="Root"
-                    HorizontalAlignment="Center">
+                    HorizontalAlignment="Center"
+                    ClipToBounds="True">
 
-          <CalendarItem Name="CalendarItem"
+                    <CalendarItem Name="CalendarItem"
                         Background="{TemplateBinding Background}"
                         BorderBrush="{TemplateBinding BorderBrush}"
                         BorderThickness="{TemplateBinding BorderThickness}"

+ 3 - 3
src/Avalonia.Visuals/Media/FontFamily.cs

@@ -77,10 +77,10 @@ namespace Avalonia.Media
         /// <summary>
         /// Implicit conversion of string to FontFamily
         /// </summary>
-        /// <param name="fontFamily"></param>
-        public static implicit operator FontFamily(string fontFamily)
+        /// <param name="s"></param>
+        public static implicit operator FontFamily(string s)
         {
-            return new FontFamily(fontFamily);
+            return Parse(s);
         }
 
         /// <summary>

+ 5 - 1
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -261,10 +261,13 @@ namespace Avalonia.Gtk3.Interop
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_unmaximize(GtkWindow window);
-            
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_close(GtkWindow window);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_window_set_keep_above(GtkWindow gtkWindow, bool setting);
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
 
@@ -472,6 +475,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_window_maximize GtkWindowMaximize;
         public static D.gtk_window_unmaximize GtkWindowUnmaximize;
         public static D.gtk_window_close GtkWindowClose;
+        public static D.gtk_window_set_keep_above GtkWindowSetKeepAbove;
         public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
         public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
         public static D.gdk_event_request_motions GdkEventRequestMotions;

+ 2 - 0
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -416,6 +416,8 @@ namespace Avalonia.Gtk3
 
         public void Hide() => Native.GtkWidgetHide(GtkWidget);
 
+        public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value);
+
         void GetGlobalPointer(out int x, out int y)
         {
             int mask;

+ 1 - 1
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@@ -81,7 +81,7 @@ namespace Avalonia.Gtk3
         public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
 
         public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value);
-        
+
 
         class EmptyDisposable : IDisposable
         {

+ 2 - 1
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@@ -10,6 +10,7 @@ using System.Reactive.Subjects;
 using Avalonia.Controls;
 using Avalonia.Data.Converters;
 using Avalonia.Metadata;
+using JetBrains.Annotations;
 
 namespace Avalonia.Data
 {
@@ -65,7 +66,7 @@ namespace Avalonia.Data
             var children = Bindings.Select(x => x.Initiate(target, null));
             var input = children.Select(x => x.Subject).CombineLatest().Select(x => ConvertValue(x, targetType));
             var mode = Mode == BindingMode.Default ?
-                targetProperty.GetMetadata(target.GetType()).DefaultBindingMode : Mode;
+                targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode;
 
             switch (mode)
             {

+ 1 - 0
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@@ -123,6 +123,7 @@ namespace Avalonia.MonoMac
 
         public void Hide() => Window?.OrderOut(Window);
 
+        public void SetTopmost(bool value) => Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal;
 
         public void BeginMoveDrag()
         {

+ 26 - 4
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@@ -581,11 +581,10 @@ namespace Avalonia.Skia
                 }
 
                 measured = LineBreak(Text, curOff, length, _paint, constraint, out trailingnumber);
-
                 AvaloniaFormattedTextLine line = new AvaloniaFormattedTextLine();
                 line.Start = curOff;
                 line.TextLength = measured;
-
+                line.Start = curOff;
                 subString = Text.Substring(line.Start, line.TextLength);
                 lineWidth = _paint.MeasureText(subString);
                 line.Length = measured - trailingnumber;
@@ -596,10 +595,33 @@ namespace Avalonia.Skia
                 _skiaLines.Add(line);
 
                 curY += _lineHeight;
-
                 curY += mLeading;
-
                 curOff += measured;
+
+                //if this is the last line and there are trailing newline characters then
+                //insert a additional line
+                if (curOff >= length)
+                {
+                    var subStringMinusNewlines = subString.TrimEnd('\n', '\r');
+                    var lengthDiff = subString.Length - subStringMinusNewlines.Length;
+                    if (lengthDiff > 0)
+                    {
+                        AvaloniaFormattedTextLine lastLine = new AvaloniaFormattedTextLine();
+                        lastLine.TextLength = lengthDiff;
+                        lastLine.Start = curOff - lengthDiff;
+                        var lastLineSubString = Text.Substring(line.Start, line.TextLength);
+                        var lastLineWidth = _paint.MeasureText(lastLineSubString);
+                        lastLine.Length = 0;
+                        lastLine.Width = lastLineWidth;
+                        lastLine.Height = _lineHeight;
+                        lastLine.Top = curY;
+
+                        _skiaLines.Add(lastLine);
+
+                        curY += _lineHeight;
+                        curY += mLeading;
+                    }
+                }
             }
 
             // Now convert to Avalonia data formats

+ 8 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -78,6 +78,14 @@ namespace Avalonia.Win32.Interop
             SWP_RESIZE = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
         }
 
+        public static class WindowPosZOrder
+        {
+            public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
+            public static readonly IntPtr HWND_TOP = new IntPtr(0);
+            public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
+            public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
+        }
+
         public enum SizeCommand
         {
             Restored,

+ 1 - 1
src/Windows/Avalonia.Win32/SystemDialogImpl.cs

@@ -54,7 +54,7 @@ namespace Avalonia.Win32
                 var fileBuffer = new char[256];
                 dialog.InitialFileName?.CopyTo(0, fileBuffer, 0, dialog.InitialFileName.Length);
 
-                string userSelectedExt = null;
+                string userSelectedExt = string.Empty;
 
 
                 var title = ToChars(dialog.Title);

+ 18 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -32,6 +32,7 @@ namespace Avalonia.Win32
         private bool _trackingMouse;
         private bool _decorated = true;
         private bool _resizable = true;
+        private bool _topmost = false;
         private double _scaling = 1;
         private WindowState _showWindowState;
         private WindowState _lastWindowState;
@@ -838,7 +839,7 @@ namespace Avalonia.Win32
                     var cx = Math.Abs(monitorInfo.rcWork.right - x);
                     var cy = Math.Abs(monitorInfo.rcWork.bottom - y);
 
-                    SetWindowPos(_hwnd, new IntPtr(-2), x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
+                    SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
                 }
             }
         }
@@ -901,5 +902,21 @@ namespace Avalonia.Win32
 
             _resizable = value;
         }
+
+        public void SetTopmost(bool value)
+        {
+            if (value == _topmost)
+            {
+                return;
+            }
+
+            IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
+            UnmanagedMethods.SetWindowPos(_hwnd,
+                   hWndInsertAfter,
+                   0, 0, 0, 0,
+                   SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
+
+            _topmost = value;
+        }
     }
 }

+ 35 - 0
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@@ -170,6 +170,41 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal("Bar", ((TextBlock)child).Text);
         }
 
+        [Fact]
+        public void Selected_Index_Changes_To_When_Items_Assigned_Null()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "FooBar"
+            };
+
+            var target = new Carousel
+            {
+                Template = new FuncControlTemplate<Carousel>(CreateTemplate),
+                Items = items,
+                IsVirtualized = false
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            Assert.Single(target.GetLogicalChildren());
+
+            var child = target.GetLogicalChildren().Single();
+
+            Assert.IsType<TextBlock>(child);
+            Assert.Equal("Foo", ((TextBlock)child).Text);
+
+            target.Items = null;
+
+            var numChildren = target.GetLogicalChildren().Count();
+
+            Assert.Equal(0, numChildren);
+            Assert.Equal(-1, target.SelectedIndex);
+        }
+
         [Fact]
         public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
         {

+ 59 - 0
tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

@@ -5,6 +5,7 @@ using System;
 using System.Linq;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
+using Avalonia.Input;
 using Avalonia.Media;
 using Xunit;
 
@@ -59,6 +60,64 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(50, target.Value);
         }
 
+        [Fact]
+        public void Thumb_DragDelta_Event_Should_Raise_Scroll_Event()
+        {
+            var target = new ScrollBar
+            {
+                Template = new FuncControlTemplate<ScrollBar>(Template),
+            };
+
+            target.ApplyTemplate();
+
+            var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
+
+            var raisedEvent = Assert.Raises<ScrollEventArgs>(
+                handler => target.Scroll += handler,
+                handler => target.Scroll -= handler,
+                () =>
+                {
+                    var ev = new VectorEventArgs
+                    {
+                        RoutedEvent = Thumb.DragDeltaEvent,
+                        Vector = new Vector(0, 0)
+                    };
+
+                    track.Thumb.RaiseEvent(ev);
+                });
+
+            Assert.Equal(ScrollEventType.ThumbTrack, raisedEvent.Arguments.ScrollEventType);
+        }
+
+        [Fact]
+        public void Thumb_DragComplete_Event_Should_Raise_Scroll_Event()
+        {
+            var target = new ScrollBar
+            {
+                Template = new FuncControlTemplate<ScrollBar>(Template),
+            };
+
+            target.ApplyTemplate();
+
+            var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
+
+            var raisedEvent = Assert.Raises<ScrollEventArgs>(
+                handler => target.Scroll += handler,
+                handler => target.Scroll -= handler,
+                () =>
+                {
+                    var ev = new VectorEventArgs
+                    {
+                        RoutedEvent = Thumb.DragCompletedEvent,
+                        Vector = new Vector(0, 0)
+                    };
+
+                    track.Thumb.RaiseEvent(ev);
+                });
+
+            Assert.Equal(ScrollEventType.EndScroll, raisedEvent.Arguments.ScrollEventType);
+        }
+
         [Fact]
         public void ScrollBar_Can_AutoHide()
         {