Merge branch 'release/1.3.6'

This commit is contained in:
Antony Male 2021-02-28 11:40:31 +00:00
commit 5e97f9e1ea
53 changed files with 1401 additions and 300 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
ko_fi: canton7
custom: ['https://www.paypal.com/donate?hosted_button_id=92FADFBYS42MU']

17
.github/ISSUE_TEMPLATE/bug-template.md vendored Normal file
View File

@ -0,0 +1,17 @@
---
name: Report a bug
about: If you've definitely found something wrong, use this. Not sure? Open a discussion.
---
**Description**
A clear and concise description of what the bug is. Use screenshots as necessary.
**To Reproduce**
Code to reproduce the bug, which someone else can run.
**Version Info**
- Stylet version: [e.g. 1.2.3]
- Runtime version: [e.g. 5.0.300]
**Additional Info**
Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Open a Discussion
url: https://github.com/canton7/stylet/discussions/new
about: If you've got a question, suggestion, or something you're not sure about, please open a discussion.

9
.github/pull-request-template.md vendored Normal file
View File

@ -0,0 +1,9 @@
**Checklist**
Thanks for contributing! Before we start, there are a few things we need to check:
1. This Pull Request has a corresponding Issue.
2. You've discussed your intention to work on this feature/bug fix.
3. This feature branch is based on develop (**not** master). The bar above should say "base: develop".
Thanks!

View File

@ -12,10 +12,10 @@ namespace Bootstrappers
{
private IContainer container;
private object _rootViewModel;
protected virtual object RootViewModel
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = this.GetInstance(typeof(TRootViewModel))); }
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
protected override void ConfigureBootstrapper()
@ -43,7 +43,9 @@ namespace Bootstrappers
builder.RegisterType<WindowManager>().As<IWindowManager>().SingleInstance();
builder.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();
builder.RegisterType<MessageBoxViewModel>().As<IMessageBoxViewModel>().ExternallyOwned(); // Not singleton!
builder.RegisterAssemblyTypes(this.GetType().Assembly).ExternallyOwned();
// See https://github.com/canton7/Stylet/discussions/211
builder.RegisterAssemblyTypes(this.GetType().Assembly).Where(x => !x.Name.Contains("ProcessedByFody")).ExternallyOwned();
}
/// <summary>

View File

@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bootstrappers</RootNamespace>
<AssemblyName>Bootstrappers</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -30,42 +31,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.2.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>packages\Autofac.4.2.1\lib\net45\Autofac.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Windsor">
<HintPath>packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.Configuration">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.RegistrationByConvention">
<HintPath>packages\Unity.3.5.1404.0\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll</HintPath>
</Reference>
<Reference Include="Ninject">
<HintPath>packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework" />
<Reference Include="StructureMap, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="StructureMap.Net4, Version=3.1.6.186, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\structuremap.3.1.6.186\lib\net40\StructureMap.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -80,20 +46,19 @@
<Compile Include="NinjectBootstrapper.cs" />
<Compile Include="NoIoCContainerBootstrapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MicrosoftDependencyInjectionBootstrapper.cs" />
<Compile Include="StructureMapBootstrapper.cs" />
<Compile Include="Tests\AutofacTests.cs" />
<Compile Include="Tests\BootstrapperTests.cs" />
<Compile Include="Tests\CastleWindsorTests.cs" />
<Compile Include="Tests\NinjectTests.cs" />
<Compile Include="Tests\NoIoCContainerTests.cs" />
<Compile Include="Tests\MicrosoftDependencyInjectionTests.cs" />
<Compile Include="Tests\StructureMapTests.cs" />
<Compile Include="Tests\StubType.cs" />
<Compile Include="Tests\UnityTests.cs" />
<Compile Include="UnityBootstrapper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>

View File

@ -14,10 +14,10 @@ namespace Bootstrappers
{
private IWindsorContainer container;
private object _rootViewModel;
protected virtual object RootViewModel
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = this.GetInstance(typeof(TRootViewModel))); }
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
protected override void ConfigureBootstrapper()
@ -39,7 +39,7 @@ namespace Bootstrappers
};
// Stylet does its own disposal of ViewModels: Castle Windsor shouldn't be doing the same
// Castle Windsor seems to be ver opinionated on this point, insisting that the container
// Castle Windsor seems to be very opinionated on this point, insisting that the container
// should be responsible for disposing all components. This is at odds with Stylet's approach
// (and indeed common sense).
#pragma warning disable CS0618 // Type or member is obsolete

View File

@ -0,0 +1,79 @@
using Autofac;
using Microsoft.Extensions.DependencyInjection;
using Stylet;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Bootstrappers
{
public class MicrosoftDependencyInjectionBootstrapper<TRootViewModel> : BootstrapperBase where TRootViewModel : class
{
private ServiceProvider serviceProvider;
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
public IServiceProvider ServiceProvider
{
get { return this.serviceProvider; }
}
protected override void ConfigureBootstrapper()
{
var services = new ServiceCollection();
this.DefaultConfigureIoC(services);
this.ConfigureIoC(services);
this.serviceProvider = services.BuildServiceProvider();
}
/// <summary>
/// Carries out default configuration of the IoC container. Override if you don't want to do this
/// </summary>
protected virtual void DefaultConfigureIoC(IServiceCollection services)
{
var viewManagerConfig = new ViewManagerConfig()
{
ViewFactory = this.GetInstance,
ViewAssemblies = new List<Assembly>() { this.GetType().Assembly }
};
services.AddSingleton<IViewManager>(new ViewManager(viewManagerConfig));
services.AddTransient<MessageBoxView>();
services.AddSingleton<IWindowManagerConfig>(this);
services.AddSingleton<IWindowManager, WindowManager>();
services.AddSingleton<IEventAggregator, EventAggregator>();
services.AddTransient<IMessageBoxViewModel, MessageBoxViewModel>(); // Not singleton!
// Also need a factory
services.AddSingleton<Func<IMessageBoxViewModel>>(() => new MessageBoxViewModel());
}
/// <summary>
/// Override to add your own types to the IoC container.
/// </summary>
protected virtual void ConfigureIoC(IServiceCollection services) { }
public override object GetInstance(Type type)
{
return this.serviceProvider.GetRequiredService(type);
}
protected override void Launch()
{
base.DisplayRootView(this.RootViewModel);
}
public override void Dispose()
{
base.Dispose();
ScreenExtensions.TryDispose(this._rootViewModel);
if (this.serviceProvider != null)
this.serviceProvider.Dispose();
}
}
}

View File

@ -11,10 +11,10 @@ namespace Bootstrappers
{
private IKernel kernel;
private object _rootViewModel;
protected virtual object RootViewModel
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = this.GetInstance(typeof(TRootViewModel))); }
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
protected override void ConfigureBootstrapper()

View File

@ -12,10 +12,10 @@ namespace Bootstrappers
{
private IContainer container;
private object _rootViewModel;
protected virtual object RootViewModel
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = this.GetInstance(typeof(TRootViewModel))); }
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
protected override void ConfigureBootstrapper()

View File

