I had checked out Timer in Portable Library, but I had no idea how to call the Change function, because it was not implemented in the class nor inherited from parent.
_timer = new Timer(OnTimerTick, null, TimeSpan.FromSeconds(1.0), TimeSpan.Zero);
_timer.Change(TimeSpan.FromSeconds(1.0), TimeSpan.Zero); //how to implement this
Instead of using the 1st solution: (building the class Timer) by David Kean, I am now using his 3rd solution: (create a target .NET 4.0 Timer adapter) with the code sample by Henry C.
Anyway, I'm still hoping to get some feedback on how to implement the Change function in the Timer class as defined in .NET. Thanks!
public class PCLTimer
{
private System.Threading.Timer timer;
private Action<object> action;
public PCLTimer(Action<object> action, object state, int dueTimeMilliseconds, int periodMilliseconds)
{
this.action = action;
timer = new System.Threading.Timer(PCLTimerCallback, state, dueTimeMilliseconds, periodMilliseconds);
}
public PCLTimer(Action<object> action, object state, TimeSpan dueTimeMilliseconds, TimeSpan periodMilliseconds)
{
this.action = action;
timer = new System.Threading.Timer(PCLTimerCallback, state, dueTimeMilliseconds, periodMilliseconds);
}
private void PCLTimerCallback(object state)
{
action.Invoke(state);
}
public bool Change(int dueTimeMilliseconds, int periodMilliseconds)
{
return timer.Change(dueTimeMilliseconds, periodMilliseconds);
}
public bool Change(TimeSpan dueTimeMilliseconds, TimeSpan periodMilliseconds)
{
return timer.Change(dueTimeMilliseconds, periodMilliseconds);
}
public new void Dispose()
{
timer.Dispose();
}
}
I have developed a support library containing types missing or incomplete in PCL. The library is called Shim and is also available on NuGet.
Shim comes in different instances for different platform targets. If you install Shim form NuGet, it will pick the relevant instance for your Visual Studio project, be it a portable class library, a Windows 8 application or a Xamarin.Android class library.
Shim contains a declaration of System.Threading.Timer, including two constructors and the Change(int, int) method. When using Shim from a Windows 8 application or class library, there is a Windows 8 specific implementation using the Windows.System.Threading.ThreadPoolTimer class internally. For the other (non-PCL) platform targets a [TypeForwardedTo] declaration is used to forward calls to the existing implementation for this particular target.
Some general implementation details can be found in this SO answer. If you don't want the overhead of the full Shim package, you could use the approach presented in this SO answer to implement the necessary parts yourself.
Related
I am writing here the same issue posted on Github since I don't see much traffic there recently.
.NET Core Version: 3.1.9 and .Net 5
Windows version: 10.0.18363
Does the bug reproduce also in WPF for .NET Framework 4.8?: AssemblyLoadContext not supported
I am trying to load and unload on demand a Wpf App library (and all the related dependencies). Everything works, but none of the assemblies get unloaded when calling the Unload method.
If I replace the Wpf library with a .Net Core library containing a few sample methods, I can see the library removed from VS Modules window after a couple of GC iterations.
If I'm not wrong I should expect AssemblyLoadContext to load WpfLibrary and related dependencies (PresentationCore, PresentationFramework etc), but WpfLibrary is the only one loaded. All the other dependencies seems be loaded in the default context. May be that I misunderstood how it works, but to me seems that the framework dependencies prevent the unloading.
Also I am not sure if the problem I am reporting is related to this and/or this.
I attached a sample project which is structured like this:
Project 1 (MainApp, a console project with added System.Windows.Forms reference to enable message pump)
class Program
{
class WpfAppAssemblyLoadContext : AssemblyLoadContext
{
public WpfAppAssemblyLoadContext() : base(true) { }
protected override Assembly Load(AssemblyName assemblyName) => null;
}
[MethodImpl(MethodImplOptions.NoInlining),]
public static void TestRun()
{
var context = new WpfAppAssemblyLoadContext();
var assembly = context.LoadFromAssemblyPath($"{Environment.CurrentDirectory}\\WpfLibrary.dll");
var inst = (IProxy) assembly.CreateInstance("WpfLibrary.MainWindow");
inst.ShowWindow();
inst.CloseWindow();
context.Unload();
assembly = null;
context = null;
inst = null;
}
[STAThread,]
static void Main(string[] args)
{
TestRun();
for (var i = 0; i < 100; i++) {
GC.Collect();
GC.WaitForPendingFinalizers();
}
Application.Run();
}
}
Project 2 (ProxyInterface)
namespace ProxyInterface
{
public interface IProxy
{
void ShowWindow();
void CloseWindow();
}
}
Project 3 (a regular wpf library with implementation of interface in Project 2 )
namespace WpfLibrary
{
public partial class MainWindow : Window, IProxy
{
public MainWindow()
{
InitializeComponent();
}
public void ShowWindow() { Show();}
public void CloseWindow() { Close();}
}
}
UnloadWpfLibrary.zip
(Solution file inside "MainApp" folder)
Further updates:
DotNet team added the issue to "Future Milestone", therefore I have to deduce that they recognized this as a bug. I have no idea on when we will see Wpf working with AssemblyLoadContext.
Seems to be that there is a workaround which involve splitting the target assembly into two separate assemblies. I attached the project with the suggested modifications and this time one of the two assemblies is unloaded, but all of the others are still loaded included WpfLibrary.
UnloadWpfLibraryWithWorkaround.zip
I think that for me it's time to give up and recur to IPC (named pipes) although I am not sure if this could be a valid replacement.
May be I missed something and someone more expert can do further progress and attach here the project with the correct modifications, it would be of great benefit for all the users that want to use ALC to load and unload WPF.
It would be a total of 4 projects just to load and unload a wpf assembly on demand and this is not exactly clean, but if the final result is the same it would be acceptable.
In Prism 7.1, the IModule interface has changed from version 6.3, and now exposes the two methods RegisterTypes (IContainerRegistry containerRegistry) and OnInitialized (IContainerProvider containerProvider). I ask forgiveness, but I cannot understand how I have to register the Views implemented in the Module. it's probably so simple that I can not see the solution to the problem. Can you give me an example to finally make me understand how I should do? Wherever I looked, I found only examples regarding version 6.3, which I know rather well ..
Prism 7.X introduces an abstraction around the DI Container. There were a number of reasons for this but the top two are:
Many of the support questions from the community to the Prism team revolved around how to do something with a container that has nothing to do with the Prism team.
By abstracting the container it makes scenarios around sharing code and swapping out containers easier.
It's also important to understand that by abstracting the Container we've also made changes to the Container Extensions responsible for registering Views. Specifically we now have them on the IContainerRegistry. So given the sample Prism 6.X Module here:
public class ModuleA
{
private IUnityContainer _container { get; }
public ModuleA(IUnityContainer container)
{
_container = container;
}
public void Initialize()
{
// register stuff
_container.RegisterViewForNavigation<ViewA>();
// Setup Event listeners etc...
var ea = _container.Resolve<IEventAggregator>();
}
}
We would update this to:
public class ModuleA
{
public void OnInitialized(IContainerProvider containerProvider)
{
// Setup Event listeners etc...
var ea = containerProvider.Resolve<IEventAggregator>();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
// register stuff
containerRegistry.RegisterForNavigation<ViewA>();
}
}
it's probably so simple that I can not see the solution to the problem
Just use RegisterTypes - registering a view is registering a type, after all.
I've written a WPF app that has two different main windows. I don't know which one to launch until runtime by looking up what kind of user is using the program in a database. The code I currently have works but Castle Windsor is doing tons of extra work by newing up the object graphs for both kinds of windows.
private readonly IMainWindow _mainWindow;
private readonly ISimplifiedMainWindow _simplifiedMainWindow;
public MainClass(
IMainWindow mainWindow,
ISimplifiedMainWindow simplifiedMainWindow)
{
_mainWindow = mainWindow;
_simplifiedMainWindow = simplifiedMainWindow;
}
public RunApp()
{ // pseudocode
if (user is fullUser) _mainWindow.Show();
else _simplifiedMainWindow.Show();
}
How do I defer creation of my window objects without resorting to making an abstract factory that will basically duplicate what Castle Windsor does anyway?
A factory is in fact the solution I'd recommend (and a solution I've successfully used multiple times in the past to solve this very problem).
I wouldn't implement the factory myself though, let Windsor do it (via a Typed Factory).
public interface IWindowFactory
{
IMainWindow FullUserWindow();
ISimplifiedMainWindow SimplifiedUserWindow();
//optionally
void DestroyWindow(IWindow window);
}
Now you just need to tell Windsor to build a factory for that interface
container.AddFacility<TypedFactoryFacility>();
// later on, in your installer
container.Register(Component.For<IWindowFactory>()
.AsFactory()
.LifestyleTransient());
and your app code changes to:
public RunApp()
{ // pseudocode
if (user is fullUser) Show(factory.FullUserWindow());
else Show(factory.SimplifiedUserWindow());
}
I've been reading through a lot of the Jabbr code to learn Nancy and trying to implement many of the same patterns in my own application. One of the things I can't seem to get working is the concept of an on application start class. The Jabbr code base has an App_Start folder with a Startup.cs file (here) in it with the following implementation.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
...
SetupNancy(kernel, app);
...
}
}
private static void SetupNancy(IKernel kernel, IAppBuilder app)
{
var bootstrapper = new JabbRNinjectNancyBootstrapper(kernel);
app.UseNancy(bootstrapper);
}
When I tried to do something similar to that in my project the Startup.cs file was just ignored. I searched the Jabbr code base to see if it was used anywhere but I wasn't able to find anything and the only differences I could see is Jabbr uses Ninject while I wanted to use AutoFac
Is there a way to register a startup class in nancy?
Take a look at my project over on GitHub, you'll be interested in the Spike branch and may have to unload the ChainLink.Web project to run I can't remember.
I had some trouble finding a way to configure the ILifetimeScope even after reading the accepted answer here by TheCodeJunkie. Here's how you do the actual configuration:
In the bootstrapper class derived from the AutofacNancyBootstrapper, to actually configure the request container, you update the ILifetimeScope's component registry.
protected override void ConfigureRequestContainer(
ILifetimeScope container, NancyContext context)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>();
builder.Update(container.ComponentRegistry);
}
The application container can be updated similarly in the ConfigureApplicationContainer override.
You should install the Nancy.Bootstrappers.Autofac nuget, inherit from the AutofacNancyBootstrapper type and override the appropriate method (depending on your lifetime scope requirements: application or request). For more info check the readme file https://github.com/nancyfx/nancy.bootstrappers.autofac
HTH
After following the advice from TheCodeJunkie you can use the Update method on the ILifetimeScope container parameter which gives you a ContainerBuilder through an Action:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
container.Update(builder =>
{
builder.RegisterType<MyType>();
});
}
If I have two windows in an oob application how do I communicate between them?
This is the new feature of silverlight 5 that allows for multiple windows.
They run in a common application. Hence they share the same static data. The scope of communication choices are therefore very large. Here is an example:-
public class MessageEventArgs : EventArgs
{
public MessageEventArgs(object payload)
{
Payload = payload;
}
public object Payload {get; private set; }
}
public class Messenger
{
private static readonly Messenger _current = new Messenger();
public static Messenger Current { get { return _current; } }
public event EventHandler<MessageEventArgs> MessageReceived;
public void Send(object payload)
{
if (MessageReceived != null)
MessageReceived(this, new MessageEventArgs(payload));
}
}
All windows can attach a handler to Messenger.Current.MessageReceived (just be sure to detach when the window closes) and any window can call Messenger.Current.Send.
Ok so you wouldn't actually use this code its a bit rubbish, the point is Windows in SL5 are not isolated. You can create whatever internal application communication mechanism you need.
Option 1: MVVM Pattern
Both windows share a reference to the same view-model. Changes made by one are seen by both.
Option 2: Normal references
Window A can how a refernce to Windows B when it creates it.
Option 3: Message Passing
You can have a global event that you subscribe to in the Load event. (Make sure you unsubscribe in the Unload event or you will leak memory!) Windows can post messages to that event which the other windows listen for.