Code Monkey home page Code Monkey logo

splat's Introduction

NuGet Stats Build Code Coverage

Splat

Certain types of things are basically impossible to do in cross-platform mobile code today, yet there's no reason why. Writing a ViewModel that handles loading a gallery of pictures from disk will be completely riddled with #ifdefs and basically unreadable.

Splat aims to fix that, by providing a usable leaky abstraction above platform code. It is leaky, because it always provides an extension method ToNative() and FromNative(), which converts the abstraction to the platform-specific version. Load the image in the cross-platform code, then call ToNative() in your view to actually display it.

What does it do?

Splat currently supports:

  • Cross-platform image loading/saving
  • A port of System.Drawing.Color for portable libraries
  • Cross-platform geometry primitives (PointF, SizeF, RectangleF), as well as a bunch of additional extension methods to make using them easier.
  • A way to detect whether you're in a Unit Test runner / Design Mode
  • A cross-platform logging framework
  • Simple yet flexible Service Location

Core Team


Glenn Watson

Melbourne, Australia


Rodney Littles II

Texas, USA


David Vreony

UK


Chris Pulman

UK

How do I install?

Always Be NuGetting. Package contains binaries for:

  • .NET Framework 4.6.2, .NET Framework 4.7.2, .NET Standard 2.0, .NET 6.0, and .NET 8.0
  • Works with:
    • WPF
    • Windows Forms
    • WinUI 3
    • Maui (WinUI, Android, iOS and Mac)
    • Avalonia

Detecting whether you're in a unit test runner

// If true, we are running unit tests
ModeDetector.InUnitTestRunner();  

Service Location

Splat provides a simple service location implementation that is optimized for Desktop and Mobile applications, while still remaining reasonably flexible.

There are 2 parts to the locator design:

  • Locator.Current The property to use to retrieve services. Locator.Current is a static variable that can be set on startup, to adapt Splat to other DI/IoC frameworks. We're currently working from v7 onward to make it easier to use your DI/IoC framework of choice. (see below)
  • Locator.CurrentMutable The property to use to register services

To get a service:

// To get a single service registration
var toaster = Locator.Current.GetService<IToaster>();

// To get all service registrations
var allToasterImpls = Locator.Current.GetServices<IToaster>();

Locator.Current is a static variable that can be set on startup, to adapt Splat to other DI/IoC frameworks. We're currently working from v7 onward to make it easier to use your DI/IoC framework of choice.

The default implementation of Service Location also allows new types to be registered at runtime.

// Create a new Toaster any time someone asks
Locator.CurrentMutable.Register(() => new Toaster(), typeof(IToaster));

// Register a singleton instance
Locator.CurrentMutable.RegisterConstant(new ExtraGoodToaster(), typeof(IToaster));

// Register a singleton which won't get created until the first user accesses it
Locator.CurrentMutable.RegisterLazySingleton(() => new LazyToaster(), typeof(IToaster));

Dependency Injection Source Generator

There is a source generator that will inject constructor and properties. See here for instructions.

Dependency Resolver Packages

For each of the provided dependency resolver adapters, there is a specific package that allows the service locator to be implemented by another ioc container.

Note: When using ReactiveUI and overriding Splat's default behavior, you have to be sure to initialize ReactiveUI before your container finalizes.

Please note: If you are adjusting behaviours of Splat by working with your custom container directly. Please read the relevant projects documentation on REPLACING the registration. If the container supports appending\ multiple registrations you may get undesired behaviours, such as the wrong logger factory being used.

Container NuGet Read Me
Splat.Autofac SplatAutofacBadge Setup Autofac
Splat.DryIoc SplatDryIocBadge Setup DryIoc
Splat.Microsoft.Extensions.DependencyInjection SplatMicrosoftBadge Setup Microsoft DI
Splat.Ninject SplatNinjectBadge Setup Ninject
Splat.SimpleInjector SplatSimpleInjectorBadge Setup Simple Injector

Logging

Splat provides a simple logging proxy for libraries and applications to set up. By default, this logging isn't configured (i.e. it logs to the Null Logger). To set up logging:

  1. Register an implementation of ILogger using Service Location.
  2. In the class in which you want to log stuff, "implement" the IEnableLogger interface (this is a tag interface, no implementation actually needed).
  3. Call the Log method to write log entries:
this.Log().Warn("Something bad happened: {0}", errorMessage);
this.Log().ErrorException("Tried to do a thing and failed", exception);

For static methods, LogHost.Default can be used as the object to write a log entry for. The Static logger uses a different interface from the main logger to allow capture of additional caller context as it doesn't have the details of the class instance etc. when compared to the normal logger. To get the benefit of these you don't need to do much as they are optional parameters at the end of the methods that are utilised by the compiler\framework. Currently we only capture CallerMemberName.

Available logging adapters

Splat has support for the following logging frameworks

Target Package NuGet
Console Splat SplatBadge
Debug Splat SplatBadge
Log4Net Splat.Log4Net SplatLog4NetBadge
Microsoft Extensions Logging Splat.Microsoft.Extensions.Logging SplatMicrosoftExtensionsLoggingBadge
NLog Splat.NLog SplatNLogBadge
Serilog Splat.Serilog SplatSerilogBadge

Log4Net

First configure Log4Net. For guidance see https://logging.apache.org/log4net/release/manual/configuration.html

using Splat.Log4Net;

// then in your service locator initialisation
Locator.CurrentMutable.UseLog4NetWithWrappingFullLogger();

Thanks to @dpvreony for first creating this logger.

Microsoft.Extensions.Logging

First configure Microsoft.Extensions.Logging. For guidance see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/

using Splat.Microsoft.Extensions.Logging;

