示例#1
0
        public SettingsViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, IWindowManager windowManager, Guid accessToken, MobileApiInfo mobileApiInfo)
        {
            if (library == null)
                Throw.ArgumentNullException(() => library);

            if (viewSettings == null)
                Throw.ArgumentNullException(() => viewSettings);

            if (coreSettings == null)
                Throw.ArgumentNullException(() => coreSettings);

            if (mobileApiInfo == null)
                throw new ArgumentNullException("mobileApiInfo");

            this.library = library;
            this.viewSettings = viewSettings;
            this.coreSettings = coreSettings;
            this.windowManager = windowManager;
            this.accessToken = accessToken;

            this.canCreateAdmin = this
                .WhenAnyValue(x => x.CreationPassword, x => !string.IsNullOrWhiteSpace(x) && !this.isAdminCreated)
                .ToProperty(this, x => x.CanCreateAdmin);

            this.CreateAdminCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanCreateAdmin),
                ImmediateScheduler.Instance); // Immediate execution, because we set the password to an empty string afterwards
            this.CreateAdminCommand.Subscribe(p =>
            {
                this.library.LocalAccessControl.SetLocalPassword(this.accessToken, this.CreationPassword);
                this.isAdminCreated = true;
            });

            this.ChangeToPartyCommand = ReactiveCommand.Create(this.CreateAdminCommand.Select(x => true).StartWith(false));
            this.ChangeToPartyCommand.Subscribe(p =>
            {
                this.library.LocalAccessControl.DowngradeLocalAccess(this.accessToken);
                this.ShowSettings = false;
            });

            this.canLogin = this.WhenAnyValue(x => x.LoginPassword, x => !string.IsNullOrWhiteSpace(x))
                .ToProperty(this, x => x.CanLogin);

            this.LoginCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanLogin),
                ImmediateScheduler.Instance); // Immediate execution, because we set the password to an empty string afterwards
            this.LoginCommand.Subscribe(p =>
            {
                try
                {
                    this.library.LocalAccessControl.UpgradeLocalAccess(this.accessToken, this.LoginPassword);
                    this.IsWrongPassword = false;

                    this.ShowLogin = false;
                    this.ShowSettings = true;
                }

                catch (WrongPasswordException)
                {
                    this.IsWrongPassword = true;
                }
            });

            this.OpenLinkCommand = ReactiveCommand.Create();
            this.OpenLinkCommand.Cast<string>().Subscribe(x =>
            {
                try
                {
                    Process.Start(x);
                }

                catch (Win32Exception ex)
                {
                    this.Log().ErrorException(String.Format("Could not open link {0}", x), ex);
                }
            });

            this.ReportBugCommand = ReactiveCommand.Create();
            this.ReportBugCommand.Subscribe(p => this.windowManager.ShowWindow(new BugReportViewModel()));

            this.ChangeAccentColorCommand = ReactiveCommand.Create();
            this.ChangeAccentColorCommand.Subscribe(x => this.viewSettings.AccentColor = (string)x);

            this.ChangeAppThemeCommand = ReactiveCommand.Create();
            this.ChangeAppThemeCommand.Subscribe(x => this.viewSettings.AppTheme = (string)x);

            this.UpdateLibraryCommand = ReactiveCommand.Create(this.library.WhenAnyValue(x => x.IsUpdating, x => !x)
                .ObserveOn(RxApp.MainThreadScheduler)
                .CombineLatest(this.library.WhenAnyValue(x => x.SongSourcePath).Select(x => !String.IsNullOrEmpty(x)), (x1, x2) => x1 && x2));
            this.UpdateLibraryCommand.Subscribe(_ => this.library.UpdateNow());

            this.librarySource = this.library.WhenAnyValue(x => x.SongSourcePath)
                .ToProperty(this, x => x.LibrarySource);

            this.port = this.coreSettings.Port;
            this.ChangePortCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.Port, NetworkHelpers.IsPortValid));
            this.ChangePortCommand.Subscribe(_ => this.coreSettings.Port = this.Port);

            this.remoteControlPassword = this.coreSettings.RemoteControlPassword;
            this.ChangeRemoteControlPasswordCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.RemoteControlPassword)
                .Select(x => !String.IsNullOrWhiteSpace(x)));
            this.ChangeRemoteControlPasswordCommand.Subscribe(x =>
                this.library.RemoteAccessControl.SetRemotePassword(this.accessToken, this.RemoteControlPassword));
            this.showRemoteControlPasswordError = this.WhenAnyValue(x => x.RemoteControlPassword, x => x.LockRemoteControl,
                    (password, lockRemoteControl) => String.IsNullOrWhiteSpace(password) && lockRemoteControl)
                .ToProperty(this, x => x.ShowRemoteControlPasswordError);

            this.isRemoteAccessReallyLocked = this.library.RemoteAccessControl.WhenAnyValue(x => x.IsRemoteAccessReallyLocked)
                .ToProperty(this, x => x.IsRemoteAccessReallyLocked);

            this.isPortOccupied = mobileApiInfo.IsPortOccupied.ToProperty(this, x => x.IsPortOccupied);

            this.enableChangelog = this.viewSettings.WhenAnyValue(x => x.EnableChangelog)
                .ToProperty(this, x => x.EnableChangelog);

            this.defaultPlaybackEngine = this.coreSettings.WhenAnyValue(x => x.DefaultPlaybackEngine)
                .ToProperty(this, x => x.DefaultPlaybackEngine);
        }
        private protected DynamicMethod CreateMethodInvokerAsDynamicMethod(MethodBase methodBase, DynamicMethodOptions options)
        {
            #region Local Methods

            (string name, List <Type> parameters) GetNameAndParams(MethodBase methodOrCtor, DynamicMethodOptions o)
            {
                List <Type> parameters = new List <Type>();
                string      name;
                bool        forceMethod = (o & DynamicMethodOptions.TreatCtorAsMethod) != DynamicMethodOptions.None;

                if (methodOrCtor is ConstructorInfo && !forceMethod)
                {
                    // ReSharper disable once PossibleNullReferenceException - already checked by caller
                    name = ctorInvokerPrefix + methodOrCtor.DeclaringType.Name;
                    parameters.Add(typeof(object[])); // ctor parameters
                }
                else
                {
                    name = methodInvokerPrefix + methodOrCtor.Name;
                    parameters.Add(Reflector.ObjectType); // instance parameter

                    // not a property setter
                    if ((o & DynamicMethodOptions.TreatAsPropertySetter) == DynamicMethodOptions.None)
                    {
                        if ((o & DynamicMethodOptions.OmitParameters) == DynamicMethodOptions.None)
                        {
                            parameters.Add(typeof(object[])); // method parameters
                        }
                    }
                    // property setter
                    else
                    {
                        parameters.Add(Reflector.ObjectType); // value
                        if (ParameterTypes.Length > 0)
                        {
                            parameters.Add(typeof(object[])); // indexer parameters
                        }
                    }
                }

                return(name, parameters);
            }

            void GenerateLocalsForRefParams(MethodBase methodorCtor, ILGenerator il, DynamicMethodOptions o)
            {
                if ((o & DynamicMethodOptions.HandleByRefParameters) == DynamicMethodOptions.None)
                {
                    return;
                }

                ParameterInfo[] parameters = methodorCtor.GetParameters();
                for (int i = 0, localsIndex = 0; i < ParameterTypes.Length; i++)
                {
                    if (!ParameterTypes[i].IsByRef)
                    {
                        continue;
                    }

                    Type paramType = ParameterTypes[i].GetElementType();

                    // ReSharper disable once AssignNullToNotNullAttribute - not null because of the if above
                    il.DeclareLocal(paramType);

                    // initializing locals of ref (non-out) parameters
                    if (!parameters[i].IsOut)
                    {
                        il.Emit(methodorCtor is MethodInfo || (o & DynamicMethodOptions.TreatCtorAsMethod) != DynamicMethodOptions.None ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0); // loading parameters argument
                        il.Emit(OpCodes.Ldc_I4, i);                                                                                                                           // loading index of processed argument
                        il.Emit(OpCodes.Ldelem_Ref);                                                                                                                          // loading the pointed element in arguments
                        il.Emit(paramType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, paramType);
                        il.Emit(OpCodes.Stloc, localsIndex);                                                                                                                  // storing value in local variable
                    }

                    localsIndex++;
                }
            }

            void LoadParameters(MethodBase methodOrCtor, ILGenerator il, DynamicMethodOptions o)
            {
                for (int i = 0, localsIndex = 0; i < ParameterTypes.Length; i++)
                {
                    // ref/out parameters: from local variables
                    if (ParameterTypes[i].IsByRef)
                    {
                        il.Emit(OpCodes.Ldloca, localsIndex++); // loading address of local variable
                    }
                    // normal parameters: from object[] parameters argument
                    else
                    {
                        // loading parameters argument
                        il.Emit(methodOrCtor is ConstructorInfo && (o & DynamicMethodOptions.TreatCtorAsMethod) == DynamicMethodOptions.None
                            ? OpCodes.Ldarg_0
                            : (o & DynamicMethodOptions.TreatAsPropertySetter) == DynamicMethodOptions.None ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2);
                        il.Emit(OpCodes.Ldc_I4, i);  // loading index of processed argument
                        il.Emit(OpCodes.Ldelem_Ref); // loading the pointed element in arguments
                        il.Emit(ParameterTypes[i].IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, ParameterTypes[i]);
                    }
                }
            }

            void AssignRefParams(MethodBase methodOrCtor, ILGenerator il, DynamicMethodOptions o)
            {
                if ((options & DynamicMethodOptions.HandleByRefParameters) != DynamicMethodOptions.None)
                {
                    for (int i = 0, localsIndex = 0; i < ParameterTypes.Length; i++)
                    {
                        if (!ParameterTypes[i].IsByRef)
                        {
                            continue;
                        }
                        Type paramType = ParameterTypes[i].GetElementType();
                        il.Emit(methodOrCtor is MethodInfo || (o & DynamicMethodOptions.TreatCtorAsMethod) != DynamicMethodOptions.None ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0); // loading parameters argument
                        il.Emit(OpCodes.Ldc_I4, i);                                                                                                                           // loading index of processed argument
                        il.Emit(OpCodes.Ldloc, localsIndex++);                                                                                                                // loading local variable

                        // ReSharper disable once PossibleNullReferenceException - not null because of the if above
                        if (paramType.IsValueType)
                        {
                            il.Emit(OpCodes.Box, paramType); // boxing value type into object
                        }
                        il.Emit(OpCodes.Stelem_Ref);         // storing the variable into the pointed array index
                    }
                }
            }

            #endregion

            if (methodBase == null)
            {
                Throw.ArgumentNullException(Argument.methodBase);
            }
            Type declaringType = methodBase.DeclaringType;
            if (declaringType == null)
            {
                Throw.ArgumentException(Argument.methodBase, Res.ReflectionDeclaringTypeExpected);
            }
            MethodInfo      method = methodBase as MethodInfo;
            ConstructorInfo ctor   = methodBase as ConstructorInfo;
            if (method == null && ctor == null)
            {
                Throw.ArgumentException(Argument.methodBase, Res.ReflectionInvalidMethodBase);
            }

            bool treatCtorAsMethod = (options & DynamicMethodOptions.TreatCtorAsMethod) != DynamicMethodOptions.None;
            Type returnType        = method != null ? method.ReturnType : treatCtorAsMethod ? Reflector.VoidType : declaringType;
            Type dmReturnType      = returnType == Reflector.VoidType ? Reflector.VoidType : Reflector.ObjectType;

            (string methodName, List <Type> methodParameters) = GetNameAndParams(methodBase, options);

            DynamicMethod dm = new DynamicMethod(methodName,                 // method name
                                                 dmReturnType,               // return type
                                                 methodParameters.ToArray(), // parameters
                                                 declaringType, true);       // owner

            ILGenerator ilGenerator = dm.GetILGenerator();

            // generating local variables for ref/out parameters and initializing ref parameters
            GenerateLocalsForRefParams(methodBase, ilGenerator, options);

            // return value is the last local variable
            LocalBuilder returnValue = returnType == Reflector.VoidType ? null : ilGenerator.DeclareLocal(returnType);

            // if instance method:
            if ((method != null && !method.IsStatic) || treatCtorAsMethod)
            {
                ilGenerator.Emit(OpCodes.Ldarg_0); // loading 0th argument (instance)
                if (declaringType.IsValueType)
                {
                    // Note: this is a tricky solution that could not be made in C#:
                    // We are just unboxing the value type without storing it in a typed local variable
                    // This makes possible to preserve the modified content of a value type without using ref parameter
                    ilGenerator.Emit(OpCodes.Unbox, declaringType); // unboxing the instance

                    // If instance parameter was a ref parameter, then it should be unboxed into a local variable:
                    //LocalBuilder unboxedInstance = il.DeclareLocal(declaringType);
                    //il.Emit(OpCodes.Ldarg_0); // loading 0th argument (instance)
                    //il.Emit(OpCodes.Ldind_Ref); // as a reference - in dm instance parameter must be defined as: Reflector.ObjectType.MakeByRefType()
                    //il.Emit(OpCodes.Unbox_Any, declaringType); // unboxing the instance
                    //il.Emit(OpCodes.Stloc_0); // saving value into 0. local
                    //il.Emit(OpCodes.Ldloca_S, unboxedInstance);
                }
            }

            // loading parameters for the method call (property setter: indexer parameters)
            LoadParameters(methodBase, ilGenerator, options);

            // property value is the last parameter in a setter method
            if ((options & DynamicMethodOptions.TreatAsPropertySetter) != DynamicMethodOptions.None)
            {
                PropertyInfo pi = MemberInfo as PropertyInfo;
                if (pi == null)
                {
                    Throw.InvalidOperationException(Res.ReflectionCannotTreatPropertySetter);
                }
                ilGenerator.Emit(OpCodes.Ldarg_1); // loading value parameter (always the 1st param in setter delegate because static properties are set by expressions)
                ilGenerator.Emit(pi.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, pi.PropertyType);
            }

            if (ctor != null)
            {
                if (treatCtorAsMethod)
                {
                    // calling the constructor as method
                    ilGenerator.Emit(ctor.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, ctor);
                }
                else
                {
                    // invoking the constructor
                    ilGenerator.Emit(OpCodes.Newobj, ctor);
                }
            }
            else
            {
                // calling the method
                ilGenerator.Emit(methodBase.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method);
            }

            // If instance parameter was a ref parameter, then local variable should be boxed back:
            //il.Emit(OpCodes.Ldarg_0); // loading instance parameter
            //il.Emit(OpCodes.Ldloc_0); // loading unboxedInstance local variable
            //il.Emit(OpCodes.Box, declaringType); // boxing
            //il.Emit(OpCodes.Stind_Ref); // storing the boxed object value

            // assigning back ref/out parameters
            AssignRefParams(methodBase, ilGenerator, options);

            // setting return value
            if (returnValue != null)
            {
                ilGenerator.Emit(OpCodes.Stloc, returnValue); // storing return value to local variable

                ilGenerator.Emit(OpCodes.Ldloc, returnValue); // loading return value from its local variable
                if (returnType.IsValueType)
                {
                    ilGenerator.Emit(OpCodes.Box, returnType); // boxing if value type
                }
            }

            // returning
            ilGenerator.Emit(OpCodes.Ret);
            return(dm);
        }