@ -159,6 +159,9 @@ namespace Bootstrappers.Tests
[Test]
public void DoesNotDisposeTransientInstances()
{
if (!this.Autobinds)
Assert.Ignore("Autobinding not supported");
StubType.Reset();
var vm = this.bootstrapper.GetInstance(typeof(StubType));

View File

@ -0,0 +1,71 @@
using Autofac;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bootstrappers.Tests
{
public class MyMicrosoftDependencyInjectionBootstrapper : MicrosoftDependencyInjectionBootstrapper<TestRootViewModel>, ITestBootstrapper
{
public List<string> ConfigureLog { get; set; }
public int DisposeCount { get; private set; }
public MyMicrosoftDependencyInjectionBootstrapper()
{
this.ConfigureLog = new List<string>();
}
protected override void Configure()
{
base.Configure();
this.ConfigureLog.Add("Configure");
}
protected override void DefaultConfigureIoC(IServiceCollection services)
{
base.DefaultConfigureIoC(services);
this.ConfigureLog.Add("DefaultConfigureIoC");
}
protected override void ConfigureIoC(IServiceCollection services)
{
base.ConfigureIoC(services);
this.ConfigureLog.Add("ConfigureIoC");
}
public new object GetInstance(Type type)
{
return base.GetInstance(type);
}
public new void ConfigureBootstrapper()
{
base.ConfigureBootstrapper();
}
public override void Dispose()
{
base.Dispose();
this.DisposeCount++;
}
}
[TestFixture(Category = "ServiceCollection")]
public class MicrosoftDependencyInjectionTests : BootstrapperTests<MyMicrosoftDependencyInjectionBootstrapper>
{
public MicrosoftDependencyInjectionTests()
{
this.Autobinds = false;
}
public override MyMicrosoftDependencyInjectionBootstrapper CreateBootstrapper()
{
return new MyMicrosoftDependencyInjectionBootstrapper();
}
}
}

View File

@ -11,10 +11,10 @@ namespace Bootstrappers
{
private IUnityContainer container;
private object _rootViewModel;
protected virtual object RootViewModel
private TRootViewModel _rootViewModel;
protected virtual TRootViewModel RootViewModel
{
get { return this._rootViewModel ?? (this._rootViewModel = this.GetInstance(typeof(TRootViewModel))); }
get { return this._rootViewModel ?? (this._rootViewModel = (TRootViewModel)this.GetInstance(typeof(TRootViewModel))); }
}
protected override void ConfigureBootstrapper()

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.2.1" targetFramework="net45" />
<package id="Castle.Core" version="3.3.3" targetFramework="net45" />
<package id="Castle.Windsor" version="3.3.0" targetFramework="net45" />
<package id="Ninject" version="3.2.2.0" targetFramework="net45" />
<package id="NUnit" version="3.5.0" targetFramework="net45" />
<package id="NUnit3TestAdapter" version="3.6.0" targetFramework="net45" />
<package id="structuremap" version="3.1.6.186" targetFramework="net45" />
<package id="Unity" version="3.5.1404.0" targetFramework="net45" />
</packages>

View File

@ -1,6 +1,20 @@
Stylet Changelog
================
v1.3.6
------
- If an Action returns a Task, await it in an `async void` method (#53)
- Allow Actions to have explicit targets (`{s:Action ..., Target=...}`) (#177)
- Allow Actions to invoke static methods (#177)
- Actions: if the target does not implement `INotifyPropertyChanged`, still try and evaluate guard properties (#214)
- Add an extra layer to the bootstrapper class hierarchy between `BootstrapperBase` and `Bootstrapper<T>`,
for people who don't have a root ViewModel
- Allow starting the Bootstrapper without an Application (#206)
- Expose SynchronousDispatcher and ApplicationDispatcher to assign to `Execute.Dispatcher` (#217)
- Improve the sample bootstrappers (fix Autofac, add Microsoft.Services.DependencyInjection)
- Improve samples
v1.3.5
------

View File

@ -19,21 +19,20 @@ directory COVERAGE_DIR
desc "Build the project using the current CONFIG (or Debug)"
task :build do
# https://github.com/novotnyllc/MSBuildSdkExtras/pull/249
sh 'dotnet', 'build', '-c', CONFIG, CSPROJ, '/nowarn:MSB4011'
sh 'dotnet', 'build', '-c', CONFIG, CSPROJ
end
desc "Run unit tests using the current CONFIG (or Debug)"
task :test do
sh 'dotnet', 'test', '-c', CONFIG, UNIT_TESTS, '/nowarn:MSB4011'
sh 'dotnet', 'test', '-c', CONFIG, UNIT_TESTS
end
desc "Create NuGet package"
task :package do
# Not sure why these have to be this way around, but they do
sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, "-p:NuSpecFile=../#{NUSPEC_START}", '/nowarn:MSB4011'
sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, '-p:IncludeSymbols=true', '/nowarn:MSB4011'
sh 'dotnet', 'pack', '-c', CONFIG, TEMPLATES_CSPROJ, '/nowarn:MSB4011'
sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, "-p:NuSpecFile=../#{NUSPEC_START}"
sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, '-p:IncludeSymbols=true'
sh 'dotnet', 'pack', '-c', CONFIG, TEMPLATES_CSPROJ
end
desc "Bump version number"

View File

@ -10,7 +10,7 @@
</DockPanel>
<Grid DockPanel.Dock="Bottom">
<Button HorizontalAlignment="Left" Width="100" IsDefault="True" Command="{s:Action Close}">Save</Button>
<Button HorizontalAlignment="Left" Width="100" IsDefault="True" Command="{s:Action Save}">Save</Button>
<Button HorizontalAlignment="Right" Width="100" IsCancel="True">Cancel</Button>
</Grid>
</DockPanel>

View File

@ -15,5 +15,10 @@ namespace Stylet.Samples.HelloDialog
{
this.RequestClose(null);
}
public void Save()
{
this.RequestClose(true);
}
}
}

View File

@ -24,8 +24,9 @@ namespace Stylet.Samples.HelloDialog
this.NameString = "Click the button to show the dialog";
}
public void ShowDialog()
public async System.Threading.Tasks.Task ShowDialog()
{
throw new Exception("KABLAMMO");
var dialogVm = this.dialogFactory.CreateDialog1();
var result = this.windowManager.ShowDialog(dialogVm);
if (result.GetValueOrDefault())

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

View File

@ -0,0 +1,13 @@
<Application x:Class="Stylet.Samples.NavigationController.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Stylet.Samples.NavigationController"
xmlns:s="https://github.com/canton7/Stylet">
<Application.Resources>
<s:ApplicationLoader>
<s:ApplicationLoader.Bootstrapper>
<local:Bootstrapper />
</s:ApplicationLoader.Bootstrapper>
</s:ApplicationLoader>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Stylet.Samples.NavigationController
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,27 @@
using System;
using Stylet;
using StyletIoC;
using Stylet.Samples.NavigationController.Pages;
namespace Stylet.Samples.NavigationController
{
public class Bootstrapper : Bootstrapper<ShellViewModel>
{
protected override void ConfigureIoC(IStyletIoCBuilder builder)
{
builder.Bind<NavigationController>().And<INavigationController>().To<NavigationController>().InSingletonScope();
// https://github.com/canton7/Stylet/issues/24
builder.Bind<Func<Page1ViewModel>>().ToFactory<Func<Page1ViewModel>>(c => () => c.Get<Page1ViewModel>());
builder.Bind<Func<Page2ViewModel>>().ToFactory<Func<Page2ViewModel>>(c => () => c.Get<Page2ViewModel>());
}
protected override void OnLaunch()
{
// There's a circular dependency, where ShellViewModel -> HeaderViewModel -> NavigationController -> ShellViewModel
// We break this by assigning the ShellViewModel to the NavigationController after constructing it
var navigationController = this.Container.Get<NavigationController>();
navigationController.Delegate = this.RootViewModel;
navigationController.NavigateToPage1();
}
}
}

View File

@ -0,0 +1,46 @@
using Stylet.Samples.NavigationController.Pages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet.Samples.NavigationController
{
public interface INavigationController
{
void NavigateToPage1();
void NavigateToPage2(string initiator);
}
public interface INavigationControllerDelegate
{
void NavigateTo(IScreen screen);
}
public class NavigationController : INavigationController
{
private readonly Func<Page1ViewModel> page1ViewModelFactory;
private readonly Func<Page2ViewModel> page2ViewModelFactory;
public INavigationControllerDelegate Delegate { get; set; }
public NavigationController(Func<Page1ViewModel> page1ViewModelFactory, Func<Page2ViewModel> page2ViewModelFactory)
{
this.page1ViewModelFactory = page1ViewModelFactory ?? throw new ArgumentNullException(nameof(page1ViewModelFactory));
this.page2ViewModelFactory = page2ViewModelFactory ?? throw new ArgumentNullException(nameof(page2ViewModelFactory));
}
public void NavigateToPage1()
{
this.Delegate?.NavigateTo(this.page1ViewModelFactory());
}
public void NavigateToPage2(string initiator)
{
var vm = this.page2ViewModelFactory();
vm.Initiator = initiator;
this.Delegate?.NavigateTo(vm);
}
}
}

View File

@ -0,0 +1,15 @@
<UserControl x:Class="Stylet.Samples.NavigationController.Pages.HeaderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Stylet.Samples.NavigationController.Pages"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock>Go to: </TextBlock>
<Button Margin="5,0" Command="{s:Action NavigateToPage1}">Page 1</Button>
<Button Margin="5,0" Command="{s:Action NavigateToPage2}">Page 2</Button>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet.Samples.NavigationController.Pages
{
public class HeaderViewModel : Screen
{
private readonly INavigationController navigationController;
public HeaderViewModel(INavigationController navigationController)
{
this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController));
}
public void NavigateToPage1() => this.navigationController.NavigateToPage1();
public void NavigateToPage2() => this.navigationController.NavigateToPage2("the Header");
}
}

View File

@ -0,0 +1,16 @@
<UserControl x:Class="Stylet.Samples.NavigationController.Pages.Page1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:local="clr-namespace:Stylet.Samples.NavigationController.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<TextBlock>This is page 1</TextBlock>
<Button Command="{s:Action NavigateToPage2}">Go to page 2</Button>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet.Samples.NavigationController.Pages
{
public class Page1ViewModel : Screen
{
private readonly INavigationController navigationController;
public Page1ViewModel(INavigationController navigationController)
{
this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController));
}
public void NavigateToPage2() => this.navigationController.NavigateToPage2("Page 1");
}
}

View File

@ -0,0 +1,17 @@
<UserControl x:Class="Stylet.Samples.NavigationController.Pages.Page2View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:local="clr-namespace:Stylet.Samples.NavigationController.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<TextBlock>This is page 2</TextBlock>
<TextBlock Text="{Binding Initiator, StringFormat='You got here from {0}'}"/>
<Button Command="{s:Action NavigateToPage1}">Go to page 1</Button>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet.Samples.NavigationController.Pages
{
public class Page2ViewModel : Screen
{
private readonly INavigationController navigationController;
private string _initiator;
public string Initiator
{
get => this._initiator;
set => this.SetAndNotify(ref this._initiator, value);
}
public Page2ViewModel(INavigationController navigationController)
{
this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController));
}
public void NavigateToPage1() => this.navigationController.NavigateToPage1();
}
}