// note: this is different from the other adapter extension methods
//       as it needs knowledge of the logger factory
//       also the "container" is how you configured the Microsoft.Logging.Extensions
var loggerFactory = container.Resolve<ILoggerFactory>();
// in theory it could also be
// var loggerFactory = new LoggerFactory();


/// then in your service locator initialisation
Locator.CurrentMutable.UseMicrosoftExtensionsLoggingWithWrappingFullLogger(loggerFactory);

Thanks to @dpvreony for first creating this logger.

NLog

First configure NLog. For guidance see https://github.com/nlog/nlog/wiki/Tutorial and https://github.com/nlog/nlog/wiki/Configuration-file

using Splat.NLog;

//  then in your service locator initialisation
Locator.CurrentMutable.UseNLogWithWrappingFullLogger();

Thanks to @dpvreony for first creating this logger.

Serilog

First configure Serilog. For guidance see https://github.com/serilog/serilog/wiki/Configuration-Basics

using Splat.Serilog;

// Then in your service locator initialisation
Locator.CurrentMutable.UseSerilogFullLogger()

Thanks to @joelweiss for first creating this logger.

Cross platform drawing

Target Package NuGet
Splat.Drawing Splat.Drawing SplatDrawingBadge

Using Cross-Platform Colors and Geometry

// This System.Drawing class works, even on WinRT where it's not supposed to exist
// Also, this works in a Portable Library, in your ViewModel
ProfileBackgroundAccentColor = Color.FromArgb(255, 255, 255, 255);

Later, in the view, we can use it:

ImageView.Background = ViewModel.ProfileBackgroundAccentColor.ToNativeBrush();