示例#3
0
        /// <summary>
        /// Removes <paramref name="count"/> amount of items from the <paramref name="target"/>&#160;<see cref="IList{T}"/> at the specified <paramref name="index"/>, and
        /// inserts the specified <paramref name="collection"/> at the same position. The number of elements in <paramref name="collection"/> can be different from the amount of removed items.
        /// </summary>
        /// <typeparam name="T">The type of the elements in the collections.</typeparam>
        /// <param name="target">The target collection.</param>
        /// <param name="index">The zero-based index of the first item to remove and also the index at which <paramref name="collection"/> items should be inserted.</param>
        /// <param name="count">The number of items to remove.</param>
        /// <param name="collection">The collection to insert into the <paramref name="target"/> list.</param>
        /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="collection"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not a valid index in the <see cref="CircularList{T}"/>.</exception>
        /// <remarks>
        /// <note>If <paramref name="target"/> is neither a <see cref="List{T}"/> or an <see cref="ISupportsRangeList{T}"/> implementation,
        /// then after overwriting the elements of the overlapping range, the difference will be removed or inserted one by one.</note>
        /// </remarks>
        public static void ReplaceRange <T>(this IList <T> target, int index, int count, IEnumerable <T> collection)
        {
            if (target == null)
            {
                Throw.ArgumentNullException(Argument.target);
            }
            if (collection == null)
            {
                Throw.ArgumentNullException(Argument.collection);
            }

            switch (target)
            {
            case ISupportsRangeList <T> supportsRangeList:
                supportsRangeList.ReplaceRange(index, count, collection);
                return;

            default:
                int len = target.Count;
                if ((uint)index >= (uint)len)
                {
                    Throw.ArgumentOutOfRangeException(Argument.index);
                }
                if (count < 0)
                {
                    Throw.ArgumentOutOfRangeException(Argument.count);
                }
                if (index + count > len)
                {
                    Throw.ArgumentException(Res.IListInvalidOffsLen);
                }

                using (IEnumerator <T> enumerator = collection.GetEnumerator())
                {
                    // Copying elements while possible
                    int elementsCopied = 0;
                    while (count > 0 && enumerator.MoveNext())
                    {
                        target[index + elementsCopied] = enumerator.Current;
                        elementsCopied += 1;
                        count          -= 1;
                    }

                    // all inserted, removing the rest
                    if (count > 0)
                    {
                        target.RemoveRange(index + elementsCopied, count);
                        return;
                    }

                    // all removed (overwritten), inserting the rest
                    IList <T> rest = collection is IList <T> list ? new ListSegment <T>(list, elementsCopied) : enumerator.RestToList();
                    if (rest.Count > 0)
                    {
                        target.InsertRange(index + elementsCopied, rest);
                    }

                    return;
                }
            }
        }
