Browse Source

Merge branch 'master' into fixes/StylableTabItem

pull/2496/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
e546124a2d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 111
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  2. 125
      src/Avalonia.Controls/Presenters/ItemContainerSync.cs
  3. 48
      src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
  4. 9
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  5. 11
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  6. 110
      src/Avalonia.Visuals/Media/FontFamily.cs
  7. 35
      src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
  8. 2
      src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
  9. 6
      src/Avalonia.X11/XIStructs.cs
  10. 38
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  11. 753
      tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
  12. 48
      tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs
  13. 11
      tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs

111
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@ -38,7 +38,6 @@ namespace Avalonia.Controls.Presenters
Carousel.PageTransitionProperty.AddOwner<CarouselPresenter>(); Carousel.PageTransitionProperty.AddOwner<CarouselPresenter>();
private int _selectedIndex = -1; private int _selectedIndex = -1;
// private Task _current;
private Task _currentTransition; private Task _currentTransition;
private int _queuedTransitionIndex = -1; private int _queuedTransitionIndex = -1;
@ -47,6 +46,7 @@ namespace Avalonia.Controls.Presenters
/// </summary> /// </summary>
static CarouselPresenter() static CarouselPresenter()
{ {
IsVirtualizedProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.IsVirtualizedChanged);
SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.SelectedIndexChanged); SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.SelectedIndexChanged);
} }
@ -94,58 +94,67 @@ namespace Avalonia.Controls.Presenters
set { SetValue(PageTransitionProperty, value); } set { SetValue(PageTransitionProperty, value); }
} }
/// <inheritdoc/>
protected override void PanelCreated(IPanel panel)
{
#pragma warning disable 4014
MoveToPage(-1, SelectedIndex);
#pragma warning restore 4014
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void ItemsChanged(NotifyCollectionChangedEventArgs e) protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) if (!IsVirtualized)
{ {
case NotifyCollectionChangedAction.Remove: base.ItemsChanged(e);
if (!IsVirtualized)
{
var generator = ItemContainerGenerator;
var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
#pragma warning disable 4014 if (Items == null || SelectedIndex >= Items.Count())
MoveToPage(-1, SelectedIndex); {
#pragma warning restore 4014 SelectedIndex = Items.Count() - 1;
} }
break;
case NotifyCollectionChangedAction.Reset:
{
var generator = ItemContainerGenerator;
var containers = generator.Containers.ToList();
generator.Clear();
Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
#pragma warning disable 4014
var newIndex = SelectedIndex;
if(SelectedIndex < 0) foreach (var c in ItemContainerGenerator.Containers)
{
c.ContainerControl.IsVisible = c.Index == SelectedIndex;
}
}
else if (SelectedIndex != -1 && Panel != null)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex > SelectedIndex)
{ {
if(Items != null && Items.Count() > 0) return;
{
newIndex = 0;
}
else
{
newIndex = -1;
}
} }
break;
MoveToPage(-1, newIndex); case NotifyCollectionChangedAction.Remove:
#pragma warning restore 4014 if (e.OldStartingIndex > SelectedIndex)
} {
break; return;
}
break;
case NotifyCollectionChangedAction.Replace:
if (e.OldStartingIndex > SelectedIndex ||
e.OldStartingIndex + e.OldItems.Count - 1 < SelectedIndex)
{
return;
}
break;
case NotifyCollectionChangedAction.Move:
if (e.OldStartingIndex > SelectedIndex &&
e.NewStartingIndex > SelectedIndex)
{
return;
}
break;
}
if (Items == null || SelectedIndex >= Items.Count())
{
SelectedIndex = Items.Count() - 1;
}
Panel.Children.Clear();
ItemContainerGenerator.Clear();
if (SelectedIndex != -1)
{
GetOrCreateContainer(SelectedIndex);
}
} }
} }
@ -212,6 +221,18 @@ namespace Avalonia.Controls.Presenters
return container; return container;
} }
/// <summary>
/// Called when the <see cref="IsVirtualized"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private void IsVirtualizedChanged(AvaloniaPropertyChangedEventArgs e)
{
if (Panel != null)
{
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
/// <summary> /// <summary>
/// Called when the <see cref="SelectedIndex"/> property changes. /// Called when the <see cref="SelectedIndex"/> property changes.
/// </summary> /// </summary>

125
src/Avalonia.Controls/Presenters/ItemContainerSync.cs

@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Utils;
namespace Avalonia.Controls.Presenters
{
internal static class ItemContainerSync
{
public static void ItemsChanged(
ItemsPresenterBase owner,
IEnumerable items,
NotifyCollectionChangedEventArgs e)
{
var generator = owner.ItemContainerGenerator;
var panel = owner.Panel;
if (panel == null)
{
return;
}
void Add()
{
if (e.NewStartingIndex + e.NewItems.Count < items.Count())
{
generator.InsertSpace(e.NewStartingIndex, e.NewItems.Count);
}
AddContainers(owner, e.NewStartingIndex, e.NewItems);
}
void Remove()
{
RemoveContainers(panel, generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count));
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Add();
break;
case NotifyCollectionChangedAction.Remove:
Remove();
break;
case NotifyCollectionChangedAction.Replace:
RemoveContainers(panel, generator.Dematerialize(e.OldStartingIndex, e.OldItems.Count));
var containers = AddContainers(owner, e.NewStartingIndex, e.NewItems);
var i = e.NewStartingIndex;
foreach (var container in containers)
{
panel.Children[i++] = container.ContainerControl;
}
break;
case NotifyCollectionChangedAction.Move:
Remove();
Add();
break;
case NotifyCollectionChangedAction.Reset:
RemoveContainers(panel, generator.Clear());
if (items != null)
{
AddContainers(owner, 0, items);
}
break;
}
}
private static IList<ItemContainerInfo> AddContainers(
ItemsPresenterBase owner,
int index,
IEnumerable items)
{
var generator = owner.ItemContainerGenerator;
var result = new List<ItemContainerInfo>();
var panel = owner.Panel;
foreach (var item in items)
{
var i = generator.Materialize(index++, item, owner.MemberSelector);
if (i.ContainerControl != null)
{
if (i.Index < panel.Children.Count)
{
// TODO: This will insert at the wrong place when there are null items.
panel.Children.Insert(i.Index, i.ContainerControl);
}
else
{
panel.Children.Add(i.ContainerControl);
}
}
result.Add(i);
}
return result;
}
private static void RemoveContainers(
IPanel panel,
IEnumerable<ItemContainerInfo> items)
{
foreach (var i in items)
{
if (i.ContainerControl != null)
{
panel.Children.Remove(i.ContainerControl);
}
}
}
}
}

48
src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs

@ -60,53 +60,7 @@ namespace Avalonia.Controls.Presenters
public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e) public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
{ {
base.ItemsChanged(items, e); base.ItemsChanged(items, e);
ItemContainerSync.ItemsChanged(Owner, items, e);
var generator = Owner.ItemContainerGenerator;
var panel = Owner.Panel;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex + e.NewItems.Count < Items.Count())
{
generator.InsertSpace(e.NewStartingIndex, e.NewItems.Count);
}
AddContainers(e.NewStartingIndex, e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveContainers(generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count));
break;
case NotifyCollectionChangedAction.Replace:
RemoveContainers(generator.Dematerialize(e.OldStartingIndex, e.OldItems.Count));
var containers = AddContainers(e.NewStartingIndex, e.NewItems);
var i = e.NewStartingIndex;
foreach (var container in containers)
{
panel.Children[i++] = container.ContainerControl;
}
break;
case NotifyCollectionChangedAction.Move:
// TODO: Handle move in a more efficient manner. At the moment we just
// drop through to Reset to recreate all the containers.
case NotifyCollectionChangedAction.Reset:
RemoveContainers(generator.Clear());
if (Items != null)
{
AddContainers(0, Items);
}
break;
}
Owner.InvalidateMeasure(); Owner.InvalidateMeasure();
} }