If targeting iOS or Mac in a cross-platform solution (e.g. iOS & Android), use the SplatColor class to define colors in your netstandard library (since Cocoa doesn't include System.Drawing.Color).

// In a netstandard library
SplatColor BackgroundColor = SplatColor.Red;
// From an iOS project
UIColor bgColor = ViewModel.BackgroundColor.ToNative();
// From an Android project
Android.Graphics.Color bgColor = ViewModel.BackgroundColor.ToNative();

Cross-platform Image Loading

You can register with the Splat locators.

Locator.CurrentMutable.RegisterPlatformBitmapLoader();

You can then load your images in a cross platform way:

//
// Load an Image
// This code even works in a Portable Library
//

var wc = new WebClient();
Stream imageStream = wc.OpenRead("http://octodex.github.com/images/Professortocat_v2.png");

// IBitmap is a type that provides basic image information such as dimensions
IBitmap profileImage = await BitmapLoader.Current.Load(imageStream, null /* Use original width */, null /* Use original height */);

Then later, in your View:

// ToNative always converts an IBitmap into the type that the platform
// uses, such as UIBitmap on iOS or BitmapSource in WPF
ImageView.Source = ViewModel.ProfileImage.ToNative();

Images can also be loaded from a Resource. On Android, this can either be a Resource ID casted to a string, or the name of the resource as as string (optionally including the extension).

var profileImage = await BitmapLoader.Current.LoadFromResource("DefaultAvatar.png", null, null);

Bitmaps can also be created and saved - actually drawing on the image is beyond the scope of this library, you should do this in your view-specific code.

var blankImage = BitmapLoader.Current.Create(512.0f, 512.0f);
await blankImage.Save(CompressedBitmapFormat.Png, 0.0, File.Open("ItsBlank.png"));

Detecting if you're in design mode

// If true, we are running inside Blend, so don't do anything
PlatformModeDetector.InDesignMode();

Application Performance Monitoring

Application Performance Monitoring is split into the follow sections

  • Error Reporting
  • Feature Usage Tracking
  • View Tracking

The table below shows the support across various APM packages

Product Package NuGet Maturity Level Error Reporting Feature Usage Tracking View Tracking
Appcenter Splat.AppCenter SplatAppcenterBadge Alpha TODO Native Native
Application Insights Splat.ApplicationInsights SplatApplicationInsightsBadge Alpha TODO Native Native
Exceptionless Splat.Exceptionless SplatExceptionlessBadge Alpha TODO Native By Convention
New Relic N\A N\A Not Started TODO TODO TODO
OpenTrace N\A N\A Not Started TODO TODO TODO
Raygun Splat.Raygun SplatRaygunBadge Prototype TODO By Convention By Convention

Goals of the Splat APM feature

  • To sit on top of existing APM libaries using native features where possible, or by using a common convention that gives parity in behaviour. ** Where there is a convention behaviour it will be detailed under the relevant frameworks documentation.
  • To define basic behaviours that are dropped into consuming libraries, for example with ReactiveUI ** Commands ** ViewModels ** Views

Getting started with APM with Splat

Splat comes with a default implementation that pushes events into your active Splat logging framework. This allows for design and testing prior to hooking up a full APM offering.

Error Reporting

TODO

Feature Usage Tracking

The most basic ability for feature usage tracking is to implement the Splat.ApplicationPerformanceMonitoring.IEnableFeatureUsageTracking interface. This has the same behaviour as the logging interface and allows Splat to inject whichever APM platform is registered with the ServiceLocator at initialization.

        /// <summary>
        /// Dummy object for testing IEnableFeatureUsageTracking.
        /// </summary>
        public sealed class TestObjectThatSupportsFeatureUsageTracking : IEnableFeatureUsageTracking
        {
			public async Task SomeFeatureIWantToTrack()
			{
                using (var trackingSession = this.FeatureUsageTrackingSession("featureName"))
                {
					try
					{
						// do some work here.
					}
					catch (Exception exception)
					{
						trackingSession.OnException(exception);
					}
                }
			}
        }

Splat also has the notion of subfeatures, some APM platforms support this natively, others have been done by convention, which will be explained in the relevant library. Splat itself does not dictate when these should be used. It's up to you. You may have a primary feature (such as a search view) and then track buttons, etc. on that view as subfeatures.

        /// <summary>
        /// Dummy object for testing IEnableFeatureUsageTracking.
        /// </summary>
        public sealed class TestObjectThatSupportsFeatureUsageTracking : IEnableFeatureUsageTracking
        {
			public async Task SomeFeatureIWantToTrack()
			{
                using (var mainFeature = this.FeatureUsageTrackingSession("featureName"))
                {
					try
					{
						await DoSubFeature(mainFeature).ConfigureAwait(false);
					}
					catch (Exception exception)
					{
						mainFeature.OnException(exception);
					}
                }
			}

			public async Task SomeFeatureIWantToTrack(IFeatureUsageTrackingSession parentFeature)
			{
                using (var subFeature = parentFeature.SubFeature("subFeatureName"))
                {
					try
					{
						// do some work here.
					}
					catch (Exception exception)
					{
						subFeature.OnException(exception);
					}
                }
			}
        }

View Tracking

TODO

Configuring Appcenter

First configure Appcenter. For guidance see https://docs.microsoft.com/en-us/appcenter/diagnostics/enabling-diagnostics

using Splat.AppCenter;

// then in your service locator initialisation
Locator.CurrentMutable.UseAppcenterApm();

Configuring Application Insights

First configure Application Insights. For guidance see https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service

using Splat.ApplicationInsights;

// then in your service locator initialisation
Locator.CurrentMutable.UseApplicationInsightsApm();

Configuring Exceptionless

First configure Exceptionless. For guidance see https://github.com/exceptionless/Exceptionless/wiki/Getting-Started

using Splat.Exceptionless;

// then in your service locator initialisation
Locator.CurrentMutable.UseExceptionlessApm();

Configuring New Relic

New Relic support isn't currently available.

Configuring OpenTrace

OpenTrace support isn't currently available.

Configuring Raygun

First configure Raygun. For guidance see TODO

using Splat.Raygun;

// then in your service locator initialisation
Locator.CurrentMutable.UseRaygunApm();

Testing and developing the APM functionality

The unit tests for this functionality do not generate activity to the relevant platform. The integration tests DO SEND TEST DATA to the relevant platforms, so they need to have the user-secrets configured. There is a script in the \scripts\inttestusersecrets.cmd that shows how to set the relevant secrets up.

Contribute

Splat is developed under an OSI-approved open source license, making it freely usable and distributable, even for commercial use. We โค the people who are involved in this project, and weโ€™d love to have you on board, especially if you are just getting started or have never contributed to open-source before.

So here's to you, lovely person who wants to join us โ€” this is how you can support us:

splat's People

Contributors

adamradocz avatar alski avatar anaisbetts avatar cabauman avatar chrispulman avatar dependabot-preview[bot] avatar dependabot[bot] avatar dharmaturtle avatar distantcam avatar dpvreony avatar flagbug avatar ghuntley avatar glennawatson avatar jlaanstra avatar kentcb avatar martinsmyllie avatar oleksandrtsvirkun avatar olevett avatar patsissons avatar peteryongzhong avatar renovate[bot] avatar rgomez90 avatar rlittlesii avatar rzhw avatar shiftbot avatar shiftkey avatar simoncropp avatar tomasfil avatar weitzhandler avatar worldbeater avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

splat's Issues

How could I use Log in Splat

I read the Logging part of Readme.md, but I still don't know how to use it. Could you be more specific? Or give me a sample?

Cross Platform API for spawning additional event loop threads?

What do you think about adding an API to spawn additional event loop threads? The use case is to allow the dispatching of certain asynchronous work on to a single background thread, where it can benefit from having thread affinity for non-thread safe operations while not having to worry about executing work that could block the UI thread. Common use case would be making networking calls and then parsing response data on the thread. This works particularly nicely with F# Async workflows that make thread hopping trivial. I've included a potential API below, and could provide a PR with an Android implementation. Are there any cross platform issues, specifically for windows phone?

Potential API surface:

namespace Splat
{
    public interface IEventLoop : IDisposable
    {
        SynchronizationContext Context { get; }
    }

    public static class EventLoop
    {
        Task<IEventLoop> Create()
        {
            throw new Exception("Could not create EventLoop. This should never happen, your dependency resolver is broken");
        }
    }
}

Exception logging improvements

Hi! I have a little proposal for splat-2 logging facilities:
What do you think about replacing IFullLogger methods like this

void ErrorException([Localizable(false)] string message, Exception exception);

by methods like this

void Error(Exception exception, [Localizable(false)] string message, params object[] args);

In my opinion it has two advantages: 1) there is no need to use String.Format inside ErrorException call and actual formatting could be done by wrapped logging library (that is quite important for Serilog) and 2) these methods don't need Exception suffix.

Xamarin.Mac

I would like to use this library in Xamarin Mac but looks like it is referencing PresentationCore, is it possible to not do so? Wait, I just noticed there is a monomac project. Does it work?

Clipboard

What are people's thoughts on clipboard support? It seems easy enough theoretically.

So it turns out Windows Phone only supports write-only, text-only access.

I'm guessing that the design of Splat right now is that everything that is provided should be usable, instead of needing to if (Foo.CanPerformThisAction(HugeEnum.Format)), so maybe this is out of scope?

TypeLoadException - PCL and iOS project

After testing akavache successfully, I'm starting to use Splat in our mvvmcross based project. It has a core project that is a PCL (profile78) with an ios "app" project linked to it. Without any IBitmaps in the PCL code - it works and I can get IBitmaps (and use ToNative() on them) in the iOS side. However,
if I even declare a single variable in the PCL project as an IBitmap, I get the following error(s):