示例#4
0
        /// <summary>
        /// Gets the zero-based index of the first occurrence in the specified <see cref="string"/>&#160;<paramref name="s"/> of any of the strings in the specified <paramref name="set"/> using a specific <paramref name="comparison"/>.
        /// </summary>
        /// <param name="comparison">The <see cref="StringComparison"/> to use.</param>
        /// <param name="s">A <see cref="string"/> instance that is to be compared to each element of the <paramref name="set"/>.</param>
        /// <param name="set">An <see cref="Array"/> of strings.</param>
        /// <returns>The zero-based index of the first occurrence in the specified <see cref="string"/>&#160;<paramref name="s"/> of any of the strings in the specified <paramref name="set"/>,
        /// or -1 if none of the strings of <paramref name="set"/> are found in <paramref name="s"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="s"/> is <see langword="null"/>
        /// <br/>-or-
        /// <br/><paramref name="set"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="comparison"/> is not a defined <see cref="StringComparison"/> value.</exception>
        /// <exception cref="ArgumentException"><paramref name="set"/>contains a <see langword="null"/>&#160;element.</exception>
        public static int IndexOfAny(this string s, StringComparison comparison, params string[] set)
        {
            if (s == null)
            {
                Throw.ArgumentNullException(Argument.s);
            }
            if (set == null)
            {
                Throw.ArgumentNullException(Argument.set);
            }
            if (!Enum <StringComparison> .IsDefined(comparison))
            {
                Throw.EnumArgumentOutOfRange(Argument.comparison, comparison);
            }

            int len = s.Length;

            if (len == 0)
            {
                foreach (string str in set)
                {
                    if (str == null)
                    {
                        Throw.ArgumentException(Argument.set, Res.ArgumentContainsNull);
                    }
                    if (str.Length == 0)
                    {
                        return(0);
                    }
                }

                return(-1);
            }

            var index = -1;

            for (int i = 0; i < len; i++)
            {
                foreach (string str in set)
                {
                    if (str == null)
                    {
                        Throw.ArgumentException(Argument.set, Res.ArgumentContainsNull);
                    }
                    if (str.Length == 0)
                    {
                        return(0);
                    }

                    int strLen = str.Length;
                    if (s[i] != str[0] || strLen > len - i)
                    {
                        continue;
                    }
                    if (strLen == 1 || String.Compare(s, i, str, 0, strLen, comparison) == 0)
                    {
                        return(i);
                    }
                }
            }

            return(index);
        }
