diff --git a/samples/BindingTest/App.config b/samples/BindingTest/App.config index d1428ad712..a54d71fef0 100644 --- a/samples/BindingTest/App.config +++ b/samples/BindingTest/App.config @@ -1,6 +1,18 @@ - + - + + + + + + + + + + + + + diff --git a/samples/BindingTest/App.xaml.cs b/samples/BindingTest/App.xaml.cs index 5ffac249e9..ef5f7ba4d9 100644 --- a/samples/BindingTest/App.xaml.cs +++ b/samples/BindingTest/App.xaml.cs @@ -13,30 +13,20 @@ namespace BindingTest public App() { RegisterServices(); - InitializeSubsystems((int)Environment.OSVersion.Platform); - InitializeComponent(); - InitializeLogging(); - } - - public static void AttachDevTools(Window window) - { - DevTools.Attach(window); } private static void Main() { - var app = new App(); - var window = new MainWindow(); - window.Show(); - app.Run(window); - } + InitializeLogging(); - private void InitializeComponent() - { - PerspexXamlLoader.Load(this); + new App() + .UseWin32() + .UseDirect2D() + .LoadFromXaml() + .RunWithMainWindow(); } - private void InitializeLogging() + private static void InitializeLogging() { #if DEBUG SerilogLogger.Initialize(new LoggerConfiguration() diff --git a/samples/BindingTest/MainWindow.xaml.cs b/samples/BindingTest/MainWindow.xaml.cs index a8e64485cb..00b46faecf 100644 --- a/samples/BindingTest/MainWindow.xaml.cs +++ b/samples/BindingTest/MainWindow.xaml.cs @@ -1,4 +1,5 @@ using BindingTest.ViewModels; +using Perspex; using Perspex.Controls; using Perspex.Markup.Xaml; @@ -10,12 +11,12 @@ namespace BindingTest { this.InitializeComponent(); this.DataContext = new MainWindowViewModel(); - App.AttachDevTools(this); + this.AttachDevTools(); } private void InitializeComponent() { - PerspexXamlLoader.Load(this); + this.LoadFromXaml(); } } } diff --git a/samples/ControlCatalog/App.config b/samples/ControlCatalog/App.config index 7ef9d715f8..0c47592f2c 100644 --- a/samples/ControlCatalog/App.config +++ b/samples/ControlCatalog/App.config @@ -1,13 +1,21 @@ - + - + - - + + + + + + + + + + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 2930fa746a..5ee9373edb 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -9,58 +9,14 @@ using Serilog; namespace ControlCatalog { + // Eventually we should move this into a PCL library so we can access + // from mobile platforms + // class App : Application { public App() { RegisterServices(); - InitializeSubsystems(GetPlatformId()); - InitializeLogging(); - InitializeComponent(); - } - - public static void AttachDevTools(Window window) - { -#if DEBUG - DevTools.Attach(window); -#endif - } - - static void Main(string[] args) - { - var app = new App(); - var window = new MainWindow(); - window.Show(); - app.Run(window); - } - - private void InitializeComponent() - { - PerspexXamlLoader.Load(this); - } - - private void InitializeLogging() - { -#if DEBUG - SerilogLogger.Initialize(new LoggerConfiguration() - .MinimumLevel.Warning() - .WriteTo.Trace(outputTemplate: "{Area}: {Message}") - .CreateLogger()); -#endif - } - - private int GetPlatformId() - { - var args = Environment.GetCommandLineArgs(); - - if (args.Contains("--gtk")) - { - return (int)PlatformID.Unix; - } - else - { - return (int)Environment.OSVersion.Platform; - } } } } diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 087998c805..36238efc7c 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -103,6 +103,7 @@ SliderPage.xaml + diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 5a5b7d4303..62949a9abd 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using Perspex.Controls; +using Perspex; +using Perspex.Controls; using Perspex.Markup.Xaml; namespace ControlCatalog @@ -8,12 +9,12 @@ namespace ControlCatalog public MainWindow() { this.InitializeComponent(); - App.AttachDevTools(this); + this.AttachDevTools(); } private void InitializeComponent() { - PerspexXamlLoader.Load(this); + this.LoadFromXaml(); } } } diff --git a/samples/ControlCatalog/Program.cs b/samples/ControlCatalog/Program.cs new file mode 100644 index 0000000000..d01a6aa11b --- /dev/null +++ b/samples/ControlCatalog/Program.cs @@ -0,0 +1,127 @@ +using Perspex.Logging.Serilog; +using Serilog; +using System; +using System.Linq; +using Perspex; + +namespace ControlCatalog +{ + internal class Program + { + static void Main(string[] args) + { + InitializeLogging(); + + new App() + .ConfigureRenderSystem(args) + .LoadFromXaml() + .RunWithMainWindow(); + } + + // This will be made into a runtime configuration extension soon! + private static void InitializeLogging() + { +#if DEBUG + SerilogLogger.Initialize(new LoggerConfiguration() + .MinimumLevel.Warning() + .WriteTo.Trace(outputTemplate: "{Area}: {Message}") + .CreateLogger()); +#endif + } + + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Experimental: Would like to move this into a shared location once I figure out the best place for it + // considering all common libraries are PCL and do not have access to Environment.OSVersion.Platform + // nor do they have access to the platform specific render/subsystem extensions. + // + // Perhaps via DI we register each system with a priority/rank + // + public static class RenderSystemExtensions + { + [Flags] + enum RenderSystem + { + None = 0, + GTK = 1, + Skia = 2, + Direct2D = 4 + }; + + /// + /// Default (Optimal) render system for a particular platform + /// + /// + private static RenderSystem DefaultRenderSystem() + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + return RenderSystem.GTK; + + case PlatformID.Unix: + return RenderSystem.GTK; + + case PlatformID.Win32Windows: + return RenderSystem.Direct2D; + } + + return RenderSystem.None; + } + + /// + /// Returns an array of avalidable rendering systems in priority order + /// + /// + private static RenderSystem[] AvailableRenderSystems() + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + return new RenderSystem[] { RenderSystem.GTK, RenderSystem.Skia }; + + case PlatformID.Unix: + return new RenderSystem[] { RenderSystem.GTK, RenderSystem.Skia }; + + case PlatformID.Win32Windows: + return new RenderSystem[] { RenderSystem.Direct2D, RenderSystem.Skia, RenderSystem.GTK }; + } + + return new RenderSystem[0]; + } + + /// + /// Selects the optimal render system for desktop platforms. Supports cmd line overrides + /// + /// + /// + public static TApp ConfigureRenderSystem(this TApp app, string[] args) where TApp : Application + { + // So this all works great under Windows where it can support + // ALL configurations. But on OSX/Unix we cannot use Direct2D + // + if (args.Contains("--gtk") || DefaultRenderSystem() == RenderSystem.GTK) + { + app.UseGtk(); + app.UseCairo(); + } + else + { + app.UseWin32(); + + // not available until we do the SkiaSharp merge + //if (args.Contains("--skia") || DefaultRenderSystem() == RenderSystem.Skia) + //{ + // app.UseSkia(); + //} + //else + { + app.UseDirect2D(); + } + } + + return app; + } + } +} diff --git a/samples/TestApplication/App.config b/samples/TestApplication/App.config index 6bcf42cd00..410577105c 100644 --- a/samples/TestApplication/App.config +++ b/samples/TestApplication/App.config @@ -23,11 +23,11 @@ - + - + diff --git a/samples/TestApplication/Program.cs b/samples/TestApplication/Program.cs index 7aa433bfa8..38b059afff 100644 --- a/samples/TestApplication/Program.cs +++ b/samples/TestApplication/Program.cs @@ -31,11 +31,30 @@ namespace TestApplication // The version of ReactiveUI currently included is for WPF and so expects a WPF // dispatcher. This makes sure it's initialized. System.Windows.Threading.Dispatcher foo = System.Windows.Threading.Dispatcher.CurrentDispatcher; - new App(); - MainWindow.RootNamespace = "TestApplication"; - var wnd = MainWindow.Create(); - DevTools.Attach(wnd); - Application.Current.Run(wnd); + + var app = new App(); + + if (args.Contains("--gtk")) + { + app.UseGtk(); + app.UseCairo(); + } + else + { + app.UseWin32(); + + // not available until we do the SkiaSharp merge + //if (args.Contains("--skia")) + //{ + // app.UseSkia(); + //} + //else + { + app.UseDirect2D(); + } + } + + app.Run(); } } } diff --git a/samples/TestApplicationShared/App.cs b/samples/TestApplicationShared/App.cs index 8bab852747..83987d26df 100644 --- a/samples/TestApplicationShared/App.cs +++ b/samples/TestApplicationShared/App.cs @@ -8,6 +8,9 @@ using Perspex.Controls.Templates; using Perspex.Markup.Xaml; using Perspex.Styling; using Perspex.Themes.Default; +using Perspex.Diagnostics; +using Perspex.Platform; +using Perspex.Shared.PlatformSupport; namespace TestApplication { @@ -15,34 +18,35 @@ namespace TestApplication { public App() { + // TODO: I believe this has to happen before we select sub systems. Can we + // move this safely into Application itself? Is there anything in here + // that is platform specific?? + // RegisterServices(); + } -#if !__IOS__ // IOS Startup flow is a bit different and cannot use this - RegisterPlatformCallback(PlatformInitialization); -#endif - - InitializeSubsystems((int)Environment.OSVersion.Platform); - + public void Run() + { Styles.Add(new DefaultTheme()); - var loader = new PerspexXamlLoader(); - var baseLight = (IStyle)loader.Load( - new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default")); - Styles.Add(baseLight); + var loader = new PerspexXamlLoader(); + var baseLight = (IStyle)loader.Load( + new Uri("resm:Perspex.Themes.Default.Accents.BaseLight.xaml?assembly=Perspex.Themes.Default")); + Styles.Add(baseLight); - Styles.Add(new SampleTabStyle()); - DataTemplates = new DataTemplates - { - new FuncTreeDataTemplate( - x => new TextBlock {Text = x.Name}, - x => x.Children), - }; - } + Styles.Add(new SampleTabStyle()); + DataTemplates = new DataTemplates + { + new FuncTreeDataTemplate( + x => new TextBlock {Text = x.Name}, + x => x.Children), + }; - protected virtual void PlatformInitialization() - { - // default behavior - InitializeSubsystems((int)Environment.OSVersion.Platform); + MainWindow.RootNamespace = "TestApplication"; + var wnd = MainWindow.Create(); + DevTools.Attach(wnd); + + Run(wnd); } } } diff --git a/samples/XamlTestApplication/App.config b/samples/XamlTestApplication/App.config index ccdb787c0f..46a3a2eb27 100644 --- a/samples/XamlTestApplication/App.config +++ b/samples/XamlTestApplication/App.config @@ -1,33 +1,33 @@ - + - + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/samples/XamlTestApplication/App.cs b/samples/XamlTestApplication/App.cs index 01ac91299c..9d5df3236c 100644 --- a/samples/XamlTestApplication/App.cs +++ b/samples/XamlTestApplication/App.cs @@ -7,26 +7,5 @@ using Serilog; namespace XamlTestApplication { - public class App : XamlTestApp - { - public App() - { - InitializeLogging(); - } - - protected override void RegisterPlatform() - { - InitializeSubsystems((int)Environment.OSVersion.Platform); - } - - private void InitializeLogging() - { -#if DEBUG - SerilogLogger.Initialize(new LoggerConfiguration() - .MinimumLevel.Warning() - .WriteTo.Trace(outputTemplate: "{Area}: {Message}") - .CreateLogger()); -#endif - } - } + // No longer needed! } diff --git a/samples/XamlTestApplication/Program.cs b/samples/XamlTestApplication/Program.cs index 76d1aa7efb..a6dc2a5501 100644 --- a/samples/XamlTestApplication/Program.cs +++ b/samples/XamlTestApplication/Program.cs @@ -1,39 +1,39 @@ // Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; +using System.Linq; using System.Diagnostics; using System.Windows.Threading; using Perspex; -using Perspex.Collections; -using Perspex.Controls; -using Perspex.Controls.Templates; -using ReactiveUI; -using XamlTestApplication.Views; +using Serilog; +using Perspex.Logging.Serilog; namespace XamlTestApplication { internal class Program { - private static void Main() + private static void Main(string[] args) { - var sw = new Stopwatch(); - sw.Start(); - + // this sucks. Can we fix this? Do we even need it anymore? var foo = Dispatcher.CurrentDispatcher; - App application = new App - { - - }; - - var window = new MainWindow(); - window.Show(); + InitializeLogging(); - sw.Stop(); - Debug.WriteLine($"Startup: {sw.ElapsedMilliseconds}ms"); + new XamlTestApp() + .UseWin32() + .UseDirect2D() + .LoadFromXaml() + .RunWithMainWindow(); + } - Application.Current.Run(window); + private static void InitializeLogging() + { +#if DEBUG + SerilogLogger.Initialize(new LoggerConfiguration() + .MinimumLevel.Warning() + .WriteTo.Trace(outputTemplate: "{Area}: {Message}") + .CreateLogger()); +#endif } } } \ No newline at end of file diff --git a/samples/XamlTestApplicationPcl/XamlTestApp.cs b/samples/XamlTestApplicationPcl/XamlTestApp.cs index a4d4ac2e0e..15f9ddb30e 100644 --- a/samples/XamlTestApplicationPcl/XamlTestApp.cs +++ b/samples/XamlTestApplicationPcl/XamlTestApp.cs @@ -1,23 +1,14 @@ using Perspex; using Perspex.Markup.Xaml; +using XamlTestApplication.Views; namespace XamlTestApplication { - public abstract class XamlTestApp : Application + public class XamlTestApp : Application { - protected abstract void RegisterPlatform(); - public XamlTestApp() { RegisterServices(); - RegisterPlatform(); - InitializeComponent(); - } - - private void InitializeComponent() - { - var loader = new PerspexXamlLoader(); - loader.Load(typeof(XamlTestApp), this); } } } diff --git a/src/Gtk/Perspex.Cairo/CairoPlatform.cs b/src/Gtk/Perspex.Cairo/CairoPlatform.cs index 2c72811bbb..4e2c6727a5 100644 --- a/src/Gtk/Perspex.Cairo/CairoPlatform.cs +++ b/src/Gtk/Perspex.Cairo/CairoPlatform.cs @@ -7,6 +7,18 @@ using Perspex.Cairo.Media.Imaging; using Perspex.Media; using Perspex.Platform; +namespace Perspex +{ + public static class GtkApplicationExtensions + { + public static TApp UseCairo(this TApp app) where TApp : Application + { + Perspex.Cairo.CairoPlatform.Initialize(); + return app; + } + } +} + namespace Perspex.Cairo { using System.IO; diff --git a/src/Gtk/Perspex.Gtk/GtkPlatform.cs b/src/Gtk/Perspex.Gtk/GtkPlatform.cs index b8a6d5c511..6a1d269dba 100644 --- a/src/Gtk/Perspex.Gtk/GtkPlatform.cs +++ b/src/Gtk/Perspex.Gtk/GtkPlatform.cs @@ -10,6 +10,18 @@ using Perspex.Input; using Perspex.Platform; using Perspex.Shared.PlatformSupport; +namespace Perspex +{ + public static class GtkApplicationExtensions + { + public static TApp UseGtk(this TApp app) where TApp : Application + { + Perspex.Gtk.GtkPlatform.Initialize(); + return app; + } + } +} + namespace Perspex.Gtk { using Gtk = global::Gtk; diff --git a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs index 588cc12f0e..a99582cea7 100644 --- a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs @@ -16,6 +16,22 @@ namespace Perspex.Markup.Xaml.Data /// public class Binding : IBinding { + /// + /// Initializes a new instance of the class. + /// + public Binding() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The binding path. + public Binding(string path) + { + Path = path; + } + /// /// Gets or sets the to use. /// diff --git a/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs b/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs index 389e646215..68ae90d60f 100644 --- a/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs +++ b/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs @@ -187,3 +187,15 @@ namespace Perspex.Markup.Xaml } } } + +namespace Perspex +{ + public static class XamlObjectExtensions + { + public static TObject LoadFromXaml(this TObject obj) + { + Markup.Xaml.PerspexXamlLoader.Load(obj); + return obj; + } + } +} diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index 6ff38d08f1..74899a336a 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -337,19 +337,13 @@ namespace Perspex.Controls IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent; /// - void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) - { - this.OnDetachedFromLogicalTreeCore(e); - } - - /// - void ISupportInitialize.BeginInit() + public virtual void BeginInit() { ++_initCount; } /// - void ISupportInitialize.EndInit() + public virtual void EndInit() { if (_initCount == 0) { @@ -373,14 +367,10 @@ namespace Perspex.Controls } } - /// - /// Gets a value which indicates whether a change to the is in - /// the process of being notified. - /// - protected bool IsDataContextChanging + /// + void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { - get; - private set; + this.OnDetachedFromLogicalTreeCore(e); } /// @@ -548,6 +538,21 @@ namespace Perspex.Controls base.OnDetachedFromVisualTreeCore(e); } + /// + /// Called before the property changes. + /// + protected virtual void OnDataContextChanging() + { + } + + /// + /// Called after the property changes. + /// + protected virtual void OnDataContextChanged() + { + DataContextChanged?.Invoke(this, EventArgs.Empty); + } + /// protected override void OnGotFocus(GotFocusEventArgs e) { @@ -598,15 +603,6 @@ namespace Perspex.Controls } } - /// - /// Called when the is changed and all subscribers to that change - /// have been notified. - /// - protected virtual void OnDataContextChanged() - { - DataContextChanged?.Invoke(this, EventArgs.Empty); - } - /// /// Called when the property begins and ends being notified. /// @@ -618,9 +614,11 @@ namespace Perspex.Controls if (control != null) { - control.IsDataContextChanging = notifying; - - if (!notifying) + if (notifying) + { + control.OnDataContextChanging(); + } + else { control.OnDataContextChanged(); } diff --git a/src/Perspex.Controls/Presenters/ContentPresenter.cs b/src/Perspex.Controls/Presenters/ContentPresenter.cs index f6a09f6cb1..0ea1eb4723 100644 --- a/src/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/src/Perspex.Controls/Presenters/ContentPresenter.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Reactive.Linq; using Perspex.Controls.Primitives; using Perspex.Controls.Templates; using Perspex.Layout; @@ -83,6 +84,17 @@ namespace Perspex.Controls.Presenters TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); } + /// + /// Initializes a new instance of the class. + /// + public ContentPresenter() + { + var dataContext = this.GetObservable(ContentProperty) + .Select(x => x is IControl ? PerspexProperty.UnsetValue : x); + + Bind(Control.DataContextProperty, dataContext); + } + /// /// Gets or sets a brush with which to paint the background. /// diff --git a/src/Perspex.Controls/Primitives/SelectingItemsControl.cs b/src/Perspex.Controls/Primitives/SelectingItemsControl.cs index 4d9482e7de..a141444754 100644 --- a/src/Perspex.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Perspex.Controls/Primitives/SelectingItemsControl.cs @@ -107,7 +107,9 @@ namespace Perspex.Controls.Primitives private IList _selectedItems; private bool _ignoreContainerSelectionChanged; private bool _syncingSelectedItems; - private IList _clearSelectedItemsAfterDataContextChanged; + private int _updateCount; + private int _updateSelectedIndex; + private IList _updateSelectedItems; /// /// Initializes static members of the class. @@ -117,13 +119,6 @@ namespace Perspex.Controls.Primitives IsSelectedChangedEvent.AddClassHandler(x => x.ContainerSelectionChanged); } - /// - /// Initializes a new instance of the class. - /// - public SelectingItemsControl() - { - } - /// /// Occurs when the control's selection changes. /// @@ -145,7 +140,6 @@ namespace Perspex.Controls.Primitives /// /// Gets or sets the index of the selected item. /// - [DependsOn(nameof(Items))] public int SelectedIndex { get @@ -155,14 +149,22 @@ namespace Perspex.Controls.Primitives set { - var old = SelectedIndex; - var effective = (value >= 0 && value < Items?.Cast().Count()) ? value : -1; + if (_updateCount == 0) + { + var old = SelectedIndex; + var effective = (value >= 0 && value < Items?.Cast().Count()) ? value : -1; - if (old != effective) + if (old != effective) + { + _selectedIndex = effective; + RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue); + SelectedItem = ElementAt(Items, effective); + } + } + else { - _selectedIndex = effective; - RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue); - SelectedItem = ElementAt(Items, effective); + _updateSelectedIndex = value; + _updateSelectedItems = null; } } } @@ -170,7 +172,6 @@ namespace Perspex.Controls.Primitives /// /// Gets or sets the selected item. /// - [DependsOn(nameof(Items))] public object SelectedItem { get @@ -180,45 +181,39 @@ namespace Perspex.Controls.Primitives set { - var old = SelectedItem; - var index = IndexOf(Items, value); - var effective = index != -1 ? value : null; - - if (!object.Equals(effective, old)) + if (_updateCount == 0) { - _selectedItem = effective; - RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue); - SelectedIndex = index; + var old = SelectedItem; + var index = IndexOf(Items, value); + var effective = index != -1 ? value : null; - if (effective != null) + if (!object.Equals(effective, old)) { - if (SelectedItems.Count != 1 || SelectedItems[0] != effective) + _selectedItem = effective; + RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue); + SelectedIndex = index; + + if (effective != null) { - _syncingSelectedItems = true; - SelectedItems.Clear(); - SelectedItems.Add(effective); - _syncingSelectedItems = false; + if (SelectedItems.Count != 1 || SelectedItems[0] != effective) + { + _syncingSelectedItems = true; + SelectedItems.Clear(); + SelectedItems.Add(effective); + _syncingSelectedItems = false; + } } - } - else if (SelectedItems.Count > 0) - { - if (!IsDataContextChanging) + else if (SelectedItems.Count > 0) { SelectedItems.Clear(); } - else - { - // The DataContext is changing, and it's quite possible that our - // selection is being cleared because both Items and SelectedItems - // are bound to something on the DataContext. However, if we clear - // the collection now, we may be clearing a the SelectedItems from - // the DataContext which is being unbound, so do it after DataContext - // has notified all interested parties, in - // the OnDataContextFinishedChanging method. - _clearSelectedItemsAfterDataContextChanged = SelectedItems; - } } } + else + { + _updateSelectedItems = new PerspexList(value); + _updateSelectedIndex = int.MinValue; + } } } @@ -240,6 +235,12 @@ namespace Perspex.Controls.Primitives set { + if (value?.IsFixedSize == true || value?.IsReadOnly == true) + { + throw new NotSupportedException( + "Cannot use a fixed size or read-only collection as SelectedItems."); + } + UnsubscribeFromSelectedItems(); _selectedItems = value ?? new PerspexList(); SubscribeToSelectedItems(); @@ -260,6 +261,25 @@ namespace Perspex.Controls.Primitives /// protected bool AlwaysSelected => (SelectionMode & SelectionMode.AlwaysSelected) != 0; + /// + public override void BeginInit() + { + base.BeginInit(); + ++_updateCount; + _updateSelectedIndex = int.MinValue; + } + + /// + public override void EndInit() + { + base.EndInit(); + + if (--_updateCount == 0) + { + UpdateFinished(); + } + } + /// /// Tries to get the container that was the source of an event. /// @@ -279,13 +299,16 @@ namespace Perspex.Controls.Primitives { base.ItemsChanged(e); - if (SelectedIndex != -1) - { - SelectedIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem); - } - else if (AlwaysSelected && Items != null && Items.Cast().Any()) + if (_updateCount == 0) { - SelectedIndex = 0; + if (SelectedIndex != -1) + { + SelectedIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem); + } + else if (AlwaysSelected && Items != null && Items.Cast().Any()) + { + SelectedIndex = 0; + } } } @@ -354,15 +377,22 @@ namespace Perspex.Controls.Primitives } } + /// + protected override void OnDataContextChanging() + { + base.OnDataContextChanging(); + ++_updateCount; + } + /// protected override void OnDataContextChanged() { - if (_clearSelectedItemsAfterDataContextChanged == SelectedItems) + base.OnDataContextChanged(); + + if (--_updateCount == 0) { - _clearSelectedItemsAfterDataContextChanged.Clear(); + UpdateFinished(); } - - _clearSelectedItemsAfterDataContextChanged = null; } /// @@ -695,6 +725,7 @@ namespace Perspex.Controls.Primitives if (SelectedItems.Count > 0) { + _selectedItem = null; SelectedItemsAdded(SelectedItems); added = SelectedItems; } @@ -800,5 +831,17 @@ namespace Perspex.Controls.Primitives incc.CollectionChanged -= SelectedItemsCollectionChanged; } } + + private void UpdateFinished() + { + if (_updateSelectedIndex != int.MinValue) + { + SelectedIndex = _updateSelectedIndex; + } + else if (_updateSelectedItems != null) + { + SelectedItems = _updateSelectedItems; + } + } } } diff --git a/src/Perspex.Controls/Window.cs b/src/Perspex.Controls/Window.cs index 7692f22e01..3a527ebb35 100644 --- a/src/Perspex.Controls/Window.cs +++ b/src/Perspex.Controls/Window.cs @@ -325,3 +325,16 @@ namespace Perspex.Controls } } } + +namespace Perspex +{ + public static class WindowApplicationExtensions + { + public static void RunWithMainWindow(this Application app) where TWindow : Perspex.Controls.Window, new() + { + var window = new TWindow(); + window.Show(); + app.Run(window); + } + } +} diff --git a/src/Perspex.Diagnostics/DevTools.xaml.cs b/src/Perspex.Diagnostics/DevTools.xaml.cs index a6e4e83533..5d4d962f15 100644 --- a/src/Perspex.Diagnostics/DevTools.xaml.cs +++ b/src/Perspex.Diagnostics/DevTools.xaml.cs @@ -13,9 +13,20 @@ using Perspex.Markup.Xaml; using Perspex.VisualTree; using ReactiveUI; +namespace Perspex +{ + public static class WindowExtensions + { + public static void AttachDevTools(this Window window) + { + Perspex.Diagnostics.DevTools.Attach(window); + } + } +} + namespace Perspex.Diagnostics { - public class DevTools : UserControl + public class DevTools : UserControl { private static Dictionary s_open = new Dictionary(); private IDisposable _keySubscription; diff --git a/src/Perspex.Themes.Default/Button.xaml b/src/Perspex.Themes.Default/Button.xaml index 93c372adc6..114849b131 100644 --- a/src/Perspex.Themes.Default/Button.xaml +++ b/src/Perspex.Themes.Default/Button.xaml @@ -14,7 +14,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" diff --git a/src/Perspex.Themes.Default/CheckBox.xaml b/src/Perspex.Themes.Default/CheckBox.xaml index 7452964213..d05e8b5cf6 100644 --- a/src/Perspex.Themes.Default/CheckBox.xaml +++ b/src/Perspex.Themes.Default/CheckBox.xaml @@ -22,7 +22,6 @@ diff --git a/src/Perspex.Themes.Default/ContentControl.xaml b/src/Perspex.Themes.Default/ContentControl.xaml index cce99a45e6..90e34c7d3c 100644 --- a/src/Perspex.Themes.Default/ContentControl.xaml +++ b/src/Perspex.Themes.Default/ContentControl.xaml @@ -5,8 +5,7 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" - Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" + Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/DropDown.xaml b/src/Perspex.Themes.Default/DropDown.xaml index a0340099a5..37dd47d49a 100644 --- a/src/Perspex.Themes.Default/DropDown.xaml +++ b/src/Perspex.Themes.Default/DropDown.xaml @@ -11,7 +11,6 @@ BorderThickness="{TemplateBinding BorderThickness}"> diff --git a/src/Perspex.Themes.Default/DropDownItem.xaml b/src/Perspex.Themes.Default/DropDownItem.xaml index 69742258a6..4900c961e4 100644 --- a/src/Perspex.Themes.Default/DropDownItem.xaml +++ b/src/Perspex.Themes.Default/DropDownItem.xaml @@ -10,7 +10,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/Expander.xaml b/src/Perspex.Themes.Default/Expander.xaml index 87728c555b..158f6514fd 100644 --- a/src/Perspex.Themes.Default/Expander.xaml +++ b/src/Perspex.Themes.Default/Expander.xaml @@ -17,7 +17,6 @@ Grid.Row="1" IsVisible="{TemplateBinding IsExpanded}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> @@ -35,7 +34,6 @@ Grid.Row="0" IsVisible="{TemplateBinding IsExpanded}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> @@ -53,7 +51,6 @@ Grid.Column="1" IsVisible="{TemplateBinding IsExpanded}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> @@ -71,7 +68,6 @@ Grid.Column="0" IsVisible="{TemplateBinding IsExpanded}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> diff --git a/src/Perspex.Themes.Default/LayoutTransformControl.xaml b/src/Perspex.Themes.Default/LayoutTransformControl.xaml index 3d59fd4482..e393eedf83 100644 --- a/src/Perspex.Themes.Default/LayoutTransformControl.xaml +++ b/src/Perspex.Themes.Default/LayoutTransformControl.xaml @@ -6,7 +6,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/ListBoxItem.xaml b/src/Perspex.Themes.Default/ListBoxItem.xaml index 7c22cd4b47..784731d57b 100644 --- a/src/Perspex.Themes.Default/ListBoxItem.xaml +++ b/src/Perspex.Themes.Default/ListBoxItem.xaml @@ -7,7 +7,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/MenuItem.xaml b/src/Perspex.Themes.Default/MenuItem.xaml index 5b2fc5ec9e..2011bb041b 100644 --- a/src/Perspex.Themes.Default/MenuItem.xaml +++ b/src/Perspex.Themes.Default/MenuItem.xaml @@ -13,7 +13,6 @@ @@ -83,7 +81,6 @@ diff --git a/src/Perspex.Themes.Default/RadioButton.xaml b/src/Perspex.Themes.Default/RadioButton.xaml index 6e5c958331..8fc0b14179 100644 --- a/src/Perspex.Themes.Default/RadioButton.xaml +++ b/src/Perspex.Themes.Default/RadioButton.xaml @@ -21,7 +21,6 @@ VerticalAlignment="Center"/> diff --git a/src/Perspex.Themes.Default/TabStripItem.xaml b/src/Perspex.Themes.Default/TabStripItem.xaml index 1ac77d6609..e32de4613b 100644 --- a/src/Perspex.Themes.Default/TabStripItem.xaml +++ b/src/Perspex.Themes.Default/TabStripItem.xaml @@ -9,7 +9,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/ToggleButton.xaml b/src/Perspex.Themes.Default/ToggleButton.xaml index 5f26051058..03354c9b27 100644 --- a/src/Perspex.Themes.Default/ToggleButton.xaml +++ b/src/Perspex.Themes.Default/ToggleButton.xaml @@ -14,7 +14,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" diff --git a/src/Perspex.Themes.Default/ToolTip.xaml b/src/Perspex.Themes.Default/ToolTip.xaml index 19af7c63bc..73bf2215ee 100644 --- a/src/Perspex.Themes.Default/ToolTip.xaml +++ b/src/Perspex.Themes.Default/ToolTip.xaml @@ -10,7 +10,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - DataContext="{TemplateBinding Content}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Perspex.Themes.Default/TreeViewItem.xaml b/src/Perspex.Themes.Default/TreeViewItem.xaml index 072b5a0840..5e673d767f 100644 --- a/src/Perspex.Themes.Default/TreeViewItem.xaml +++ b/src/Perspex.Themes.Default/TreeViewItem.xaml @@ -13,7 +13,6 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Header}" - DataContext="{TemplateBinding Header}" Padding="{TemplateBinding Padding}" TemplatedControl.IsTemplateFocusTarget="True" Grid.Column="1"/> diff --git a/src/Perspex.Themes.Default/Window.xaml b/src/Perspex.Themes.Default/Window.xaml index 3dca88b9f4..4316626e58 100644 --- a/src/Perspex.Themes.Default/Window.xaml +++ b/src/Perspex.Themes.Default/Window.xaml @@ -6,10 +6,7 @@ - + diff --git a/src/Skia/Perspex.Skia/SkiaPlatform.cs b/src/Skia/Perspex.Skia/SkiaPlatform.cs index 5526db720f..d9c5728a04 100644 --- a/src/Skia/Perspex.Skia/SkiaPlatform.cs +++ b/src/Skia/Perspex.Skia/SkiaPlatform.cs @@ -3,16 +3,26 @@ using System.Collections.Generic; using System.Text; using Perspex.Platform; +namespace Perspex +{ + public static class SkiaApplicationExtensions + { + public static TApp UseSkia(this TApp app) where TApp : Application + { + Perspex.Skia.SkiaPlatform.Initialize(); + return app; + } + } +} + namespace Perspex.Skia { public static class SkiaPlatform { private static bool s_forceSoftwareRendering; - public static void Initialize() - { - PerspexLocator.CurrentMutable.Bind().ToConstant(new PlatformRenderInterface()); - } + public static void Initialize() + => PerspexLocator.CurrentMutable.Bind().ToConstant(new PlatformRenderInterface()); public static bool ForceSoftwareRendering { diff --git a/src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs b/src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs index 93c820b26d..fca41e0f64 100644 --- a/src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs @@ -7,6 +7,18 @@ using Perspex.Direct2D1.Media; using Perspex.Media; using Perspex.Platform; +namespace Perspex +{ + public static class Direct2DApplicationExtensions + { + public static TApp UseDirect2D(this TApp app) where TApp : Application + { + Perspex.Direct2D1.Direct2D1Platform.Initialize(); + return app; + } + } +} + namespace Perspex.Direct2D1 { public class Direct2D1Platform : IPlatformRenderInterface diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 196588b5ad..e548989c1a 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -8,8 +8,8 @@ using System.Reactive.Disposables; using Perspex.Media; using SharpDX; using SharpDX.Direct2D1; -using IBitmap = Perspex.Media.Imaging.IBitmap; using SharpDX.Mathematics.Interop; +using IBitmap = Perspex.Media.Imaging.IBitmap; namespace Perspex.Direct2D1.Media { diff --git a/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs index 4daeb685fb..9c9bb6f29e 100644 --- a/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs @@ -22,19 +22,20 @@ namespace Perspex.Direct2D1.Media { var factory = PerspexLocator.Current.GetService(); - var format = new DWrite.TextFormat( + using (var format = new DWrite.TextFormat( factory, fontFamily, (DWrite.FontWeight)fontWeight, (DWrite.FontStyle)fontStyle, - (float)fontSize); - - TextLayout = new DWrite.TextLayout( - factory, - text ?? string.Empty, - format, - float.MaxValue, - float.MaxValue); + (float)fontSize)) + { + TextLayout = new DWrite.TextLayout( + factory, + text ?? string.Empty, + format, + float.MaxValue, + float.MaxValue); + } TextLayout.TextAlignment = textAlignment.ToDirect2D(); } diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index dc1e582bf1..96653c4c23 100644 --- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -36,27 +36,21 @@ CS1591 - - ..\..\..\packages\SharpDX.3.0.1\lib\net45\SharpDX.dll + + ..\..\..\packages\SharpDX.3.0.2\lib\net45\SharpDX.dll True - - ..\..\..\packages\SharpDX.Direct2D1.3.0.1\lib\net45\SharpDX.Direct2D1.dll + + ..\..\..\packages\SharpDX.Direct2D1.3.0.2\lib\net45\SharpDX.Direct2D1.dll True - - ..\..\..\packages\SharpDX.DXGI.3.0.1\lib\net45\SharpDX.DXGI.dll + + ..\..\..\packages\SharpDX.DXGI.3.0.2\lib\net45\SharpDX.DXGI.dll True - - - - - - ..\..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll diff --git a/src/Windows/Perspex.Direct2D1/app.config b/src/Windows/Perspex.Direct2D1/app.config index 0dfc66e74b..8a1277a9d4 100644 --- a/src/Windows/Perspex.Direct2D1/app.config +++ b/src/Windows/Perspex.Direct2D1/app.config @@ -15,16 +15,16 @@ - - + + - - + + - - + + diff --git a/src/Windows/Perspex.Direct2D1/packages.config b/src/Windows/Perspex.Direct2D1/packages.config index b5795e1753..11c75cc603 100644 --- a/src/Windows/Perspex.Direct2D1/packages.config +++ b/src/Windows/Perspex.Direct2D1/packages.config @@ -3,7 +3,7 @@ - - - + + + \ No newline at end of file diff --git a/src/Windows/Perspex.Win32/Perspex.Win32.csproj b/src/Windows/Perspex.Win32/Perspex.Win32.csproj index 458e1de004..38968ea099 100644 --- a/src/Windows/Perspex.Win32/Perspex.Win32.csproj +++ b/src/Windows/Perspex.Win32/Perspex.Win32.csproj @@ -40,14 +40,8 @@ - - - - - - ..\..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll diff --git a/src/Windows/Perspex.Win32/Win32Platform.cs b/src/Windows/Perspex.Win32/Win32Platform.cs index 5fb0a7c4b7..480c7f859e 100644 --- a/src/Windows/Perspex.Win32/Win32Platform.cs +++ b/src/Windows/Perspex.Win32/Win32Platform.cs @@ -16,6 +16,18 @@ using Perspex.Shared.PlatformSupport; using Perspex.Win32.Input; using Perspex.Win32.Interop; +namespace Perspex +{ + public static class Win32ApplicationExtensions + { + public static TApp UseWin32(this TApp app) where TApp : Application + { + Perspex.Win32.Win32Platform.Initialize(); + return app; + } + } +} + namespace Perspex.Win32 { public class Win32Platform : IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs index b5c1b8f4eb..6ddb24afd1 100644 --- a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs +++ b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs @@ -130,6 +130,28 @@ namespace Perspex.Controls.UnitTests.Presenters Assert.IsType(target.Child); } + [Fact] + public void Assigning_Control_To_Content_Should_Not_Set_DataContext() + { + var target = new ContentPresenter + { + Content = new Border(), + }; + + Assert.False(target.IsSet(Control.DataContextProperty)); + } + + [Fact] + public void Assigning_NonControl_To_Content_Should_Set_DataContext() + { + var target = new ContentPresenter + { + Content = "foo", + }; + + Assert.Equal("foo", target.DataContext); + } + private class TestContentControl : TemplatedControl { public IControl Child { get; set; } diff --git a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index ab8a7090e7..f472740b5f 100644 --- a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -1,6 +1,7 @@ // Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Perspex.Collections; @@ -8,6 +9,8 @@ using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; using Perspex.Controls.Templates; using Perspex.Interactivity; +using Perspex.Markup.Xaml.Data; +using Perspex.UnitTests; using Xunit; namespace Perspex.Controls.UnitTests.Primitives @@ -487,6 +490,86 @@ namespace Perspex.Controls.UnitTests.Primitives Assert.True(called); } + [Fact] + public void Order_Of_Setting_Items_And_SelectedIndex_During_Initialization_Should_Not_Matter() + { + var items = new[] { "Foo", "Bar" }; + var target = new SelectingItemsControl(); + + ((ISupportInitialize)target).BeginInit(); + target.SelectedIndex = 1; + target.Items = items; + ((ISupportInitialize)target).EndInit(); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + } + + [Fact] + public void Order_Of_Setting_Items_And_SelectedItem_During_Initialization_Should_Not_Matter() + { + var items = new[] { "Foo", "Bar" }; + var target = new SelectingItemsControl(); + + ((ISupportInitialize)target).BeginInit(); + target.SelectedItem = "Bar"; + target.Items = items; + ((ISupportInitialize)target).EndInit(); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + } + + [Fact] + public void Changing_DataContext_Should_Not_Clear_Nested_ViewModel_SelectedItem() + { + var items = new[] + { + new Item(), + new Item(), + }; + + var vm = new MasterViewModel + { + Child = new ChildViewModel + { + Items = items, + SelectedItem = items[1], + } + }; + + var target = new SelectingItemsControl { DataContext = vm }; + var itemsBinding = new Binding("Child.Items"); + var selectedBinding = new Binding("Child.SelectedItem"); + + target.Bind(SelectingItemsControl.ItemsProperty, itemsBinding); + target.Bind(SelectingItemsControl.SelectedItemProperty, selectedBinding); + + Assert.Equal(1, target.SelectedIndex); + Assert.Same(vm.Child.SelectedItem, target.SelectedItem); + + items = new[] + { + new Item { Value = "Item1" }, + new Item { Value = "Item2" }, + new Item { Value = "Item3" }, + }; + + vm = new MasterViewModel + { + Child = new ChildViewModel + { + Items = items, + SelectedItem = items[2], + } + }; + + target.DataContext = vm; + + Assert.Equal(2, target.SelectedIndex); + Assert.Same(vm.Child.SelectedItem, target.SelectedItem); + } + private FuncControlTemplate Template() { return new FuncControlTemplate(control => @@ -500,7 +583,29 @@ namespace Perspex.Controls.UnitTests.Primitives private class Item : Control, ISelectable { + public string Value { get; set; } public bool IsSelected { get; set; } } + + private class MasterViewModel : NotifyingBase + { + private ChildViewModel _child; + + public ChildViewModel Child + { + get { return _child; } + set + { + _child = value; + RaisePropertyChanged(); + } + } + } + + private class ChildViewModel : NotifyingBase + { + public IList Items { get; set; } + public Item SelectedItem { get; set; } + } } } diff --git a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 21cce03055..89d1c4fb72 100644 --- a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -57,7 +57,7 @@ namespace Perspex.Controls.UnitTests.Primitives }; target.ApplyTemplate(); - target.SelectedItems = new[] { "bar" }; + target.SelectedItems = new PerspexList("bar"); Assert.Equal(1, target.SelectedIndex); } @@ -536,7 +536,7 @@ namespace Perspex.Controls.UnitTests.Primitives target.ApplyTemplate(); target.Presenter.ApplyTemplate(); - target.SelectedItems = new[] { "foo", "baz" }; + target.SelectedItems = new PerspexList("foo", "baz"); Assert.True(called); } diff --git a/tests/Perspex.Direct2D1.UnitTests/Perspex.Direct2D1.UnitTests.csproj b/tests/Perspex.Direct2D1.UnitTests/Perspex.Direct2D1.UnitTests.csproj index 98197fbd8f..ca23b228fb 100644 --- a/tests/Perspex.Direct2D1.UnitTests/Perspex.Direct2D1.UnitTests.csproj +++ b/tests/Perspex.Direct2D1.UnitTests/Perspex.Direct2D1.UnitTests.csproj @@ -104,6 +104,7 @@ + diff --git a/tests/Perspex.Direct2D1.UnitTests/app.config b/tests/Perspex.Direct2D1.UnitTests/app.config new file mode 100644 index 0000000000..1723f9c94a --- /dev/null +++ b/tests/Perspex.Direct2D1.UnitTests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index 0224584cdb..7f01d3251f 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -16,8 +16,8 @@ namespace Perspex.Markup.Xaml.UnitTests.Xaml { var xaml = @" + xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' + xmlns:local='clr-namespace:Perspex.Markup.Xaml.UnitTests.Xaml;assembly=Perspex.Markup.Xaml.UnitTests'>