Missing method ToNative in assembly /Users/PATHTOAPP/MyAppName.app/MyAppName.exe, type Splat.BitmapMixins
(and a while later in the midst of some mvvmcross debug spam)
TypeLoadException: Could not load type 'Splat.BitmapMixins' from assembly 'MyAppName'

I'm not even using the variable, just declared it. Using IBitmaps and those from the Akavache.LoadImage() functions did but only on the iOS side. The reference in the PCL side is:
F:\PATHTOAPP\packages\Splat.1.4.1\lib\Portable-net45+win+wpa81+wp80\Splat.dll
and the reference on the iOS side is:
F:\PATHTOAPP\packages\Splat.1.4.1\lib\monotouch\Splat.dll

Akavache nuget added it to the project. And I have also added the splat reference before adding akavache to both the PCL and iOS project... I also removed all those references and re-added them to both PCL and iOS projects, Am I doing that wrong? What is the proper order to add them? Kind of dead in the water at the moment any help would be appreciated.

Strange array manipulation when doing string format

public void Debug()
{
    var logger = Locator.Current.GetService<ILogManager>().GetLogger(typeof(ClassWithExistingField));
    logger.Debug("jjh {0}", new object[]{ 1});
}

causes this

System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Object[]'.
   at System.RuntimeType.TryChangeType(Objectย value,ย Binderย binder,ย CultureInfoย culture,ย Booleanย needsSpecialCast)
   at System.Reflection.MethodBase.CheckArguments(Object[]ย parameters,ย Binderย binder,ย BindingFlagsย invokeAttr,ย CultureInfoย culture,ย Signatureย sig)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Objectย obj,ย BindingFlagsย invokeAttr,ย Binderย binder,ย Object[]ย parameters,ย CultureInfoย culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Objectย obj,ย BindingFlagsย invokeAttr,ย Binderย binder,ย Object[]ย parameters,ย CultureInfoย culture)
   at System.Reflection.MethodBase.Invoke(Objectย obj,ย Object[]ย parameters)
   at Splat.WrappingFullLogger.Debug(Stringย message,ย Object[]ย args) in Logging.cs: line 270

so in WrappingFullLogger

i thinks this

    public void Debug(string message, params object[] args)
    {
        var sfArgs = new object[args.Length + 2];
        sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message;
        Array.Copy(args, 0, sfArgs, 2, args.Length);
        string result = (string)stringFormat.Invoke(null, sfArgs);

        _inner.Write(prefix + result, LogLevel.Debug);
    }

should be this

    public void Debug(string message, params object[] args)
    {
        var sfArgs = new object[args.Length + 2];
        sfArgs[0] = CultureInfo.InvariantCulture; 
        sfArgs[1] = message;
        sfArgs[2] = args;
        string result = (string)stringFormat.Invoke(null, sfArgs);

        _inner.Write(prefix + result, LogLevel.Debug);
    }

Provide blocking versions of Bitmap.Load*

Given that both Load and LoadFromResource are not actually async but rather run as tasks on the threadpool (at least on android/xaml) would be it possible to offer two versions of these APIs:

  • Bitmap.Load
  • Bitmap.LoadAsync
  • Bitmap.LoadFromResource
    *Bitmap.LoadFromResourceAsync

The main rationale is that if the code you are executing is already on a background thread or eventloop(wink) where you can block the thread safely, the cost of the context switch to the threadpool out weights the benefit of being async.