示例#5
0
            internal static bool TryParse(string s, Type type, CultureInfo culture, bool tryKnownTypes, out object value, out Exception error)
            {
                if (type == null)
                {
                    Throw.ArgumentNullException(Argument.type);
                }

                error = null;
                value = null;
                if (s == null)
                {
                    if (type.CanAcceptValue(null))
                    {
                        return(true);
                    }

                    Throw.ArgumentNullException(Argument.s);
                }

                type = Nullable.GetUnderlyingType(type) ?? type;
                if (type.IsByRef)
                {
                    type = type.GetElementType();
                }

                if (culture == null)
                {
                    culture = CultureInfo.InvariantCulture;
                }

                try
                {
                    // ReSharper disable once PossibleNullReferenceException
                    if (type.IsEnum)
                    {
#if NET35 || NET40 || NET45 || NET472 || NETSTANDARD2_0
                        value = Enum.Parse(type, s);
                        return(true);
#else
                        return(Enum.TryParse(type, s, out value));
#endif
                    }

                    if (type.IsInstanceOfType(s))
                    {
                        value = s;
                        return(true);
                    }

                    if (tryKnownTypes && knownTypes.TryGetValue(type, out var tryParseMethod))
                    {
                        return(tryParseMethod.Invoke(s, culture, out value));
                    }

                    if (type.In(Reflector.Type, Reflector.RuntimeType
#if !NET35 && !NET40
                                , Reflector.TypeInfo
#endif
                                ))
                    {
                        value = Reflector.ResolveType(s);
                        return(value != null);
                    }

                    // a registered converter from string
                    switch (Reflector.StringType.GetConversions(type, true).ElementAtOrDefault(0))
                    {
                    case ConversionAttempt conversionAttempt:
                        if (conversionAttempt.Invoke(s, type, culture, out value) && type.CanAcceptValue(value))
                        {
                            return(true);
                        }
                        break;

                    case Conversion conversion:
                        value = conversion.Invoke(s, type, culture);
                        if (type.CanAcceptValue(value))
                        {
                            return(true);
                        }
                        break;
                    }

                    // Trying type converter as a fallback
                    TypeConverter converter = TypeDescriptor.GetConverter(type);
                    if (converter.CanConvertFrom(Reflector.StringType))
                    {
                        // ReSharper disable once AssignNullToNotNullAttribute - false alarm, context can be null
                        value = converter.ConvertFrom(null, culture, s);
                        return(true);
                    }

                    return(false);
                }
                catch (Exception e) when(!e.IsCritical())
                {
                    error = e;
                    value = null;
                    return(false);
                }
            }