9
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -7,6 +7,7 @@ using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Styling; using Avalonia.Styling;
namespace Avalonia.Controls.Presenters namespace Avalonia.Controls.Presenters
@ -205,7 +206,13 @@ namespace Avalonia.Controls.Presenters
/// has been set, the items collection has been modified, or the panel has been created. /// has been set, the items collection has been modified, or the panel has been created.
/// </summary> /// </summary>
/// <param name="e">A description of the change.</param> /// <param name="e">A description of the change.</param>
protected abstract void ItemsChanged(NotifyCollectionChangedEventArgs e); /// <remarks>
/// The panel is guaranteed to be created when this method is called.
/// </remarks>
protected virtual void ItemsChanged(NotifyCollectionChangedEventArgs e)
{
ItemContainerSync.ItemsChanged(this, Items, e);
}
/// <summary> /// <summary>
/// Creates the <see cref="Panel"/> when <see cref="ApplyTemplate"/> is called for the first /// Creates the <see cref="Panel"/> when <see cref="ApplyTemplate"/> is called for the first

11
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -325,14 +325,19 @@ namespace Avalonia.Controls.Primitives
if (_updateCount == 0) if (_updateCount == 0)
{ {
var newIndex = -1;
if (SelectedIndex != -1) if (SelectedIndex != -1)
{ {
SelectedIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem); newIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem);
} }
else if (AlwaysSelected && Items != null && Items.Cast<object>().Any())
if (AlwaysSelected && Items != null && Items.Cast<object>().Any())
{ {
SelectedIndex = 0; newIndex = 0;
} }
SelectedIndex = newIndex;
} }
} }