Also reading through the iOS code, I was curious why the work to load the image was being dispatched to the main thread. UIImage appears to be threadsafe (http://stackoverflow.com/questions/10645307/thread-safety-of-uiimage) and the APIs to load images block afaict. Why not run these on threadpool similar to android?

MonoMac support

Looks like there's some issues with using this with MonoMac instead of Xamarin.Mac. I know the readme doesn't list MonoMac support, but I thought I'd raise the issue to see if it's worth fixing. So there's a few things:

I tried using this addin to install via NuGet into a MonoMac project and got the WPF version. Not sure where the source of the issue would be.

Then tried manually changing the reference to ..\packages\Splat.0.3.3\lib\MonoMac\Splat.dll, but it seems that it's the Android version (dependency on Mono.Android)

Finally tried referencing the monomac csproj, and needed to change the XamMac reference to MonoMac. It looks like NSColor.FromSrgb is exclusive to XamMac however, so I've tried changing this to use NSColor.FromDeviceRgba. I'm guessing that's probably not a good idea (possible colour space conversion?)

Bitmap width/height

Is it a concern that IBitmap width/height doesn't report the real pixel width/height but instead it's dpi/platform/config dependent?

These two 512x512 pngs were extracted from an icns file: Image one (72dpi) Image two (144dpi)

Using WPF:

async void Blah()
{
    var dlg = new Microsoft.Win32.OpenFileDialog();
    dlg.Filter = "PNG Files (*.png)|*.png";
    var result = dlg.ShowDialog();
    if (result == true)
    {
        // Open document 
        string filename = dlg.FileName;
        using (var stream = new FileStream(filename, FileMode.Open))
        {
            var bitmap = await Splat.BitmapLoader.Current.Load(stream, null, null);
            MessageBox.Show("width " + (int)bitmap.Width + " height " + (int)bitmap.Height);
        }
    }
}

This reports 682x682 and 341x341 respectively at 100% DPI.

Using Cocoa:

async void Blah()
{
    var openPanel = new NSOpenPanel();
    openPanel.ReleasedWhenClosed = true;
    openPanel.AllowedFileTypes = new[] { "png" };
    var result = openPanel.RunModal();
    using (var stream = new System.IO.FileStream(openPanel.Url.Path, System.IO.FileMode.Open))
    {
        var bitmap = await Splat.BitmapLoader.Current.Load(stream, null, null);
        var alert = new NSAlert();
        alert.MessageText = "width " + (int)bitmap.Width + " height " + (int)bitmap.Height;
        alert.AddButton("OK");
        alert.RunModal();
    }
}

This reports 512x512 and 256x256 respectively at 200% DPI.

WP8 Cross Theading on IBitmap Save

On WP8 the save function blows up with:

An exception of type 'System.UnauthorizedAccessException' occurred in mscorlib.ni.dll but was not handled in user code
   at MS.Internal.XcpImports.CheckThread()
   at System.Windows.DependencyObject..ctor(UInt32 nativeTypeIndex, IntPtr constructDO)
   at System.Windows.Media.Imaging.WriteableBitmap..ctor(BitmapSource source)
   at Splat.BitmapSourceBitmap.<>c__DisplayClass1.<Save>b__0()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at ThePaperWall.WP8.ViewModels.MainPageViewModel.<OnActivate>d__4.MoveNext()

I know. You're thrilled.

System.Drawing.Color no longer available in Xamarin.Mac 1.12.x+

Using splat on the beta channel of Xamarin right now with Unified Xamarin.Mac 1.12.0.4 and Color is no longer an understood reference. Note that it IS available on the stable channel (1/29/15) Unified Xamarin.Mac 1.10.0.18. But Xamarin support says this was removed in 1.12 as part of the ongoing transition of the Mac stuff to Unified.

I think the fix may be to include the Colors folder into the Splat-XamarinMac project. But then Splat will not build due to the TypeForwardedTo's in TypeForwardedSystemDrawing.cs. Could change the preprocessor logic there to "#if !UNIFIED" but that raises the larger question of all the places where "#if UIKIT" kinds of things are going on. Maybe they all should be just based on UNIFIED going forward, not sure yet.

DebugLogger - Debug.WriteLine doesn't print anything

Creating a new instance of DebugLogger and calling DebugLogger.Write(message, level) doesn't print anything to the console. If I copy the implementation and paste it into the current project, and use that instance instead, everything works fine.

Is it an issue that you're building the Splat libraries in Release mode and thus Debug.WriteLine is being omitted from the IL? Would using Trace.WriteLine solve this issue? or maybe defining the DEBUG symbol within Logging.cs so that the Debug calls are left alone?

Literally not a real issue per-se, as it can be remedied by re-implementing ILogger in a small amount of lines, but it did leave me dumbfounded for a good few minutes while trying to learn ReactiveUI with Splat's logging.

WinRT really doesn't like big bitmaps

Reproduce by creating a new Blank Page project (applies to both Windows 8 and Windows 8.1 preview), taking this bmp file, drop it into Assets/failure.bmp, and then putting this code into MainPage.xaml.cs.

It proceeds to explode with this exception:

An exception of type 'System.Exception' occurred in mscorlib.dll but was not handled in user code

Additional information: The image is unrecognized. (Exception from HRESULT: 0x88982F60)

And with pre-0.3.0 code:

An exception of type 'System.Exception' occurred in mscorlib.dll but was not handled in user code

Additional information: The component cannot be found. (Exception from HRESULT: 0x88982F50)

This is probably more of a WinRT issue (I feel for you with those comments you've written), though I'm not getting my hopes up too much for it to be fixed on the WinRT side at this point in time

ModeDetector.InUnitTestRunner is wrong for my .net mstest assembly

I was having issues with a ReactiveList complaining about there being no dispatcher on the current thread. After digging a bit, I checked to see how ReactiveUI was deciding what schedulers to use for unit testing. That lead me to splat. The below test fails:

[TestMethod]
 public void ShouldBeInUnitTestRunner()
 {
         ModeDetector.InUnitTestRunner().Should().BeTrue("this is a unit test");
 }

This is a full framework WPF application running against .NET 4.5.2, and i am using mstest for my test assembly.

My questions:

  1. Why might splat be getting this wrong?
    2. How can i find out what implementation is selected in my case?
    3. Where do i find the code for that implementation...is that a separate nuget package?

IBitmap exception with invalid image

I tried to download the following file: http://pbs.twimg.com/profile_images/488751537720922112/8tBkI3WN_normal.jpeg

It then gets saved in a IBitmap, but is obviously an html page. I then had a check in my code that see's if the IBitmap instance is null (which it's not) and then checks that the Width and Height are greater than 0. Splat then throws an exception when trying to work out the Width and Height.

You should have a property in IBitmap called IsValid or something for the user to check that the IBitmap is in fact a proper image. At least then when Width or Height throws an exception it's easy enough to figure out that IsValid is indeed false. Or return null from BitmapLoader if image is not valid.

                byte[] data = await DownloadImage(url, token); 
                IBitmap item = await BitmapLoader.Current.Load(new MemoryStream(data), null, null); // does not return a valid image

WP8 LoadImageFromURL

I heard you like threads in Windows.

 BlobCache.LocalMachine.LoadImageFromUrl()
"Invalid cross-thread access."

   at MS.Internal.XcpImports.CheckThread()
   at System.Windows.DependencyObject..ctor(UInt32 nativeTypeIndex, IntPtr constructDO)
   at System.Windows.Media.Imaging.BitmapImage..ctor()
   at Splat.PlatformBitmapLoader.<>c__DisplayClass2.<Load>b__0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Reactive.PlatformServices.ExceptionServicesImpl.Rethrow(Exception exception)
   at System.Reactive.ExceptionHelpers.ThrowIfNotNull(Exception exception)
   at System.Reactive.Subjects.AsyncSubject`1.GetResult()
   at ThePaperWall.Core.Downloads.AsyncDownloadManager.<DownloadImage>d__4.MoveNext()

Json Serialization Error

I'm trying to use Akavache with Splat for image caching in a Xam Forms project. When unit testing, I kept getting the following error any time I access the cache:

Unexpected token while deserializing object: PropertyName. Path 'Value.Width'.

I created a simple console app that experiences the same problem when using the GetImage method or gets an invalid image exception when using GetImage2. The image is a just a sample I pulled from Google images, but is a valid image. The example App can be found in my Git repo

Reflection exception with logging.

Calling

            this.Log().Debug(
                "Set value of {0}.{1} (#{2:x8}) to '{3}'",
                this.GetType().Name,
                property.Name,
                this.GetHashCode(),
                value);

Gives the following exception:

An unhandled exception of type 'System.Reflection.TargetParameterCountException' occurred in mscorlib.dll

Additional information: Parameter count mismatch.

Stacktrace:

   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Splat.WrappingFullLogger.Debug(String message, Object[] args) in z:\github\splat\Splat\Logging.cs:line 270
   at Perspex.PerspexObject.SetValue(PerspexProperty property, Object value) in d:\projects\Perspex\Perspex\PerspexObject.cs:line 412
   at Perspex.PerspexObject.SetValue[T](PerspexProperty`1 property, T value) in d:\projects\Perspex\Perspex\PerspexObject.cs:line 461
   at Perspex.Controls.TemplatedControl.set_Template(Func`2 value) in d:\projects\Perspex\Perspex\Controls\TemplatedControl.cs:line 29
   at Perspex.Controls.TemplatedControl..ctor() in d:\projects\Perspex\Perspex\Controls\TemplatedControl.cs:line 23
   at Perspex.Controls.ContentControl..ctor() in d:\projects\Perspex\Perspex\Controls\ContentControl.cs:line 18
   at Perspex.Windows.Window..ctor() in d:\projects\Perspex\Perspex.Windows\Window.cs:line 34
   at TestApplication.Program.Main(String[] args) in d:\projects\Perspex\TestApplication\Program.cs:line 48
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Using Autofac and NLog Generates an exception resolving Splat.LogHost

I have created a class derived from IMutableDependencyResolver that resolves against Autofac. It works fine without replacing the logging.

I then replaced the logging library by implementing a full logger using NLog. The log manager has the following GetLogger:

public IFullLogger GetLogger(Type type)
{
  return new NLogLogger(NLog.LogManager.GetCurrentClassLogger(type));
  //return new NLogLogger(NLog.LogManager.GetLogger(type.FullName));
}

The uncommented line using GetCurrentClassLogger throws when trying to get a logger for Splat.LogHost. Here is the relevant exception info:

 InnerException: NLog.NLogConfigurationException
         _HResult=-2146233088
         _message=Cannot access the constructor of type: Splat.LogHost. Is the required permission granted?
         HResult=-2146233088
         IsTransient=false
         Message=Cannot access the constructor of type: Splat.LogHost. Is the required permission granted?
         Source=NLog
  StackTrace:
              at NLog.Internal.FactoryHelper.CreateInstance(Type t)
              at NLog.LogFactory.GetLogger(LoggerCacheKey cacheKey)
              at NLog.LogFactory.GetLogger(String name, Type loggerType)
              at NLog.LogManager.GetCurrentClassLogger(Type loggerType)
              at MyApp.Extensions.Splat.SplatNLogManager.GetLogger(Type type) in c:\Users\david\MyApp\Extensions\Splat\SplatNLogManager.cs:line 34
              at Splat.LogHost.get_Default() in z:\code\paulcbetts\splat\Splat\Logging.cs:line 197
              at ReactiveUI.RxApp..cctor()

If I remove Autofac, and just use the NLogLogger, it works. If I disable the NLogLogger, and keep autofac, it works. If I use the commented out line which gets a logger by name instead of type, it also works. So the resolution of the Type by NLog.GetClassLogger() seems to be an issue but only when using Autofac.

David

Bitmap saving crash

Let's say I have a bitmap picture:
Splat.IBitmap picture = ....

calling the Save method ends in a cross thread violation crash:

System.IO.Stream pictureStream = new System.IO.MemoryStream();
await picture.Save(Splat.CompressedBitmapFormat.Jpeg, 1.0f, pictureStream);

also, shouldn't the Save method return the Task to be awaited for?

Make splat work with full linking on Xamarin Android

I'm experimenting with using full linking on Xamarin. Ideally splat would include LinkerOverrides similar to ReactiveUI to prevent reflection exceptions. See below:

MonoDroid] UNHANDLED EXCEPTION: System.TypeInitializationException: An exception was thrown by the type initializer for ReactiveUI.RxApp ---> System.TypeInitializationException: An exception was thrown by the type initializer for Splat.LogHost ---> System.TypeLoadException: Could not load type 'Splat.WrappingFullLogger' from assembly 'Splat, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
[MonoDroid]   --- End of inner exception stack trace ---
[MonoDroid]   at ReactiveUI.RxApp..cctor () [0x00000] in <filename unknown>:0 
[MonoDroid]   --- End of inner exception stack trace ---
[MonoDroid] at ReactiveUI.IReactiveObjectExtensions/ExtensionState`1

System.TypeInitializationException

(copied from reactiveui/Akavache#169)

I installed Akavache 4.0 via nuget, and with this code, I get the exception (WP8.1 WinRT project):

var image = new BitmapImage();
var source = ...

var sb = await BlobCache.LocalMachine.LoadImageFromUrl(source);

using (var ms = new MemoryStream())
{
    await sb.Save(CompressedBitmapFormat.Png, 1.0f, ms);
    image.SetSource(System.IO.WindowsRuntimeStreamExtensions.AsRandomAccessStream(ms));
}

Exception details:

{System.TypeInitializationException: The type initializer for 'Akavache.BlobCache' threw an exception. ---> System.IO.FileLoadException: Could not load file or assembly 'Splat, Version=1.3.3.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)
   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName)
   at System.RuntimeType.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark)
   at System.Type.GetType(String typeName, Boolean throwOnError)
   at Akavache.DependencyResolverMixin.InitializeAkavache(IMutableDependencyResolver This)
   at Akavache.BlobCache.<.cctor>b__0()
   at Splat.Locator.RegisterResolverCallbackChanged(Action callback)
   at Akavache.BlobCache..cctor()
   --- End of inner exception stack trace ---
   at Akavache.BlobCache.get_LocalMachine()
   at (my program here)

The "The located assembly's manifest definition does not match the assembly reference." part is likely the issue here. I'm using Splat in the source, and I see Splat in my project references, and I have Splat.dll in packages\Splat.1.3.3\lib\Portable-Win81+Wpa81, but when I inspect the file, it's not version 1.3.3.0, it's version 0.0.0.0:

ss

IBitmap Windows Phone 8.1 memory issues

Hi Paul,

I'm so sorry for asking you this, since I know how much you love this platform, but I was wondering how are you using IBitmap. Let me explain how I tried using it and where it goes wrong.

I began with using Akavache to load an IBitmap in my view model from the cache using an url. This works, no problem. The image loads from the cache (and again from the web) and is displayed on the screen. But when my ListView is starting to get a lot of images, it crashed due to an OutOfMemoryException.

I tried detecting a potential memory leak; the WriteableBitmaps seems not to be released. So, I tried disposing them manually when the image goes out of the visual area, but disposing the IBitmap actually means that the stream is closed, and when you scroll back so that the image is visible again, there is no image loaded. Makes sense. So, disposing the image doesn't seem to work if you need to show (reload) the image again.

I tried implementing some data virtualization so that it only loads items visible and disposed those who are not, but Windows Phone is really vague here; you need to implement some weird interface, which isn't described in detail anywhere, nor anyone has an implementation. So, that's not an option as well.

Back to the OutOfMemoryException... the IBitmap is loaded in my viewmodel. When I don't show the image on screen, but just load it, I do run out of memory. So even just loading it, and keeping it in memory, is a problem. When I load it, and then immediately dispose it, the memory is stable again.

So, should I actually load the IBitmap in my viewmodel in the first place? It seems that this is not the way to go. But if I don't, what's the use of having a xplat bitmap implementation?

Bitmap Save() on WinRT throwing up when using PNG

Hi Paul!

Was trying out Splat... while using Bitmap.Save() I got a nasty exception (COM exception giving no information whatsoever)... after debugging I figured out that the ImageQuality encoding property is not supported when using the PNG format. For a list of supported shizzle... see: https://msdn.microsoft.com/en-us/library/windows/apps/jj218354.aspx

It seems the ImageQuality is only supported when using JPG. I confirmed this when switching to saving to JPG format... it works!

If I find some spare time, I'll create a PR for this. Do you agree that the fix is to not use the ImageQualtity when PNG is selected? So introduce a variable for the list of encoding options. An if-statement to check if JPG, and if so, add the image qualtity option. Otherwise, it will just be the empty list of encoding options

portable?

Hi, I'm interested in using splat but is it portable? When I try to nuget it doesn't show up for my portable library.

Update. I see the problem, I was supporting Silverlight 5 in my portable class. Any way to make splat also work with this? For now I have unchecked SL5.

Make `DebugLogger` internal

It just took me 1 hour to realize why DebugLogger doesn't log anything (it's because it uses Debug.WriteLine which gets thrown away when compiling in release mode). Maybe the default logger should be NullLogger and DebugLogger should be internal?