示例#6
0
        public LocalViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, Guid accessToken)
            : base(library, coreSettings, accessToken)
        {
            if (viewSettings == null)
            {
                Throw.ArgumentNullException(() => viewSettings);
            }

            this.viewSettings = viewSettings;

            this.artistUpdateSignal = new Subject <Unit>();

            this.allArtistsViewModel = new ArtistViewModel("All Artists");
            this.allArtists          = new ReactiveList <ArtistViewModel> {
                this.allArtistsViewModel
            };

            this.Artists = this.allArtists.CreateDerivedCollection(x => x,
                                                                   x => x.IsAllArtists || this.filteredSongs.Contains(x.Name), (x, y) => x.CompareTo(y), this.artistUpdateSignal);

            // We need a default sorting order
            this.ApplyOrder(SortHelpers.GetOrderByArtist <LocalSongViewModel>, ref this.artistOrder);

            this.SelectedArtist = this.allArtistsViewModel;

            var gate = new object();

            this.Library.SongsUpdated
            .Buffer(TimeSpan.FromSeconds(1), RxApp.TaskpoolScheduler)
            .Where(x => x.Any())
            .Select(_ => Unit.Default)
            .Merge(this.WhenAny(x => x.SearchText, _ => Unit.Default)
                   .Do(_ => this.SelectedArtist = this.allArtistsViewModel))
            .Synchronize(gate)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                this.UpdateSelectableSongs();
                this.UpdateArtists();
            });

            this.WhenAnyValue(x => x.SelectedArtist)
            .Skip(1)
            .Synchronize(gate)
            .Subscribe(_ => this.UpdateSelectableSongs());

            this.playNowCommand = ReactiveCommand.CreateAsyncTask(this.Library.LocalAccessControl.ObserveAccessPermission(accessToken)
                                                                  .Select(x => x == AccessPermission.Admin || !coreSettings.LockPlayPause), _ =>
            {
                int songIndex = this.SelectableSongs.TakeWhile(x => x.Model != this.SelectedSongs.First().Model).Count();

                return(this.Library.PlayInstantlyAsync(this.SelectableSongs.Skip(songIndex).Select(x => x.Model), accessToken));
            });

            this.showAddSongsHelperMessage = this.Library.SongsUpdated
                                             .StartWith(Unit.Default)
                                             .Select(_ => this.Library.Songs.Count == 0)
                                             .TakeWhile(x => x)
                                             .Concat(Observable.Return(false))
                                             .ToProperty(this, x => x.ShowAddSongsHelperMessage);

            this.isUpdating = this.Library.WhenAnyValue(x => x.IsUpdating)
                              .ToProperty(this, x => x.IsUpdating);

            this.OpenTagEditor = ReactiveCommand.Create(this.WhenAnyValue(x => x.SelectedSongs, x => x.Any()));
        }