110
src/Avalonia.Visuals/Media/FontFamily.cs

@ -11,46 +11,49 @@ namespace Avalonia.Media
{ {
public class FontFamily public class FontFamily
{ {
/// <inheritdoc />
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class. /// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary> /// </summary>
/// <param name="name">The name of the <see cref="FontFamily"/>.</param> /// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception> public FontFamily(string name) : this(null, name)
public FontFamily(string name)
{ {
Contract.Requires<ArgumentNullException>(name != null);
FamilyNames = new FamilyNameCollection(new[] { name });
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class. /// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary> /// </summary>
/// <param name="names">The names of the <see cref="FontFamily"/>.</param> /// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception> /// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
public FontFamily(IEnumerable<string> names) /// <exception cref="T:System.ArgumentException">Base uri must be an absolute uri.</exception>
public FontFamily(Uri baseUri, string name)
{ {
Contract.Requires<ArgumentNullException>(names != null); if (string.IsNullOrEmpty(name))
{
FamilyNames = new FamilyNameCollection(string.Empty);
FamilyNames = new FamilyNameCollection(names); return;
} }
/// <inheritdoc /> var fontFamilySegment = GetFontFamilyIdentifier(name);
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class. if (fontFamilySegment.Source != null)
/// </summary> {
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param> if (baseUri != null && !baseUri.IsAbsoluteUri)
/// <param name="source">The source of font resources.</param> {
/// <param name="baseUri"></param> throw new ArgumentException("Base uri must be an absolute uri.", nameof(baseUri));
public FontFamily(string name, Uri source, Uri baseUri = null) : this(name) }
{
Key = new FontFamilyKey(source, baseUri); Key = new FontFamilyKey(fontFamilySegment.Source, baseUri);
}
FamilyNames = new FamilyNameCollection(fontFamilySegment.Name);
} }
/// <summary> /// <summary>
/// Represents the default font family /// Represents the default font family
/// </summary> /// </summary>
public static FontFamily Default => new FontFamily(String.Empty); public static FontFamily Default => new FontFamily(string.Empty);
/// <summary> /// <summary>
/// Represents all font families in the system. This can be an expensive call depending on platform implementation. /// Represents all font families in the system. This can be an expensive call depending on platform implementation.
@ -88,46 +91,40 @@ namespace Avalonia.Media
/// <param name="s"></param> /// <param name="s"></param>
public static implicit operator FontFamily(string s) public static implicit operator FontFamily(string s)
{ {
return Parse(s); return new FontFamily(s);
} }
/// <summary> private struct FontFamilyIdentifier
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
{ {
if (string.IsNullOrEmpty(s)) public FontFamilyIdentifier(string name, Uri source)
{ {
throw new ArgumentException("Specified family is not supported."); Name = name;
Source = source;
} }
var segments = s.Split('#'); public string Name { get; }
public Uri Source { get; }
}
private static FontFamilyIdentifier GetFontFamilyIdentifier(string name)
{
var segments = name.Split('#');
switch (segments.Length) switch (segments.Length)
{ {
case 1: case 1:
{ {
var names = segments[0].Split(',') return new FontFamilyIdentifier(segments[0], null);
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x));
return new FontFamily(names);
} }
case 2: case 2:
{ {
var uri = segments[0].StartsWith("/") var source = segments[0].StartsWith("/")
? new Uri(segments[0], UriKind.Relative) ? new Uri(segments[0], UriKind.Relative)
: new Uri(segments[0], UriKind.RelativeOrAbsolute); : new Uri(segments[0], UriKind.RelativeOrAbsolute);
return uri.IsAbsoluteUri return new FontFamilyIdentifier(segments[1], source);
? new FontFamily(segments[1], uri)
: new FontFamily(segments[1], uri, baseUri);
} }
default: default:
@ -137,6 +134,25 @@ namespace Avalonia.Media
} }
} }
/// <summary>
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("Specified family is not supported.", nameof(s));
}
return new FontFamily(baseUri, s);
}
/// <summary> /// <summary>
/// Returns a <see cref="string" /> that represents this instance. /// Returns a <see cref="string" /> that represents this instance.
/// </summary> /// </summary>