Could not find a default bitmap loader.

"This should never happen, your dependency resolver is broken"

I am not using a dependency resolver. Shouldn't the library be resolving this for me.
Have i missed something?
The calling code is in a test.

Handle Proxy Network

Hi,
I tried to download the Data using Splat library.
The sample code I used,

var wc = new WebClient();
byte[] imageBytes = await wc.DownloadDataTaskAsync("http://octodex.github.com/images/Professortocat_v2.png");

However, I am getting null data in the imageBytes variable. My network is using proxy. I tried to call by including proxy information also. But no luck.
The code with proxy info:

WebClient client = new WebClient();
WebProxy wp = new WebProxy("http://192.xxx.uu.yyy:zzzz");
client.Proxy = wp;
string str = client.DownloadString("http://www.google.com"); //works well. getting data
byte[] imageBytes = await client.DownloadDataTaskAsync("http://octodex.github.com/images/Professortocat_v2.png"); //receives null

WrappingFullLogger always prefixes

WrappingFullLogger always prefixes log entries, which can be annoying if you're writing your own logger and don't want said prefixes.

Two possible solutions I can think of:

  1. Add an includePrefix optional argument to the WrappingFullLogger constructor
  2. Apply SRP and define a separate WrappingPrefixLogger which just implements ILogger. Then change DefaultLogManager so that it wraps the given ILogger inside WrappingPrefixLogger, then wraps that inside WrappingFullLogger.