View File

@ -0,0 +1,15 @@
<Window x:Class="Stylet.Samples.NavigationController.Pages.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Stylet.Samples.NavigationController.Pages"
mc:Ignorable="d"
Title="Navigation Controller sample" Height="450" Width="800"
xmlns:s="https://github.com/canton7/Stylet"
d:DataContext="{d:DesignInstance local:ShellViewModel}">
<DockPanel>
<ContentControl DockPanel.Dock="Top" s:View.Model="{Binding HeaderViewModel}"/>
<ContentControl s:View.Model="{Binding ActiveItem}"/>
</DockPanel>
</Window>

View File

@ -0,0 +1,20 @@
using System;
using Stylet;
namespace Stylet.Samples.NavigationController.Pages
{
public class ShellViewModel : Conductor<IScreen>, INavigationControllerDelegate
{
public HeaderViewModel HeaderViewModel { get; }
public ShellViewModel(HeaderViewModel headerViewModel)
{
this.HeaderViewModel = headerViewModel ?? throw new ArgumentNullException(nameof(headerViewModel));
}
public void NavigateTo(IScreen screen)
{
this.ActivateItem(screen);
}
}
}

View File

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Stylet.Samples.NavigationController")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Stylet.Samples.NavigationController")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,70 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Stylet.Samples.NavigationController.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Stylet.Samples.NavigationController.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,29 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Stylet.Samples.NavigationController.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Stylet.Samples.NavigationController</RootNamespace>
<AssemblyName>Stylet.Samples.NavigationController</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
</ApplicationDefinition>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Bootstrapper.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="NavigationController.cs" />
<Compile Include="Pages\HeaderViewModel.cs" />
<Compile Include="Pages\Page1ViewModel.cs" />
<Compile Include="Pages\Page2ViewModel.cs" />
<Compile Include="Pages\ShellViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Page Include="Pages\HeaderView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\Page1View.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\Page2View.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\ShellView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stylet\Stylet.csproj">
<Project>{2435bd00-ac12-48b0-ad36-9bab2fdec3f5}</Project>
<Name>Stylet</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,70 +1,79 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet", "..\Stylet\Stylet.csproj", "{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.Hello", "Stylet.Samples.Hello\Stylet.Samples.Hello.csproj", "{6C7FBB21-52AC-4333-A42A-9F5E9D048621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.TabNavigation", "Stylet.Samples.TabNavigation\Stylet.Samples.TabNavigation.csproj", "{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.MasterDetail", "Stylet.Samples.MasterDetail\Stylet.Samples.MasterDetail.csproj", "{A281DFF2-125E-4412-8927-0F09EEFC5AD1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.HelloDialog", "Stylet.Samples.HelloDialog\Stylet.Samples.HelloDialog.csproj", "{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.OverridingViewManager", "Stylet.Samples.OverridingViewManager\Stylet.Samples.OverridingViewManager.csproj", "{2F7D7EF3-730A-45E3-93CA-7C5031250246}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.RedditBrowser", "Stylet.Samples.RedditBrowser\Stylet.Samples.RedditBrowser.csproj", "{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.ModelValidation", "Stylet.Samples.ModelValidation\Stylet.Samples.ModelValidation.csproj", "{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.DesignMode", "Stylet.Samples.DesignMode\Stylet.Samples.DesignMode.csproj", "{D5225DA1-58ED-42AA-9589-A4F86E7667F7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Release|Any CPU.Build.0 = Release|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Release|Any CPU.Build.0 = Release|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Release|Any CPU.Build.0 = Release|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Release|Any CPU.Build.0 = Release|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Release|Any CPU.Build.0 = Release|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Release|Any CPU.Build.0 = Release|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Release|Any CPU.Build.0 = Release|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Release|Any CPU.Build.0 = Release|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30709.64
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stylet", "..\Stylet\Stylet.csproj", "{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.Hello", "Stylet.Samples.Hello\Stylet.Samples.Hello.csproj", "{6C7FBB21-52AC-4333-A42A-9F5E9D048621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.TabNavigation", "Stylet.Samples.TabNavigation\Stylet.Samples.TabNavigation.csproj", "{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.MasterDetail", "Stylet.Samples.MasterDetail\Stylet.Samples.MasterDetail.csproj", "{A281DFF2-125E-4412-8927-0F09EEFC5AD1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.HelloDialog", "Stylet.Samples.HelloDialog\Stylet.Samples.HelloDialog.csproj", "{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.OverridingViewManager", "Stylet.Samples.OverridingViewManager\Stylet.Samples.OverridingViewManager.csproj", "{2F7D7EF3-730A-45E3-93CA-7C5031250246}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.RedditBrowser", "Stylet.Samples.RedditBrowser\Stylet.Samples.RedditBrowser.csproj", "{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.ModelValidation", "Stylet.Samples.ModelValidation\Stylet.Samples.ModelValidation.csproj", "{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.DesignMode", "Stylet.Samples.DesignMode\Stylet.Samples.DesignMode.csproj", "{D5225DA1-58ED-42AA-9589-A4F86E7667F7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet.Samples.NavigationController", "Stylet.Samples.NavigationController\Stylet.Samples.NavigationController.csproj", "{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}.Release|Any CPU.Build.0 = Release|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C7FBB21-52AC-4333-A42A-9F5E9D048621}.Release|Any CPU.Build.0 = Release|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A4E2DAD-AE68-4A82-8FA8-407DB74D6FBE}.Release|Any CPU.Build.0 = Release|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A281DFF2-125E-4412-8927-0F09EEFC5AD1}.Release|Any CPU.Build.0 = Release|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6DD6F38-40A3-4EC1-B342-0C2BCCF0DD44}.Release|Any CPU.Build.0 = Release|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F7D7EF3-730A-45E3-93CA-7C5031250246}.Release|Any CPU.Build.0 = Release|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72B1C6E4-1293-47DD-BEFD-FA2E782BDBDA}.Release|Any CPU.Build.0 = Release|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA5A6CA5-7E8E-4401-A3D2-0035DDE10413}.Release|Any CPU.Build.0 = Release|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5225DA1-58ED-42AA-9589-A4F86E7667F7}.Release|Any CPU.Build.0 = Release|Any CPU
{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DEE75AC-6BC1-4375-B6A0-44EAF39FF559}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A72165C7-7E4E-4336-88FF-BDCC3E300D54}
EndGlobalSection
EndGlobal

View File

@ -8,14 +8,12 @@ namespace Stylet
/// <summary>
/// Bootstrapper to be extended by any application which wants to use StyletIoC (the default)
/// </summary>
/// <remarks>
/// If you don't have a root ViewModel, you might prefer to override <see cref="StyletIoCBootstrapperBase"/>
/// </remarks>
/// <typeparam name="TRootViewModel">Type of the root ViewModel. This will be instantiated and displayed</typeparam>
public abstract class Bootstrapper<TRootViewModel> : BootstrapperBase where TRootViewModel : class
public abstract class Bootstrapper<TRootViewModel> : StyletIoCBootstrapperBase where TRootViewModel : class
{
/// <summary>
/// Gets or sets the Bootstrapper's IoC container. This is created after ConfigureIoC has been run.
/// </summary>
protected IContainer Container { get; set; }
private TRootViewModel _rootViewModel;
/// <summary>
@ -25,65 +23,7 @@ namespace Stylet
{
get { return this._rootViewModel ?? (this._rootViewModel = this.Container.Get<TRootViewModel>()); }
}
/// <summary>
/// Overridden from BootstrapperBase, this sets up the IoC container
/// </summary>
protected override sealed void ConfigureBootstrapper()
{
var builder = new StyletIoCBuilder();
builder.Assemblies = new List<Assembly>(new List<Assembly>() { this.GetType().Assembly });
// Call DefaultConfigureIoC *after* ConfigureIoIC, so that they can customize builder.Assemblies
this.ConfigureIoC(builder);
this.DefaultConfigureIoC(builder);
this.Container = builder.BuildContainer();
}
/// <summary>
/// Carries out default configuration of StyletIoC. Override if you don't want to do this
/// </summary>
/// <param name="builder">StyletIoC builder to use to configure the container</param>
protected virtual void DefaultConfigureIoC(StyletIoCBuilder builder)
{
// Mark these as weak-bindings, so the user can replace them if they want
var viewManagerConfig = new ViewManagerConfig()
{
ViewFactory = this.GetInstance,
ViewAssemblies = new List<Assembly>() { this.GetType().Assembly }
};
builder.Bind<ViewManagerConfig>().ToInstance(viewManagerConfig).AsWeakBinding();
// Bind it to both IViewManager and to itself, so that people can get it with Container.Get<ViewManager>()
builder.Bind<IViewManager>().And<ViewManager>().To<ViewManager>().InSingletonScope().AsWeakBinding();
builder.Bind<IWindowManagerConfig>().ToInstance(this).DisposeWithContainer(false).AsWeakBinding();
builder.Bind<IWindowManager>().To<WindowManager>().InSingletonScope().AsWeakBinding();
builder.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope().AsWeakBinding();
builder.Bind<IMessageBoxViewModel>().To<MessageBoxViewModel>().AsWeakBinding();
// Stylet's assembly isn't added to the container, so add this explicitly
builder.Bind<MessageBoxView>().ToSelf();
builder.Autobind();
}
/// <summary>
/// Override to add your own types to the IoC container.
/// </summary>
/// <param name="builder">StyletIoC builder to use to configure the container</param>
protected virtual void ConfigureIoC(IStyletIoCBuilder builder) { }
/// <summary>
/// Given a type, use the IoC container to fetch an instance of it
/// </summary>
/// <param name="type">Type to fetch</param>
/// <returns>Fetched instance</returns>
public override object GetInstance(Type type)
{
return this.Container.Get(type);
}
/// <summary>
/// Called when the application is launched. Displays the root view.
/// </summary>
@ -97,12 +37,11 @@ namespace Stylet
/// </summary>
public override void Dispose()
{
// Dispose the container last
base.Dispose();
// Don't create the root ViewModel if it doesn't already exist...
ScreenExtensions.TryDispose(this._rootViewModel);
if (this.Container != null)
this.Container.Dispose();
// Dispose the container last
base.Dispose();
}
}
}

View File

@ -34,6 +34,13 @@ namespace Stylet
/// <summary>
/// Called by the ApplicationLoader when this bootstrapper is loaded
/// </summary>
/// <remarks>
/// If you're constructing the bootstrapper yourself, call this manully and pass in the Application
/// (probably <see cref="Application.Current"/>). Stylet will start when <see cref="Application.Startup"/>
/// is fired. If no Application is available, do not call this but instead call <see cref="Start(string[])"/>.
/// (In this case, note that the <see cref="Execute"/> methods will all dispatch synchronously, unless you
/// set <see cref="Execute.Dispatcher"/> yourself).
/// </remarks>
/// <param name="application">Application within which Stylet is running</param>
public void Setup(Application application)
{
@ -43,7 +50,7 @@ namespace Stylet
this.Application = application;
// Use the current application's dispatcher for Execute
Execute.Dispatcher = new DispatcherWrapper(this.Application.Dispatcher);
Execute.Dispatcher = new ApplicationDispatcher(this.Application.Dispatcher);
this.Application.Startup += (o, e) => this.Start(e.Args);
// Make life nice for the app - they can handle these by overriding Bootstrapper methods, rather than adding event handlers
@ -64,6 +71,10 @@ namespace Stylet
/// <summary>
/// Called on Application.Startup, this does everything necessary to start the application
/// </summary>
/// <remarks>
/// If you're constructing the bootstrapper yourself, and aren't able to call <see cref="Setup(Application)"/>,
/// (e.g. because an Application isn't available), you must call this yourself.
/// </remarks>
/// <param name="args">Command-line arguments used to start this executable</param>
public virtual void Start(string[] args)
{
@ -73,9 +84,8 @@ namespace Stylet
this.ConfigureBootstrapper();
// Cater for the unit tests, which can't sensibly stub Application
if (this.Application != null)
this.Application.Resources.Add(View.ViewManagerResourceKey, this.GetInstance(typeof(IViewManager)));
// We allow starting without an application
this.Application?.Resources.Add(View.ViewManagerResourceKey, this.GetInstance(typeof(IViewManager)));
this.Configure();
this.Launch();
@ -107,7 +117,7 @@ namespace Stylet
/// <returns>The currently-displayed window, or null</returns>
public virtual Window GetActiveWindow()
{
return this.Application.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive) ?? this.Application.MainWindow;
return this.Application?.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive) ?? this.Application?.MainWindow;
}
/// <summary>

View File

@ -16,11 +16,13 @@ namespace Stylet
/// Gets or sets Execute's dispatcher
/// </summary>
/// <remarks>
/// Should be set to the UI thread's Dispatcher. This is normally done by the Bootstrapper.
/// Should be set a <see cref="ApplicationDispatcher"/> wrapping the current application's dispatcher, which is
/// normally done by the Bootstrapper. Can also be set to <see cref="SynchronousDispatcher.Instance"/>, or a
/// custom <see cref="IDispatcher"/> implementation.
/// </remarks>
public static IDispatcher Dispatcher
{
get { return _dispatcher ?? (_dispatcher = new SynchronousDispatcher()); }
get { return _dispatcher ?? SynchronousDispatcher.Instance; }
set
{

View File

@ -1,10 +1,12 @@
using System;
using System.Windows;
using System.Windows.Threading;
namespace Stylet
{
/// <summary>
/// Generalised dispatcher, which can post and send
/// Generalised dispatcher, which can post and send.
/// Used by <see cref="Execute"/>.
/// </summary>
public interface IDispatcher
{
@ -26,43 +28,75 @@ namespace Stylet
bool IsCurrent { get; }
}
internal class DispatcherWrapper : IDispatcher
/// <summary>
/// <see cref="IDispatcher"/> implementation which can dispatch using <see cref="Dispatcher"/>
/// </summary>
public class ApplicationDispatcher : IDispatcher
{
private readonly Dispatcher dispatcher;
public DispatcherWrapper(Dispatcher dispatcher)
/// <summary>
/// Initialises a new instance of the <see cref="ApplicationDispatcher"/> class with the given <see cref="Dispatcher"/>
/// </summary>
/// <param name="dispatcher"><see cref="Dispatcher"/> to use, normally Application.Current.Dispatcher</param>
public ApplicationDispatcher(Dispatcher dispatcher)
{
this.dispatcher = dispatcher;
this.dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
/// <summary>
/// Initialises a new instance of the <see cref="ApplicationDispatcher"/> class with the given <see cref="Application"/>
/// </summary>
/// <param name="application"><see cref="Application"/> to use, normally Application</param>
public ApplicationDispatcher(Application application)
: this(application?.Dispatcher ?? throw new ArgumentNullException(nameof(application)))
{
}
/// <inheritdoc/>
public void Post(Action action)
{
this.dispatcher.BeginInvoke(action);
}
/// <inheritdoc/>
public void Send(Action action)
{
this.dispatcher.Invoke(action);
}
/// <inheritdoc/>
public bool IsCurrent
{
get { return this.dispatcher.CheckAccess(); }
}
}
internal class SynchronousDispatcher : IDispatcher
/// <summary>
/// <see cref="IDispatcher"/> implementation whcih dispatches synchronously.
/// Usually used for unit testing.
/// </summary>
public class SynchronousDispatcher : IDispatcher
{
/// <summary>
/// Gets the singleton instance of <see cref="SynchronousDispatcher"/>
/// </summary>
public static SynchronousDispatcher Instance { get; } = new SynchronousDispatcher();
private SynchronousDispatcher() { }
/// <inheritdoc/>
public void Post(Action action)
{
action();
}
/// <inheritdoc/>
public void Send(Action action)
{
action();
}
/// <inheritdoc/>
public bool IsCurrent
{
get { return true; }

View File

@ -1,4 +1,4 @@
<Project Sdk="MSBuild.Sdk.Extras/2.1.2">
<Project Sdk="MSBuild.Sdk.Extras/3.0.23">
<PropertyGroup>
<OutputType>Library</OutputType>
@ -23,16 +23,12 @@
<!-- Just embed all sources in the PDB: snupkg files don't support bare .cs files, and SourceLink is annoying -->
<!-- We set IncludeSymbols in the Rakefile, because we don't want it to apply to Stylet.Start -->
<!-- Note that stack traces don't work on < net471, but snupkg only supports portable, so nothing we can do -->
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedAllSources>true</EmbedAllSources>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' ">
<!-- Stack traces on < net471 don't work with portable -->
<DebugType>full</DebugType>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45'">
<PackageReference Include="System.Drawing.Common" Version="4.6.0" />
</ItemGroup>

View File

@ -0,0 +1,93 @@
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Stylet
{
/// <summary>
/// Bootstrapper to be extended by any application which wants to use StyletIoC, but doesn't have a root ViewModel
/// </summary>
/// <remarks>
/// You would normally use <see cref="Bootstrapper{TRootViewModel}"/>, which lets you specify the root ViewModel
/// to display. If you don't want to show a window on startup, override <see cref="BootstrapperBase.Launch"/>
/// but don't call <see cref="BootstrapperBase.DisplayRootView(object)"/>.
/// </remarks>
public abstract class StyletIoCBootstrapperBase : BootstrapperBase
{
/// <summary>
/// Gets or sets the Bootstrapper's IoC container. This is created after ConfigureIoC has been run.
/// </summary>
protected IContainer Container { get; set; }
/// <summary>
/// Overridden from BootstrapperBase, this sets up the IoC container
/// </summary>
protected override sealed void ConfigureBootstrapper()
{
var builder = new StyletIoCBuilder();
builder.Assemblies = new List<Assembly>(new List<Assembly>() { this.GetType().Assembly });
// Call DefaultConfigureIoC *after* ConfigureIoIC, so that they can customize builder.Assemblies
this.ConfigureIoC(builder);
this.DefaultConfigureIoC(builder);
this.Container = builder.BuildContainer();
}
/// <summary>
/// Carries out default configuration of StyletIoC. Override if you don't want to do this
/// </summary>
/// <param name="builder">StyletIoC builder to use to configure the container</param>
protected virtual void DefaultConfigureIoC(StyletIoCBuilder builder)
{
// Mark these as weak-bindings, so the user can replace them if they want
var viewManagerConfig = new ViewManagerConfig()
{
ViewFactory = this.GetInstance,
ViewAssemblies = new List<Assembly>() { this.GetType().Assembly }
};
builder.Bind<ViewManagerConfig>().ToInstance(viewManagerConfig).AsWeakBinding();
// Bind it to both IViewManager and to itself, so that people can get it with Container.Get<ViewManager>()
builder.Bind<IViewManager>().And<ViewManager>().To<ViewManager>().InSingletonScope().AsWeakBinding();
builder.Bind<IWindowManagerConfig>().ToInstance(this).DisposeWithContainer(false).AsWeakBinding();
builder.Bind<IWindowManager>().To<WindowManager>().InSingletonScope().AsWeakBinding();
builder.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope().AsWeakBinding();
builder.Bind<IMessageBoxViewModel>().To<MessageBoxViewModel>().AsWeakBinding();
// Stylet's assembly isn't added to the container, so add this explicitly
builder.Bind<MessageBoxView>().ToSelf();
builder.Autobind();
}
/// <summary>
/// Override to add your own types to the IoC container.
/// </summary>
/// <param name="builder">StyletIoC builder to use to configure the container</param>
protected virtual void ConfigureIoC(IStyletIoCBuilder builder) { }
/// <summary>
/// Given a type, use the IoC container to fetch an instance of it
/// </summary>
/// <param name="type">Type to fetch</param>
/// <returns>Fetched instance</returns>
public override object GetInstance(Type type)
{
return this.Container.Get(type);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public override void Dispose()
{
base.Dispose();
// Dispose the container last
if (this.Container != null)
this.Container.Dispose();
}
}
}

View File

@ -3,7 +3,9 @@ using System;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
@ -42,11 +44,12 @@ namespace Stylet.Xaml
protected readonly ActionUnavailableBehaviour ActionNonExistentBehaviour;
/// <summary>
/// Gets the object on which methods will be invokced
/// Gets the object on which methods will be invoked
/// </summary>
public object Target
{
get { return this.GetValue(targetProperty); }
private set { this.SetValue(targetProperty, value); }
}
private static readonly DependencyProperty targetProperty =
@ -56,7 +59,7 @@ namespace Stylet.Xaml
}));
/// <summary>
/// Initialises a new instance of the <see cref="ActionBase"/> class
/// Initialises a new instance of the <see cref="ActionBase"/> class to use <see cref="View.ActionTargetProperty"/> to get the target
/// </summary>
/// <param name="subject">View to grab the View.ActionTarget from</param>
/// <param name="backupSubject">Backup subject to use if no ActionTarget could be retrieved from the subject</param>
@ -65,12 +68,9 @@ namespace Stylet.Xaml
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
/// <param name="logger">Logger to use</param>
public ActionBase(DependencyObject subject, DependencyObject backupSubject, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour, ILogger logger)
: this(methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{
this.Subject = subject;
this.MethodName = methodName;
this.TargetNullBehaviour = targetNullBehaviour;
this.ActionNonExistentBehaviour = actionNonExistentBehaviour;
this.logger = logger;
// If a 'backupSubject' was given, bind both that and 'subject' to this.Target (with a converter which picks the first
// one that isn't View.InitialActionTarget). If it wasn't given, just bind 'subject'.
@ -101,6 +101,31 @@ namespace Stylet.Xaml
}
}
/// <summary>
/// Initialises a new instance of the <see cref="ActionBase"/> class to use an explicit target
/// </summary>
/// <param name="target">Target to find the method on</param>
/// <param name="methodName">Method name. the MyMethod in Buttom Command="{s:Action MyMethod}".</param>
/// <param name="targetNullBehaviour">Behaviour for it the relevant View.ActionTarget is null</param>
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
/// <param name="logger">Logger to use</param>
public ActionBase(object target, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour, ILogger logger)
: this(methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
this.Target = target;
}
private ActionBase(string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour, ILogger logger)
{
this.MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
this.TargetNullBehaviour = targetNullBehaviour;
this.ActionNonExistentBehaviour = actionNonExistentBehaviour;
this.logger = logger;
}
private void UpdateActionTarget(object oldTarget, object newTarget)
{
MethodInfo targetMethodInfo = null;
@ -128,13 +153,22 @@ namespace Stylet.Xaml
}
else
{
var newTargetType = newTarget.GetType();
BindingFlags bindingFlags;
if (newTarget is Type newTargetType)
{
bindingFlags = BindingFlags.Public | BindingFlags.Static;
}
else
{
newTargetType = newTarget.GetType();
bindingFlags = BindingFlags.Public | BindingFlags.Instance;
}
try
{
targetMethodInfo = newTargetType.GetMethod(this.MethodName);
targetMethodInfo = newTargetType.GetMethod(this.MethodName, bindingFlags);
if (targetMethodInfo == null)
this.logger.Warn("Unable to find method {0} on {1}", this.MethodName, newTargetType.Name);
this.logger.Warn("Unable to find{0} method {1} on {2}", newTarget is Type ? " static" : "", this.MethodName, newTargetType.Name);
else
this.AssertTargetMethodInfo(targetMethodInfo, newTargetType);
}
@ -156,19 +190,19 @@ namespace Stylet.Xaml
/// </summary>
/// <param name="targetMethodInfo">MethodInfo of method on new target</param>
/// <param name="newTargetType">Type of new target</param>
protected internal abstract void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType);
private protected abstract void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType);
/// <summary>
/// Invoked when a new target is set, after all other action has been taken
/// </summary>
/// <param name="oldTarget">Previous target</param>
/// <param name="newTarget">New target</param>
protected internal virtual void OnTargetChanged(object oldTarget, object newTarget) { }
private protected virtual void OnTargetChanged(object oldTarget, object newTarget) { }
/// <summary>
/// Assert that the target is not View.InitialActionTarget
/// </summary>
protected internal void AssertTargetSet()
private protected void AssertTargetSet()
{
// If we've made it this far and the target is still the default, then something's wrong
// Make sure they know
@ -183,7 +217,7 @@ namespace Stylet.Xaml
if (this.TargetMethodInfo == null && this.ActionNonExistentBehaviour == ActionUnavailableBehaviour.Throw)
{
var ex = new ActionNotFoundException(String.Format("Unable to find method {0} on target {1}", this.MethodName, this.Target.GetType().Name));
var ex = new ActionNotFoundException(String.Format("Unable to find method {0} on {1}", this.MethodName, this.TargetName()));
this.logger.Error(ex);
throw ex;
}
@ -193,22 +227,37 @@ namespace Stylet.Xaml
/// Invoke the target method with the given parameters
/// </summary>
/// <param name="parameters">Parameters to pass to the target method</param>
protected internal void InvokeTargetMethod(object[] parameters)
private protected void InvokeTargetMethod(object[] parameters)
{
this.logger.Info("Invoking method {0} on target {1} with parameters ({2})", this.MethodName, this.Target, parameters == null ? "none" : String.Join(", ", parameters));
this.logger.Info("Invoking method {0} on {1} with parameters ({2})", this.MethodName, this.TargetName(), parameters == null ? "none" : String.Join(", ", parameters));
try
{
this.TargetMethodInfo.Invoke(this.Target, parameters);
var target = this.TargetMethodInfo.IsStatic ? null : this.Target;
var result = this.TargetMethodInfo.Invoke(target, parameters);
// Be nice and make sure that any exceptions get rethrown
if (result is Task task)
{
AwaitTask(task);
}
}
catch (TargetInvocationException e)
{
// Be nice and unwrap this for them
// They want a stack track for their VM method, not us
this.logger.Error(e.InnerException, String.Format("Failed to invoke method {0} on target {1} with parameters ({2})", this.MethodName, this.Target, parameters == null ? "none" : String.Join(", ", parameters)));
this.logger.Error(e.InnerException, String.Format("Failed to invoke method {0} on {1} with parameters ({2})", this.MethodName, this.TargetName(), parameters == null ? "none" : String.Join(", ", parameters)));
// http://stackoverflow.com/a/17091351/1086121
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
async void AwaitTask(Task t) => await t;
}
private string TargetName()
{
return this.Target is Type t
? $"static target {t.Name}"
: $"target {this.Target.GetType().Name}";
}
private class MultiBindingToActionTargetConverter : IMultiValueConverter

View File

@ -44,6 +44,11 @@ namespace Stylet.Xaml
[ConstructorArgument("method")]
public string Method { get; set; }
/// <summary>
/// Gets or sets a target to override that set with View.ActionTarget
/// </summary>
public object Target { get; set; }
/// <summary>
/// Gets or sets the behaviour if the View.ActionTarget is nulil
/// </summary>
@ -101,15 +106,13 @@ namespace Stylet.Xaml
throw new InvalidOperationException("Method has not been set");
var valueService = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
var rootObject = rootObjectProvider?.RootObject as DependencyObject;
switch (valueService.TargetObject)
{
case DependencyObject targetObject:
return this.HandleDependencyObject(valueService, targetObject, rootObject);
return this.HandleDependencyObject(serviceProvider, valueService, targetObject);
case CommandBinding commandBinding:
return this.HandleCommandBinding(rootObject, ((EventInfo)valueService.TargetProperty).EventHandlerType);
return this.CreateEventAction(serviceProvider, null, ((EventInfo)valueService.TargetProperty).EventHandlerType, isCommandBinding: true);
default:
// Seems this is the case when we're in a template. We'll get called again properly in a second.
// http://social.msdn.microsoft.com/Forums/vstudio/en-US/a9ead3d5-a4e4-4f9c-b507-b7a7d530c6a9/gaining-access-to-target-object-instead-of-shareddp-in-custom-markupextensions-providevalue-method?forum=wpf
@ -117,39 +120,66 @@ namespace Stylet.Xaml
}
}
private object HandleDependencyObject(IProvideValueTarget valueService, DependencyObject targetObject, DependencyObject rootObject)
private object HandleDependencyObject(IServiceProvider serviceProvider, IProvideValueTarget valueService, DependencyObject targetObject)
{
switch (valueService.TargetProperty)
{
case DependencyProperty dependencyProperty when dependencyProperty.PropertyType == typeof(ICommand):
// If they're in design mode and haven't set View.ActionTarget, default to looking sensible
return new CommandAction(targetObject, rootObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
return this.CreateCommandAction(serviceProvider, targetObject);
case EventInfo eventInfo:
{
var ec = new EventAction(targetObject, rootObject, eventInfo.EventHandlerType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
}
return this.CreateEventAction(serviceProvider, targetObject, eventInfo.EventHandlerType);
case MethodInfo methodInfo: // For attached events
{
var parameters = methodInfo.GetParameters();
if (parameters.Length == 2 && typeof(Delegate).IsAssignableFrom(parameters[1].ParameterType))
{
var ec = new EventAction(targetObject, rootObject, parameters[1].ParameterType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
var parameters = methodInfo.GetParameters();
if (parameters.Length == 2 && typeof(Delegate).IsAssignableFrom(parameters[1].ParameterType))
{
return this.CreateEventAction(serviceProvider, targetObject, parameters[1].ParameterType);
}
throw new ArgumentException("Action used with an attached event (or something similar) which didn't follow the normal pattern");
}
throw new ArgumentException("Action used with an attached event (or something similar) which didn't follow the normal pattern");
}
default:
throw new ArgumentException("Can only use ActionExtension with a Command property or an event handler");
}
}
private object HandleCommandBinding(DependencyObject rootObject, Type propertyType)
private ICommand CreateCommandAction(IServiceProvider serviceProvider, DependencyObject targetObject)
{
if (rootObject == null)
throw new InvalidOperationException("Action may only be used with CommandBinding from a XAML view (unable to retrieve IRootObjectProvider.RootObject)");
if (this.Target == null)
{
var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
var rootObject = rootObjectProvider?.RootObject as DependencyObject;
return new CommandAction(targetObject, rootObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
}
else
{
return new CommandAction(this.Target, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
}
}
private Delegate CreateEventAction(IServiceProvider serviceProvider, DependencyObject targetObject, Type eventType, bool isCommandBinding = false)
{
EventAction ec;
if (this.Target == null)
{
var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
var rootObject = rootObjectProvider?.RootObject as DependencyObject;
if (isCommandBinding)
{
if (rootObject == null)
throw new InvalidOperationException("Action may only be used with CommandBinding from a XAML view (unable to retrieve IRootObjectProvider.RootObject)");
ec = new EventAction(rootObject, null, eventType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
}
else
{
ec = new EventAction(targetObject, rootObject, eventType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
}
}
else
{
ec = new EventAction(this.Target, eventType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
}
var ec = new EventAction(rootObject, null, propertyType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
}
}

View File

@ -26,7 +26,7 @@ namespace Stylet.Xaml
private Func<bool> guardPropertyGetter;
/// <summary>
/// Initialises a new instance of the <see cref="CommandAction"/> class
/// Initialises a new instance of the <see cref="CommandAction"/> class to use <see cref="View.ActionTargetProperty"/> to get the target
/// </summary>
/// <param name="subject">View to grab the View.ActionTarget from</param>
/// <param name="backupSubject">Backup subject to use if no ActionTarget could be retrieved from the subject</param>
@ -37,6 +37,17 @@ namespace Stylet.Xaml
: base(subject, backupSubject, methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{ }
/// <summary>
/// Initialises a new instance of the <see cref="CommandAction"/> class to use an explicit target
/// </summary>
/// <param name="target">Target to find the method on</param>
/// <param name="methodName">Method name. the MyMethod in Buttom Command="{s:Action MyMethod}".</param>
/// <param name="targetNullBehaviour">Behaviour for it the relevant View.ActionTarget is null</param>
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
public CommandAction(object target, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
: base(target, methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{ }
private string GuardName
{
get { return "Can" + this.MethodName; }
@ -47,7 +58,7 @@ namespace Stylet.Xaml
/// </summary>
/// <param name="targetMethodInfo">MethodInfo of method on new target</param>
/// <param name="newTargetType">Type of new target</param>
protected internal override void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType)
private protected override void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType)
{
var methodParameters = targetMethodInfo.GetParameters();
if (methodParameters.Length > 1)
@ -63,34 +74,33 @@ namespace Stylet.Xaml
/// </summary>
/// <param name="oldTarget">Previous target</param>
/// <param name="newTarget">New target</param>
protected internal override void OnTargetChanged(object oldTarget, object newTarget)
private protected override void OnTargetChanged(object oldTarget, object newTarget)
{
var oldInpc = oldTarget as INotifyPropertyChanged;
if (oldInpc != null)
if (oldTarget is INotifyPropertyChanged oldInpc)
PropertyChangedEventManager.RemoveHandler(oldInpc, this.PropertyChangedHandler, this.GuardName);
this.guardPropertyGetter = null;
var inpc = newTarget as INotifyPropertyChanged;
if (inpc != null)
var guardPropertyInfo = newTarget?.GetType().GetProperty(this.GuardName);
if (guardPropertyInfo != null)
{
var guardPropertyInfo = newTarget.GetType().GetProperty(this.GuardName);
if (guardPropertyInfo != null)
if (guardPropertyInfo.PropertyType == typeof(bool))
{
if (guardPropertyInfo.PropertyType == typeof(bool))
{
var targetExpression = Expressions.Expression.Constant(newTarget);
var propertyAccess = Expressions.Expression.Property(targetExpression, guardPropertyInfo);
this.guardPropertyGetter = Expressions.Expression.Lambda<Func<bool>>(propertyAccess).Compile();
}
else
{
logger.Warn("Found guard property {0} for action {1} on target {2}, but its return type wasn't bool. Therefore, ignoring", this.GuardName, this.MethodName, newTarget);
}
var targetExpression = Expressions.Expression.Constant(newTarget);
var propertyAccess = Expressions.Expression.Property(targetExpression, guardPropertyInfo);
this.guardPropertyGetter = Expressions.Expression.Lambda<Func<bool>>(propertyAccess).Compile();
}
else
{
logger.Warn("Found guard property {0} for action {1} on target {2}, but its return type wasn't bool. Therefore, ignoring", this.GuardName, this.MethodName, newTarget);
}
}
if (this.guardPropertyGetter != null)
if (this.guardPropertyGetter != null)
{
if (newTarget is INotifyPropertyChanged inpc)
PropertyChangedEventManager.AddHandler(inpc, this.PropertyChangedHandler, this.GuardName);
else
logger.Warn("Found guard property {0} for action {1} on target {2}, but the target doesn't implement INotifyPropertyChanged, so changes won't be observed", this.GuardName, this.MethodName, newTarget);
}
this.UpdateCanExecute();

View File

@ -23,7 +23,7 @@ namespace Stylet.Xaml
private readonly Type eventHandlerType;
/// <summary>
/// Initialises a new instance of the <see cref="EventAction"/> class
/// Initialises a new instance of the <see cref="EventAction"/> classto use <see cref="View.ActionTargetProperty"/> to get the target
/// </summary>
/// <param name="subject">View whose View.ActionTarget we watch</param>
/// <param name="backupSubject">Backup subject to use if no ActionTarget could be retrieved from the subject</param>
@ -33,13 +33,32 @@ namespace Stylet.Xaml
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
public EventAction(DependencyObject subject, DependencyObject backupSubject, Type eventHandlerType, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
: base(subject, backupSubject, methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{
AssertBehaviours(targetNullBehaviour, actionNonExistentBehaviour);
this.eventHandlerType = eventHandlerType;
}
/// <summary>
/// Initialises a new instance of the <see cref="EventAction"/> class to use an explicit target
/// </summary>
/// <param name="target">Target to find the method on</param>
/// <param name="eventHandlerType">Type of event handler we're returning a delegate for</param>
/// <param name="methodName">The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired</param>
/// <param name="targetNullBehaviour">Behaviour for it the relevant View.ActionTarget is null</param>
/// <param name="actionNonExistentBehaviour">Behaviour for if the action doesn't exist on the View.ActionTarget</param>
public EventAction(object target, Type eventHandlerType, string methodName, ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
: base(target, methodName, targetNullBehaviour, actionNonExistentBehaviour, logger)
{
AssertBehaviours(targetNullBehaviour, actionNonExistentBehaviour);
this.eventHandlerType = eventHandlerType;
}
private static void AssertBehaviours(ActionUnavailableBehaviour targetNullBehaviour, ActionUnavailableBehaviour actionNonExistentBehaviour)
{
if (targetNullBehaviour == ActionUnavailableBehaviour.Disable)
throw new ArgumentException("Setting NullTarget = Disable is unsupported when used on an Event");
if (actionNonExistentBehaviour == ActionUnavailableBehaviour.Disable)
throw new ArgumentException("Setting ActionNotFound = Disable is unsupported when used on an Event");
this.eventHandlerType = eventHandlerType;
}
/// <summary>
@ -47,14 +66,14 @@ namespace Stylet.Xaml
/// </summary>
/// <param name="targetMethodInfo">MethodInfo of method on new target</param>
/// <param name="newTargetType">Type of new target</param>
protected internal override void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType)
private protected override void AssertTargetMethodInfo(MethodInfo targetMethodInfo, Type newTargetType)
{
var methodParameters = targetMethodInfo.GetParameters();
if (!(methodParameters.Length == 0 ||
(methodParameters.Length == 1 && (typeof(EventArgs).IsAssignableFrom(methodParameters[0].ParameterType) || methodParameters[0].ParameterType == typeof(DependencyPropertyChangedEventArgs))) ||
(methodParameters.Length == 2 && (typeof(EventArgs).IsAssignableFrom(methodParameters[1].ParameterType) || methodParameters[1].ParameterType == typeof(DependencyPropertyChangedEventArgs)))))
{
var e = new ActionSignatureInvalidException(String.Format("Method {0} on {1} must have the signatures void Method(), void Method(EventArgsOrSubClass e), or void Method(object sender, EventArgsOrSubClass e)", this.MethodName, newTargetType.Name));
var e = new ActionSignatureInvalidException(String.Format("Method {0} on {1} must have the signatures Method(), Method(EventArgsOrSubClass e), or Method(object sender, EventArgsOrSubClass e)", this.MethodName, newTargetType.Name));
logger.Error(e);
throw e;
}

View File

@ -126,6 +126,19 @@ namespace StyletUnitTests
Assert.Throws<InvalidOperationException>(() => this.actionExtension.ProvideValue(this.serviceProvider.Object));
}
[Test]
public void OverridesTargetIfSetCommand()
{
var target = new object();
this.actionExtension.Target = target;
this.provideValueTarget.Setup(x => x.TargetProperty).Returns(Button.CommandProperty);
var cmd = (CommandAction)this.actionExtension.ProvideValue(this.serviceProvider.Object);
Assert.AreSame(target, cmd.Target);
}
// Can't really test Target on EventAction. Oh well.
}
}

View File

@ -2,6 +2,7 @@
using Stylet;
using Stylet.Xaml;
using System;
using System.Runtime.CompilerServices;
using System.Windows;
namespace StyletUnitTests
@ -23,8 +24,11 @@ namespace StyletUnitTests
get { return this._canDoSomethingWithGuard; }
set { SetAndNotify(ref this._canDoSomethingWithGuard, value); }
}
public bool DoSomethingWithGuardCalled;
public void DoSomethingWithGuard()
{
this.DoSomethingWithGuardCalled = true;
}
public object DoSomethingArgument;
@ -62,6 +66,18 @@ namespace StyletUnitTests
{
}
private class TargetWithoutInpc
{
public bool CanDoSomething => false;
public void DoSomething() { }
}
public class StaticTarget
{
public static bool DidSomething;
public static void DoSomething() => DidSomething = true;
}
private DependencyObject subject;
private Target target;
@ -71,6 +87,7 @@ namespace StyletUnitTests
this.target = new Target();
this.subject = new DependencyObject();
View.SetActionTarget(this.subject, this.target);
StaticTarget.DidSomething = false;
}
[Test]
@ -171,6 +188,18 @@ namespace StyletUnitTests
Assert.True(eventRaised);
}
[Test]
public void FetchesGuardPropertyWhenTargetDoesNotImplementInpc()
{
var target = new TargetWithoutInpc();
var cmd = new CommandAction(this.subject, null, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
bool eventRaised = false;
cmd.CanExecuteChanged += (o, e) => eventRaised = true;
View.SetActionTarget(this.subject, target);
Assert.True(eventRaised);
Assert.False(cmd.CanExecute(null));
}
[Test]
public void RaisesEventWhenTargetChanges()
{
@ -297,5 +326,29 @@ namespace StyletUnitTests
cmd.Execute(null);
Assert.IsTrue(this.target.DoSomethingCalled);
}
[Test]
public void SupportsStaticTargets()
{
var cmd = new CommandAction(this.subject, null, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
View.SetActionTarget(this.subject, typeof(StaticTarget));
Assert.True(cmd.CanExecute(null));
cmd.Execute(null);
Assert.True(StaticTarget.DidSomething);
}
[Test]
public void UsesExplicitTarget()
{
var cmd = new CommandAction(this.target, "DoSomethingWithGuard", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
Assert.False(cmd.CanExecute(null));
this.target.CanDoSomethingWithGuard = true;
Assert.True(cmd.CanExecute(null));
cmd.Execute(null);
Assert.True(this.target.DoSomethingWithGuardCalled);
}
}
}

View File

@ -74,6 +74,12 @@ namespace StyletUnitTests
{
}
public class StaticTarget
{
public static bool DidSomething;
public static void DoSomething() => DidSomething = true;
}
private DependencyObject subject;
private Target target;
private EventInfo eventInfo;
@ -87,6 +93,7 @@ namespace StyletUnitTests
this.eventInfo = typeof(Subject).GetEvent("SimpleEventHandler");
this.dependencyChangedEventInfo = typeof(Subject).GetEvent("DependencyChangedEventHandler");
View.SetActionTarget(this.subject, this.target);
StaticTarget.DidSomething = false;
}
[Test]
@ -269,13 +276,31 @@ namespace StyletUnitTests
{
var view = new DependencyObject();
var backupView = new DependencyObject();
var cmd = new CommandAction(view, backupView, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
var cmd = new EventAction(view, backupView, this.eventInfo.EventHandlerType, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
View.SetActionTarget(backupView, this.target);
view.SetValue(FrameworkElement.DataContextProperty, this.target);
cmd.Execute(null);
cmd.GetDelegate().DynamicInvoke(null, null);
Assert.IsTrue(this.target.DoSomethingCalled);
}
[Test]
public void SupportsStaticTargets()
{
var cmd = new EventAction(this.subject, null, this.eventInfo.EventHandlerType, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
View.SetActionTarget(this.subject, typeof(StaticTarget));
cmd.GetDelegate().DynamicInvoke(null, null);
Assert.True(StaticTarget.DidSomething);
}
[Test]
public void UsesExplicitTarget()
{
var cmd = new EventAction(this.target, this.eventInfo.EventHandlerType, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
cmd.GetDelegate().DynamicInvoke(null, null);
Assert.True(this.target.DoSomethingCalled);
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="MSBuild.Sdk.Extras/2.1.2">
<Project Sdk="MSBuild.Sdk.Extras/3.0.23">
<PropertyGroup>
<TargetFrameworks>net5.0-windows</TargetFrameworks>
<TargetFrameworks>net472;net5.0-windows</TargetFrameworks>
<UseWpf>true</UseWpf>
<IsPackable>false</IsPackable>