35
src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs

@ -4,31 +4,28 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text;
namespace Avalonia.Media.Fonts namespace Avalonia.Media.Fonts
{ {
using System.Text;
public class FamilyNameCollection : IEnumerable<string> public class FamilyNameCollection : IEnumerable<string>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FamilyNameCollection"/> class. /// Initializes a new instance of the <see cref="FamilyNameCollection"/> class.
/// </summary> /// </summary>
/// <param name="familyNames">The family names.</param> /// <param name="familyNames">The family names.</param>
/// <exception cref="ArgumentException">familyNames</exception> /// <exception cref="ArgumentException">familyNames</exception>
public FamilyNameCollection(IEnumerable<string> familyNames) public FamilyNameCollection(string familyNames)
{ {
Contract.Requires<ArgumentNullException>(familyNames != null); if (familyNames == null)
{
var names = new List<string>(familyNames); throw new ArgumentNullException(nameof(familyNames));
}
if (names.Count == 0) throw new ArgumentException($"{nameof(familyNames)} must not be empty.");
Names = new ReadOnlyCollection<string>(names); Names = familyNames.Split(',').Select(x => x.Trim()).ToArray();
PrimaryFamilyName = Names.First(); PrimaryFamilyName = Names[0];
HasFallbacks = Names.Count > 1; HasFallbacks = Names.Count > 1;
} }
@ -55,7 +52,7 @@ namespace Avalonia.Media.Fonts
/// <value> /// <value>
/// The names. /// The names.
/// </value> /// </value>
internal ReadOnlyCollection<string> Names { get; } internal IReadOnlyList<string> Names { get; }
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
@ -95,7 +92,10 @@ namespace Avalonia.Media.Fonts
{ {
builder.Append(Names[index]); builder.Append(Names[index]);
if (index == Names.Count - 1) break; if (index == Names.Count - 1)
{
break;
}
builder.Append(", "); builder.Append(", ");
} }
@ -123,9 +123,12 @@ namespace Avalonia.Media.Fonts
/// </returns> /// </returns>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (!(obj is FamilyNameCollection other)) return false; if (!(obj is FamilyNameCollection other))
{
return false;
}
return other.ToString().Equals(ToString()); return other.ToString().Equals(ToString());
} }
} }
} }

2
src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs

@ -95,7 +95,7 @@ namespace Avalonia.Media.Fonts
{ {
if (!Source.IsAbsoluteUri && BaseUri != null) if (!Source.IsAbsoluteUri && BaseUri != null)
{ {
return BaseUri.Authority + Source; return BaseUri.AbsoluteUri + Source.OriginalString;
} }
return Source.ToString(); return Source.ToString();

6
src/Avalonia.X11/XIStructs.cs

@ -197,7 +197,7 @@ namespace Avalonia.X11
unsafe struct XIDeviceChangedEvent unsafe struct XIDeviceChangedEvent
{ {
public int Type; /* GenericEvent */ public int Type; /* GenericEvent */
public ulong Serial; /* # of last request processed by server */ public UIntPtr Serial; /* # of last request processed by server */
public Bool SendEvent; /* true if this came from a SendEvent request */ public Bool SendEvent; /* true if this came from a SendEvent request */
public IntPtr Display; /* Display the event was read from */ public IntPtr Display; /* Display the event was read from */
public int Extension; /* XI extension offset */ public int Extension; /* XI extension offset */
@ -214,7 +214,7 @@ namespace Avalonia.X11
struct XIDeviceEvent struct XIDeviceEvent
{ {
public XEventName type; /* GenericEvent */ public XEventName type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */ public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */ public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */ public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */ public int extension; /* XI extension offset */
@ -241,7 +241,7 @@ namespace Avalonia.X11
unsafe struct XIEvent unsafe struct XIEvent
{ {
public int type; /* GenericEvent */ public int type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */ public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */ public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */ public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */ public int extension; /* XI extension offset */

38
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@ -76,25 +76,6 @@ namespace Avalonia.Controls.UnitTests
Assert.Single(target.ItemContainerGenerator.Containers); Assert.Single(target.ItemContainerGenerator.Containers);
} }
[Fact]
public void Should_Not_Remove_NonCurrent_Page_When_IsVirtualized_False()
{
var target = new Carousel
{
Template = new FuncControlTemplate<Carousel>(CreateTemplate),
Items = new[] { "foo", "bar" },
IsVirtualized = false,
SelectedIndex = 0,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
}
[Fact] [Fact]
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes() public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
{ {
@ -115,9 +96,9 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren()); Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single(); var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child); Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text); Assert.Equal("Foo", ((TextBlock)child).Text);
@ -127,7 +108,7 @@ namespace Avalonia.Controls.UnitTests
target.Items = newItems; target.Items = newItems;
child = target.GetLogicalChildren().Single(); child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child); Assert.IsType<TextBlock>(child);
Assert.Equal("Bar", ((TextBlock)child).Text); Assert.Equal("Bar", ((TextBlock)child).Text);
@ -146,7 +127,8 @@ namespace Avalonia.Controls.UnitTests
var target = new Carousel var target = new Carousel
{ {
Template = new FuncControlTemplate<Carousel>(CreateTemplate), Template = new FuncControlTemplate<Carousel>(CreateTemplate),
Items = items Items = items,
IsVirtualized = true,
}; };
target.ApplyTemplate(); target.ApplyTemplate();
@ -190,9 +172,9 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren()); Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single(); var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child); Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text); Assert.Equal("Foo", ((TextBlock)child).Text);
@ -254,16 +236,16 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren()); Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single(); var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child); Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text); Assert.Equal("Foo", ((TextBlock)child).Text);
items.RemoveAt(0); items.RemoveAt(0);
child = target.GetLogicalChildren().Single(); child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child); Assert.IsType<TextBlock>(child);
Assert.Equal("Bar", ((TextBlock)child).Text); Assert.Equal("Bar", ((TextBlock)child).Text);

753
tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs

@ -8,6 +8,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Xunit; using Xunit;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections;
namespace Avalonia.Controls.UnitTests.Presenters namespace Avalonia.Controls.UnitTests.Presenters
{ {
@ -49,148 +50,672 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.IsType<ItemContainerGenerator<TestItem>>(target.ItemContainerGenerator); Assert.IsType<ItemContainerGenerator<TestItem>>(target.ItemContainerGenerator);
} }
[Fact] public class Virtualized
public void Setting_SelectedIndex_Should_Show_Page()
{ {
var target = new CarouselPresenter [Fact]
public void Should_Initially_Materialize_Selected_Container()
{ {
Items = new[] { "foo", "bar" }, var target = new CarouselPresenter
SelectedIndex = 0, {
}; Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate(); target.ApplyTemplate();
Assert.IsType<ContentPresenter>(target.Panel.Children[0]); AssertSingle(target);
Assert.Equal("foo", ((ContentPresenter)target.Panel.Children[0]).Content); }
}
[Fact] [Fact]
public void Changing_SelectedIndex_Should_Show_Page() public void Should_Initially_Materialize_Nothing_If_No_Selected_Container()
{
var target = new CarouselPresenter
{ {
Items = new[] { "foo", "bar" }, var target = new CarouselPresenter
SelectedIndex = 0, {
}; Items = new[] { "foo", "bar" },
IsVirtualized = true,
};
target.ApplyTemplate(); target.ApplyTemplate();
target.SelectedIndex = 1;
Assert.IsType<ContentPresenter>(target.Panel.Children[0]); Assert.Empty(target.Panel.Children);
Assert.Equal("bar", ((ContentPresenter)target.Panel.Children[0]).Content); Assert.Empty(target.ItemContainerGenerator.Containers);
} }
[Fact] [Fact]
public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True() public void Switching_To_Virtualized_Should_Reset_Containers()
{
var target = new CarouselPresenter
{ {
Items = new[] { "foo", "bar" }, var target = new CarouselPresenter
IsVirtualized = true, {
SelectedIndex = 0, Items = new[] { "foo", "bar" },
}; SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate(); target.ApplyTemplate();
Assert.Single(target.ItemContainerGenerator.Containers); target.IsVirtualized = true;
target.SelectedIndex = 1;
Assert.Single(target.ItemContainerGenerator.Containers);
}
[Fact] AssertSingle(target);
public void Should_Not_Remove_NonCurrent_Page_When_IsVirtualized_False() }
{
var target = new CarouselPresenter [Fact]
public void Changing_SelectedIndex_Should_Show_Page()
{ {
Items = new[] { "foo", "bar" }, var target = new CarouselPresenter
IsVirtualized = false, {
SelectedIndex = 0, Items = new[] { "foo", "bar" },
}; SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
AssertSingle(target);
target.SelectedIndex = 1;
AssertSingle(target);
}
target.ApplyTemplate(); [Fact]
Assert.Single(target.ItemContainerGenerator.Containers); public void Should_Remove_NonCurrent_Page()
Assert.Single(target.Panel.Children); {
target.SelectedIndex = 1; var target = new CarouselPresenter
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count()); {
Assert.Equal(2, target.Panel.Children.Count); Items = new[] { "foo", "bar" },
target.SelectedIndex = 0; IsVirtualized = true,
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count()); SelectedIndex = 0,
Assert.Equal(2, target.Panel.Children.Count); };
}
[Fact] target.ApplyTemplate();
public void Should_Remove_Controls_When_IsVirtualized_Is_False() AssertSingle(target);
{
ObservableCollection<string> items = new ObservableCollection<string>(); target.SelectedIndex = 1;
AssertSingle(target);
var target = new CarouselPresenter
}
[Fact]
public void Should_Handle_Inserting_Item_At_SelectedItem()
{ {
Items = items, var items = new ObservableCollection<string>
SelectedIndex = 0, {
IsVirtualized = false, "item0",
}; "item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertSingle(target);
}
target.ApplyTemplate(); [Fact]
target.SelectedIndex = 0; public void Should_Handle_Inserting_Item_Before_SelectedItem()
items.Add("foo"); {
target.SelectedIndex = 0; var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Inserting_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items.Insert(2, "after");
Assert.Same(child, AssertSingle(target));
}
Assert.Single(target.ItemContainerGenerator.Containers); [Fact]
Assert.Single(target.Panel.Children); public void Should_Handle_Removing_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(1);
AssertSingle(target);
}
items.Add("bar"); [Fact]
Assert.Single(target.ItemContainerGenerator.Containers); public void Should_Handle_Removing_Item_Before_SelectedItem()
Assert.Single(target.Panel.Children); {
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(0);
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Removing_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items.RemoveAt(2);
Assert.Same(child, AssertSingle(target));
}
target.SelectedIndex = 1; [Fact]
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count()); public void Should_Handle_Removing_SelectedItem_When_Its_Last()
Assert.Equal(2, target.Panel.Children.Count); {
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(2);
Assert.Equal(1, target.SelectedIndex);
AssertSingle(target);
}
items.Remove(items[0]); [Fact]
Assert.Single(target.ItemContainerGenerator.Containers); public void Should_Handle_Removing_Last_Item()
Assert.Single(target.Panel.Children); {
var items = new ObservableCollection<string>
{
"item0",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(0);
Assert.Empty(target.Panel.Children);
Assert.Empty(target.ItemContainerGenerator.Containers);
}
items.Remove(items[0]); [Fact]
Assert.Empty(target.ItemContainerGenerator.Containers); public void Should_Handle_Replacing_SelectedItem()
Assert.Empty(target.Panel.Children); {
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items[1] = "replaced";
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Replacing_Non_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items[0] = "replaced";
Assert.Same(child, AssertSingle(target));
}
[Fact]
public void Should_Handle_Moving_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Move(1, 0);
AssertSingle(target);
}
private static IControl AssertSingle(CarouselPresenter target)
{
var items = (IList)target.Items;
var index = target.SelectedIndex;
var content = items[index];
var child = Assert.Single(target.Panel.Children);
var presenter = Assert.IsType<ContentPresenter>(child);
var container = Assert.Single(target.ItemContainerGenerator.Containers);
var visible = Assert.Single(target.Panel.Children.Where(x => x.IsVisible));
Assert.Same(child, container.ContainerControl);
Assert.Same(child, visible);
Assert.Equal(content, presenter.Content);
Assert.Equal(content, container.Item);
Assert.Equal(index, container.Index);
return child;
}
} }
[Fact] public class NonVirtualized
public void Should_Have_Correct_ItemsContainer_Index()
{ {
ObservableCollection<string> items = new ObservableCollection<string>(); [Fact]
public void Should_Initially_Materialize_All_Containers()
{
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
IsVirtualized = false,
};
target.ApplyTemplate();
AssertAll(target);
}
var target = new CarouselPresenter [Fact]
public void Should_Initially_Show_Selected_Item()
{ {
Items = items, var target = new CarouselPresenter
SelectedIndex = 0, {
IsVirtualized = false, Items = new[] { "foo", "bar" },
}; SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
AssertAll(target);
}
target.ApplyTemplate(); [Fact]
target.SelectedIndex = 0; public void Switching_To_Non_Virtualized_Should_Reset_Containers()
items.Add("foo"); {
target.SelectedIndex = 0; var target = new CarouselPresenter
{
Assert.Single(target.ItemContainerGenerator.Containers); Items = new[] { "foo", "bar" },
Assert.Single(target.Panel.Children); SelectedIndex = 0,
IsVirtualized = true,
items.Add("bar"); };
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children); target.ApplyTemplate();
target.IsVirtualized = false;
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count()); AssertAll(target);
Assert.Equal(2, target.Panel.Children.Count); }
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
[Fact]
items.Remove(items[0]); public void Changing_SelectedIndex_Should_Show_Page()
Assert.Single(target.ItemContainerGenerator.Containers); {
Assert.Single(target.Panel.Children); var target = new CarouselPresenter
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index); {
Items = new[] { "foo", "bar" },
items.Remove(items[0]); SelectedIndex = 0,
Assert.Empty(target.ItemContainerGenerator.Containers); IsVirtualized = false,
Assert.Empty(target.Panel.Children); };
target.ApplyTemplate();
AssertAll(target);
target.SelectedIndex = 1;
AssertAll(target);
}
[Fact]
public void Should_Handle_Inserting_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertAll(target);
}
[Fact]
public void Should_Handle_Inserting_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertAll(target);
}
[Fact]
public void Should_Do_Handle_Inserting_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(2, "after");
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(1);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(0);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(2);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_SelectedItem_When_Its_Last()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(2);
Assert.Equal(1, target.SelectedIndex);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Last_Item()
{
var items = new ObservableCollection<string>
{
"item0",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(0);
Assert.Empty(target.Panel.Children);
Assert.Empty(target.ItemContainerGenerator.Containers);
}
[Fact]
public void Should_Handle_Replacing_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items[1] = "replaced";
AssertAll(target);
}
[Fact]
public void Should_Handle_Moving_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Move(1, 0);
AssertAll(target);
}
private static void AssertAll(CarouselPresenter target)
{
var items = (IList)target.Items;
Assert.Equal(items?.Count ?? 0, target.Panel.Children.Count);
Assert.Equal(items?.Count ?? 0, target.ItemContainerGenerator.Containers.Count());
for (var i = 0; i < items?.Count; ++i)
{
var content = items[i];
var child = target.Panel.Children[i];
var presenter = Assert.IsType<ContentPresenter>(child);
var container = target.ItemContainerGenerator.ContainerFromIndex(i);
Assert.Same(child, container);
Assert.Equal(i == target.SelectedIndex, child.IsVisible);
Assert.Equal(content, presenter.Content);
Assert.Equal(i, target.ItemContainerGenerator.IndexFromContainer(container));
}
}
} }
private class TestItem : ContentControl private class TestItem : ContentControl