Will PR both options.

Inadequate documentation

Cant find any examples on the web.

in this example on the readme

var wc = new WebClient();
var imageBytes = await wc.DownloadDataTaskAsync("http://octodex.github.com/images/Professortocat_v2.png");
ProfileImage = await BitmapLoader.Current.Load(imageBytes, null /* Use original width /, null / Use original height */);

What is the type of ProfileImage?
What type is the ToNative method on?
Presumably the type that has the ToNative method is what i need to setup as a property in my view model?
What does ToNative return?
How do i feed the result of ToNative into an android image view?

Your libraries are really good and useful but so hard to use as the examples leave out context and dont follow through. Just a thought :)

Thanks,

Harry

Can this library compress images?

As well as resizing images, is there a way to reduce the quality or change the quality setting that is used when resizing? (i.e. if it is a JPEG)

I am looking for a way to shrink the photos taken with the camera before sending to my API. I've seen several libraries that shrink photos and some that compress them, but so far haven't seen any that do both,

This library looks very full featured, but I couldn't see how to do this. Or maybe I'm missing the point here and compression is not really needed?

Cheers,
Rob

Fix Splat so that it does not depend on System.Drawing under Xam.Mac2

Xamarin does not include System.Drawing in its list of supported .Net assemblies, so projects that depend on Splat (notably also those that use RxUI) are stuck having to hand-edit .csproj files in order to point to the right version of assemblies. This is a major PITA and trolls new people.

Alpha channel in WinRT

In Splat\WinRT\Bitmaps.cs, on line 36, it states that alpha is "decoder.BitmapAlphaMode", which for my PNGs seems to be "BitmapAlphaMode.Ignore". Changing it to "BitmapAlphaMode.Premultiplied" allows my PNGs' alpha channels to work as expected.

A sample PNG is here:
imgur

โ†‘ above. Its white text, which doesn't show up on a white background.

No IsXxxEnabled properties

The IFullLogger interface lacks any IsXxxEnabled properties, such as IsDebugEnabled. This makes it more difficult for client code to selectively skip logging statements. As such, it becomes harder to skip logging in performance sensitive scenarios.

A real-world motivating example can be found in this ReactiveUI code, copied here for convenience:

this.Log().Info("Item hash: 0x{0:x}", toTrack.GetHashCode());

This code appears in the bowels of ReactiveList. Every time an item is tracked, its hash is calculated purely for the purposes of logging, even when Info-level logging has been disabled. What I'm proposing is to at least facilitate the following:

if (this.Log().IsInfoEnabled)
{
    this.Log().Info("Item hash: 0x{0:x}", toTrack.GetHashCode());
}

It's still up to client code to decide whether it is worth wrapping the logging call in an if, but at least the option would be there.

suppressLogging can be culled?

Unless I'm missing something, the LogHost.suppressLogging field is never set to true. Seems the whole concept could be culled.

Roundoff error causing RectangleF.Contains to fail

(EDITED in response to slodge pointing out a typo)

Running in a PCL under Xamarin Studio:

    public void RectangleFContainFail() {
        RectangleF big = new RectangleF(0, 0, 100, 100);
        RectangleF small = new RectangleF(50, 50, 1.0000003f, 1.0000003f);
        bool contains = big.Contains(small);
        if (contains) {
            PCLDebug.WriteLine ("contains");
        } else {
            PCLDebug.WriteLine ("Does not contain");
        }
    }

Output is "Does not contain".

Unable to add nuget package for iOS and Android PCL

I just tried to add a nuget reference to Splat from a iOS and Android PCL and received the error "Could not install package 'Splat 1.3.1'. You are trying to install this package into a project that targets 'portable-net45+sl50+MonoAndroid10+MonoTouch10', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author." I setup my PCL to just target Android and iOS and it included .Net 4.5 and Silverlight 5 by default. Would you be able to add support for this configuration?

desiredWidth and desiredHeight

I don't think the BitmapLoader Load method actually resizes images in iPhone.

In iPhone I'm looking through the source code of Bitmaps.cs for Cocoa and there is no code using the two size variables.

[Android] PlatformBitmapLoader.Load(Stream sourceStream) should throw an exception if bitmap loading fails

lines 36 and 34 of Splat/Android/Bitmaps.cs:
BitmapFactory.DecodeStream(sourceStream).FromNative()

BitmapFactory.DecodeStream will return null if loading from a stream fails. See: http://developer.android.com/reference/android/graphics/BitmapFactory.html#decodeStream(java.io.InputStream)

Since FromNative() doesn't verify that the argument is not null, this results in null pointer exceptions in user code when bitmap loading fails. Better behavior would be to cause the task returned by PlatformBitmapLoader.Load(Stream sourceStream) to fail with an IOException.

MissingMethodException

Hi,

I am writing a PCL making use of Splat System.Drawing.Color.

I am unit testing it in a .NET 4.5.1 test project. In my test method I initialize a test color so have a reference to the .NET Framework System.Drawing assembly in my test project as well as Splat 1.6.0.0 ( the .NET 4.5 version)

When my test goes into my PCL method and tries to construct a color I get the following Exception:

System.MissingMethodException Method not found: 'System.Drawing.Color System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor)'.

Have I done something wrong? or is this a bug?

DebugLogger recreated every invocation

In the ServiceLocation.InitializeSplat method is this:

This.Register(() => new DebugLogger(), typeof(ILogger));

Of course, this results in a new DebugLogger instance being created for every Log call. Not only is this potentially wasteful for performance, but it also means this outwardly intuitive attempt to set the log level does not work:

Locator.Current.GetService<ILogger>().Level = LogLevel.Warn;

Is there a reason it can't be registered as a constant:

This.RegisterConstant(new DebugLogger(), typeof(ILogger));

This freezable cannot be frozen.

    [Get("/api/users/{userid}/profileImage")]
    Task<HttpResponseMessage> GetProfileImage(string userId, [Header("Authorization")] string authorization);

    /// <summary>
    /// Gets the current user's profile image
    /// </summary>
    /// <returns></returns>
    public async Task<IBitmap> GetCurrentUsersProfileImage()
    {
        var response = await Api.GetProfileImage("current", AuthorizationHeader);
        var stream = await response.Content.ReadAsStreamAsync();
        return await BitmapLoader.Current.Load(stream, null, null);
    }

This code gives the exception:
A first chance exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll

Additional information: This Freezable cannot be frozen.

Platform details:

  • Windows 8.1 x64
  • Visual Studio Pro 2013 Update 4
  • PCL targeting .NET 4.5 and Windows 8
  • Main app is WPF targeting .NET 4.5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.