48
tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Fonts; using Avalonia.Media.Fonts;
@ -12,18 +11,6 @@ namespace Avalonia.Visuals.UnitTests.Media
{ {
public class FontFamilyTests public class FontFamilyTests
{ {
[Fact]
public void Exception_Should_Be_Thrown_If_Name_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new FontFamily((string)null));
}
[Fact]
public void Exception_Should_Be_Thrown_If_Names_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new FontFamily((IEnumerable<string>)null));
}
[Fact] [Fact]
public void Should_Implicitly_Convert_String_To_FontFamily() public void Should_Implicitly_Convert_String_To_FontFamily()
{ {
@ -41,7 +28,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Name() public void Should_Parse_FontFamily_With_SystemFont_Name()
{ {
var fontFamily = FontFamily.Parse("Courier New"); var fontFamily = FontFamily.Parse("Courier New");
@ -49,7 +36,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Names() public void Should_Parse_FontFamily_With_Fallbacks()
{ {
var fontFamily = FontFamily.Parse("Courier New, Times New Roman"); var fontFamily = FontFamily.Parse("Courier New, Times New Roman");
@ -61,7 +48,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Resource_Folder() public void Should_Parse_FontFamily_With_Resource_Folder()
{ {
var source = new Uri("resm:Avalonia.Visuals.UnitTests#MyFont"); var source = new Uri("resm:Avalonia.Visuals.UnitTests#MyFont");
@ -75,7 +62,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Resource_Filename() public void Should_Parse_FontFamily_With_Resource_Filename()
{ {
var source = new Uri("resm:Avalonia.Visuals.UnitTests.MyFont.ttf#MyFont"); var source = new Uri("resm:Avalonia.Visuals.UnitTests.MyFont.ttf#MyFont");
@ -87,5 +74,32 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Equal(key, fontFamily.Key); Assert.Equal(key, fontFamily.Key);
} }
[Theory]
[InlineData("resm:Avalonia.Visuals.UnitTests/Assets/Fonts#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts#MyFont")]
public void Should_Create_FontFamily_From_Uri(string name)
{
var fontFamily = new FontFamily(name);
Assert.Equal("MyFont", fontFamily.Name);
Assert.NotNull(fontFamily.Key);
}
[Theory]
[InlineData("resm:Avalonia.Visuals.UnitTests.Assets.Fonts", "#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts", "#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests", "/Assets/Fonts#MyFont")]
public void Should_Create_FontFamily_From_Uri_With_Base_Uri(string @base, string name)
{
var baseUri = new Uri(@base);
var fontFamily = new FontFamily(baseUri, name);
Assert.Equal("MyFont", fontFamily.Name);
Assert.NotNull(fontFamily.Key);
}
} }
} }

11
tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Linq;
using Avalonia.Media.Fonts; using Avalonia.Media.Fonts;
using Xunit; using Xunit;
@ -16,18 +15,12 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts
Assert.Throws<ArgumentNullException>(() => new FamilyNameCollection(null)); Assert.Throws<ArgumentNullException>(() => new FamilyNameCollection(null));
} }
[Fact]
public void Exception_Should_Be_Thrown_If_Names_Is_Empty()
{
Assert.Throws<ArgumentException>(() => new FamilyNameCollection(Enumerable.Empty<string>()));
}
[Fact] [Fact]
public void Should_Be_Equal() public void Should_Be_Equal()
{ {
var familyNames = new FamilyNameCollection(new[] { "Arial", "Times New Roman" }); var familyNames = new FamilyNameCollection("Arial, Times New Roman");
Assert.Equal(new FamilyNameCollection(new[] { "Arial", "Times New Roman" }), familyNames); Assert.Equal(new FamilyNameCollection("Arial, Times New Roman"), familyNames);
} }
} }
} }

Loading…
Cancel
Save