/// <summary> /// Modifies the source data before passing it to the target for display in the UI. /// </summary> /// <param name="value">The source data being passed to the target.</param> /// <param name="targetType">The <see cref="T:System.Type" /> of data expected by the target dependency property.</param> /// <param name="parameter">An optional parameter to be used in the converter logic.</param> /// <param name="culture">The culture of the conversion.</param> /// <returns>The value to be passed to the target dependency property.</returns> public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) { CurrentCulture = culture; var returnValue = value; if (Link != null) { #if NETFX_CORE var cultureToUse = culture.Name; #else var cultureToUse = culture; #endif // Linked converter is set, this is not the last in the chain // call the linked converter, i.e. the next in the chain returnValue = Link.Convert(returnValue, OverrideType ?? targetType, parameter, cultureToUse); } if (!IsConvertable <TConvert>(value)) { Log.Warning("Cannot convert value of type '{0}', expected type '{1}', ignoring converter results", ObjectToStringHelper.ToTypeString(returnValue), typeof(TConvert)); return(ConverterHelper.UnsetValue); } returnValue = Convert((TConvert)returnValue, targetType, parameter); return(returnValue); }
/// <summary> /// Modifies the target data before passing it to the source object. /// </summary> /// <param name="value">The target data being passed to the source.</param> /// <param name="targetType">The <see cref="T:System.Type" /> of data expected by the source object.</param> /// <param name="parameter">An optional parameter to be used in the converter logic.</param> /// <param name="culture">The culture of the conversion.</param> /// <returns>The value to be passed to the source object.</returns> public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { CurrentCulture = culture; var returnValue = value; if (!IsConvertable <TConvertBack>(value)) { Log.Warning("Cannot convert back value of type '{0}', expected type '{1}', ignoring converter results", ObjectToStringHelper.ToTypeString(returnValue), typeof(TConvertBack)); returnValue = ConverterHelper.UnsetValue; } // Call ConvertBack first because we are doing this in reverse order returnValue = ConvertBack((TConvertBack)returnValue, targetType, parameter); if (Link != null) { #if NETFX_CORE var cultureToUse = culture.Name; #else var cultureToUse = culture; #endif returnValue = Link.ConvertBack(returnValue, BackOverrideType ?? targetType, parameter, cultureToUse); } return(returnValue); }
/// <summary> /// Unregisters the model of a view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <param name="model">The model.</param> /// <exception cref="ArgumentNullException">The <paramref name="viewModel"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="model"/> is <c>null</c>.</exception> public void UnregisterModel(IViewModel viewModel, object model) { Argument.IsNotNull("viewModel", viewModel); Argument.IsNotNull("model", model); var viewModelTypeName = ObjectToStringHelper.ToTypeString(viewModel); var modelTypeName = ObjectToStringHelper.ToTypeString(model); Log.Debug("Unregistering model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); var modelWasRemoved = false; lock (_viewModelModelsLock) { if (_viewModelModels.TryGetValue(viewModel.UniqueIdentifier, out var models)) { models.Remove(model); modelWasRemoved = true; } } if (modelWasRemoved) { Log.Debug("Unregistered model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); } else { Log.Debug("Model '{0}' was not registered with view model '{1}' (id = '{2}') or has already been unregistered.", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); } }
/// <summary> /// Creates a new view model. /// </summary> /// <param name="viewModelType">Type of the view model that needs to be created.</param> /// <param name="dataContext">The data context of the view model.</param> /// <param name="tag">The preferred tag to use when resolving dependencies.</param> /// <returns>The newly created <see cref="IViewModel"/> or <c>null</c> if no view model could be created.</returns> /// <exception cref="ArgumentNullException">The <paramref name="viewModelType"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">The <paramref name="viewModelType"/> does not implement the <see cref="IViewModel"/> interface.</exception> public virtual IViewModel CreateViewModel(Type viewModelType, object dataContext, object tag = null) { Argument.IsNotNull("viewModelType", viewModelType); Argument.ImplementsInterface("viewModelType", viewModelType, typeof(IViewModel)); IViewModel viewModel = null; // Only try to construct the view model when the injection object is not null, otherwise every // view model can be constructed with a nullable object. If a user wants a view model to be constructed // without any datacontext or injection, he/she should use an empty default constructor which will only // be used when injection is not possible if (dataContext != null) { viewModel = _typeFactory.CreateInstanceWithParametersAndAutoCompletionWithTag(viewModelType, tag, dataContext) as IViewModel; if (viewModel != null) { Log.Debug("Constructed view model '{0}' using injection of data context '{1}'", viewModelType.FullName, ObjectToStringHelper.ToTypeString(dataContext)); return(viewModel); } } // Try to construct view model using dependency injection viewModel = _typeFactory.CreateInstanceWithTag(viewModelType, tag) as IViewModel; if (viewModel != null) { Log.Debug("Constructed view model '{0}' using dependency injection or empty constructor", viewModelType.FullName); return(viewModel); } Log.Debug("Could not construct view model '{0}' using injection of data context '{1}'", viewModelType.FullName, ObjectToStringHelper.ToTypeString(dataContext)); return(viewModel); }
/// <summary> /// Gets the view models of a model. /// </summary> /// <param name="model">The model to find the linked view models for.</param> /// <returns>An array containing all the view models.</returns> /// <exception cref="ArgumentNullException">The <paramref name="model"/> is <c>null</c>.</exception> public IViewModel[] GetViewModelsOfModel(object model) { Argument.IsNotNull("model", model); var modelType = ObjectToStringHelper.ToTypeString(model); Log.Debug("Getting all view models that are linked to model '{0}'", modelType); var viewModels = new List <IViewModel>(); lock (_viewModelModelsLock) { foreach (var viewModelModel in _viewModelModels) { var viewModelIdentifiers = (from m in viewModelModel.Value where ObjectHelper.AreEqualReferences(m, model) select viewModelModel.Key); foreach (var viewModelIdentifier in viewModelIdentifiers) { var vm = GetViewModel(viewModelIdentifier); if (vm != null) { viewModels.Add(vm); } } } } Log.Debug("Found '{0}' view models that are linked to model '{1}'", viewModels.Count, modelType); return(viewModels.ToArray()); }
/// <summary> /// Initializes the created object after its construction. /// </summary> /// <param name="obj">The object to initialize.</param> private void InitializeAfterConstruction(object obj) { if (obj == null) { return; } string objectType = ObjectToStringHelper.ToTypeString(obj); Log.Debug("Initializing type '{0}' after construction", objectType); // TODO: Consider to cache for performance var dependencyResolverManager = DependencyResolverManager.Default; var dependencyResolver = _serviceLocator.ResolveType <IDependencyResolver>(); dependencyResolverManager.RegisterDependencyResolverForInstance(obj, dependencyResolver); Log.Debug("Injecting properties into type '{0}' after construction", objectType); var type = obj.GetType(); var typeMetaData = GetTypeMetaData(type); foreach (var injectedProperty in typeMetaData.GetInjectedProperties()) { var propertyInfo = injectedProperty.Key; var injectAttribute = injectedProperty.Value; try { var dependency = _serviceLocator.ResolveType(injectAttribute.Type, injectAttribute.Tag); propertyInfo.SetValue(obj, dependency, null); } catch (Exception ex) { string error = string.Format("Failed to set property '{0}.{1}' during property dependency injection", type.Name, propertyInfo.Name); Log.Error(ex, error); throw new InvalidOperationException(error); } } var objAsINeedCustomInitialization = obj as INeedCustomInitialization; if (objAsINeedCustomInitialization != null) { objAsINeedCustomInitialization.Initialize(); } }
/// <summary> /// Unregisters all models of a view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <exception cref="ArgumentNullException">The <paramref name="viewModel"/> is <c>null</c>.</exception> public void UnregisterAllModels(IViewModel viewModel) { Argument.IsNotNull("viewModel", viewModel); var viewModelTypeName = ObjectToStringHelper.ToTypeString(viewModel); int modelCount = 0; Log.Debug("Unregistering all models of view model '{0}' (id = '{1}')", viewModelTypeName, viewModel.UniqueIdentifier); lock (_viewModelModelsLock) { if (_viewModelModels.TryGetValue(viewModel.UniqueIdentifier, out var models)) { modelCount = models.Count; _viewModelModels.Remove(viewModel.UniqueIdentifier); } } Log.Debug("Unregistered all '{0}' models of view model '{1}' (id = '{2}')", modelCount, viewModelTypeName, viewModel.UniqueIdentifier); }
/// <summary> /// Initializes the specified view model. /// </summary> /// <param name="viewModel">The view model.</param> private void InitializeViewModel(IViewModel viewModel) { var viewModelType = ObjectToStringHelper.ToTypeString(viewModel); Log.Debug("Initializing view model '{0}'", viewModelType); UninitializeViewModel(_previousViewModel); _previousViewModel = viewModel; if (viewModel != null) { // If there are mappings, sync them in the right way var viewModelContainerType = ViewModelContainerType; foreach (var mapping in _viewToViewModelMappingContainers[viewModelContainerType].GetAllViewToViewModelMappings()) { try { if ((mapping.MappingType == ViewToViewModelMappingType.TwoWayViewWins) || (mapping.MappingType == ViewToViewModelMappingType.ViewToViewModel)) { TransferValueFromViewToViewModel(viewModel, mapping.ViewPropertyName, mapping.ViewModelPropertyName); } else if ((mapping.MappingType == ViewToViewModelMappingType.TwoWayViewModelWins) || (mapping.MappingType == ViewToViewModelMappingType.ViewModelToView)) { TransferValueFromViewModelToView(viewModel, mapping.ViewPropertyName, mapping.ViewModelPropertyName); } } catch (Exception ex) { Log.Error(ex, "Failed to transfer value from view property '{0}' to the view model property '{1}' for the ViewToViewModelMapping", mapping.ViewPropertyName, mapping.ViewModelPropertyName); } } viewModel.PropertyChanged += OnViewModelPropertyChanged; } Log.Debug("Initialized view model '{0}'", viewModelType); }
/// <summary> /// Closes the document in the main shell with the specified view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <param name="tag">The tag.</param> public void CloseDocument(IViewModel viewModel, object tag = null) { Argument.IsNotNull(() => viewModel); Log.Debug("Closing document for view model '{0}'", viewModel.UniqueIdentifier); var viewType = GetViewType(viewModel); var document = AvalonDockHelper.FindDocument(viewType, tag); if (document == null) { Log.Warning("Cannot find document belonging to view model '{0}' with id '{1}' thus cannot close the document", ObjectToStringHelper.ToTypeString(viewModel), viewModel.UniqueIdentifier); } else { AvalonDockHelper.CloseDocument(document); } Log.Debug("Closed document for view model '{0}'", viewModel.UniqueIdentifier); }
/// <summary> /// Generic cast of a value. /// </summary> /// <typeparam name = "TOutput">Requested return type.</typeparam> /// <param name = "value">The value to cast.</param> /// <returns>The casted value.</returns> public static TOutput Cast <TOutput>(object value) { TOutput output = default(TOutput); if (!TryCast(value, out output)) { var tI = ObjectToStringHelper.ToTypeString(value.GetType()); string tO = typeof(TOutput).FullName; string vl = string.Concat(value); string msg = "Failed to cast from '{0}' to '{1}'"; if (!tI.Equals(vl)) { msg = string.Concat(msg, " for value '{2}'"); } msg = string.Format(msg, tI, tO, vl); throw new InvalidCastException(msg); } return(output); }
/// <summary> /// Unregisters all models of a view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <exception cref="ArgumentNullException">The <paramref name="viewModel"/> is <c>null</c>.</exception> public void UnregisterAllModels(IViewModel viewModel) { Argument.IsNotNull("viewModel", viewModel); var viewModelTypeName = ObjectToStringHelper.ToTypeString(viewModel); int modelCount = 0; Log.Debug("Unregistering all models of view model '{0}' (id = '{1}')", viewModelTypeName, viewModel.UniqueIdentifier); lock (_viewModelModelsLock) { if (!_viewModelModels.ContainsKey(viewModel.UniqueIdentifier)) { _viewModelModels[viewModel.UniqueIdentifier] = new List <object>(); } var models = _viewModelModels[viewModel.UniqueIdentifier]; modelCount = models.Count; models.Clear(); } Log.Debug("Unregistered all '{0}' models of view model '{1}' (id = '{2}')", modelCount, viewModelTypeName, viewModel.UniqueIdentifier); }
/// <summary> /// Registers the model of a view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <param name="model">The model.</param> /// <exception cref="ArgumentNullException">The <paramref name="viewModel"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="model"/> is <c>null</c>.</exception> public void RegisterModel(IViewModel viewModel, object model) { Argument.IsNotNull("viewModel", viewModel); Argument.IsNotNull("model", model); var viewModelTypeName = ObjectToStringHelper.ToTypeString(viewModel); var modelTypeName = ObjectToStringHelper.ToTypeString(model); Log.Debug("Registering model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); lock (_viewModelModelsLock) { if (!_viewModelModels.TryGetValue(viewModel.UniqueIdentifier, out var models)) { models = new List <object>(); _viewModelModels[viewModel.UniqueIdentifier] = models; } models.Add(model); } Log.Debug("Registered model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); }
/// <summary> /// Unregisters the model of a view model. /// </summary> /// <param name="viewModel">The view model.</param> /// <param name="model">The model.</param> /// <exception cref="ArgumentNullException">The <paramref name="viewModel"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="model"/> is <c>null</c>.</exception> public void UnregisterModel(IViewModel viewModel, object model) { Argument.IsNotNull("viewModel", viewModel); Argument.IsNotNull("model", model); var viewModelTypeName = ObjectToStringHelper.ToTypeString(viewModel); var modelTypeName = ObjectToStringHelper.ToTypeString(model); Log.Debug("Unregistering model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); lock (_viewModelModelsLock) { if (!_viewModelModels.ContainsKey(viewModel.UniqueIdentifier)) { _viewModelModels[viewModel.UniqueIdentifier] = new List <object>(); } var models = _viewModelModels[viewModel.UniqueIdentifier]; models.Remove(model); } Log.Debug("Unregistered model '{0}' with view model '{1}' (id = '{2}')", modelTypeName, viewModelTypeName, viewModel.UniqueIdentifier); }
/// <summary> /// Called when the <c>DataContext</c> property of the <see cref="TargetView" /> has changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> public virtual void OnTargetViewDataContextChanged(object sender, DataContextChangedEventArgs e) { if (IsCurrentDataContext(e)) { return; } Log.Debug($"DataContext of TargetView '{TargetViewType?.Name}' has changed to '{ObjectToStringHelper.ToTypeString(TargetView.DataContext)}'"); LastKnownDataContext = null; var dataContext = TargetView.DataContext; if (ReferenceEquals(dataContext, null)) { return; } if (dataContext.IsSentinelBindingObject()) { return; } // Here we have a data context that makes sense LastKnownDataContext = new WeakReference(dataContext); if (ReferenceEquals(ViewModel, dataContext)) { return; } // Check if the VM is compatible if (_viewModelLocator.IsCompatible(TargetViewType, dataContext.GetType())) { // Use the view model from the data context, probably set manually ViewModel = (IViewModel)dataContext; } }
/// <summary> /// Called when the <c>DataContext</c> property of the <see cref="TargetView" /> has changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> public virtual void OnTargetViewDataContextChanged(object sender, DataContextChangedEventArgs e) { if (e.AreEqual) { return; } Log.Debug("DataContext of TargetView '{0}' has changed to '{1}'", TargetView.GetType().Name, ObjectToStringHelper.ToTypeString(TargetView.DataContext)); var dataContext = TargetView.DataContext; if (dataContext == null) { return; } if (dataContext.IsSentinelBindingObject()) { return; } if (ViewModel == dataContext) { return; } if (dataContext.GetType().IsAssignableFromEx(ViewModelType)) { // Use the view model from the data context, probably set manually ViewModel = (IViewModel)dataContext; } }
public void ReturnsTypeNameForInt() { Assert.AreEqual("Int32", ObjectToStringHelper.ToTypeString(42)); }
public void ReturnsDbNullStringForDbNullInstance() { Assert.AreEqual("DBNull", ObjectToStringHelper.ToTypeString(DBNull.Value)); }
public void ReturnsNullStringForNullInstance() { Assert.AreEqual("null", ObjectToStringHelper.ToTypeString(null)); }
/// <summary> /// Gets the view model by its unique identifier. /// </summary> /// <param name="uniqueIdentifier">The unique identifier.</param> /// <returns>The <see cref="IViewModel"/> or <c>null</c> if the view model is not registered.</returns> public IViewModel GetViewModel(int uniqueIdentifier) { Log.Debug("Searching for the instance of view model with unique identifier '{0}'", uniqueIdentifier); lock (_managedViewModelsLock) { foreach (var managedViewModel in _managedViewModels) { foreach (var viewModel in managedViewModel.Value.ViewModels) { if (viewModel.UniqueIdentifier == uniqueIdentifier) { Log.Debug("Found the instance of view model with unique identifier '{0}' as type '{1}'", uniqueIdentifier, ObjectToStringHelper.ToTypeString(viewModel)); return(viewModel); } } } } Log.Debug("Did not find the instance of view model with unique identifier '{0}'. It is either not registered or not alive.", uniqueIdentifier); return(null); }
/// <summary> /// Constructs the view with the view model. First, this method tries to inject the specified DataContext into the /// view. If the view does not contain a constructor with this parameter type, it will try to use the default constructor /// and set the DataContext manually. /// </summary> /// <param name="viewType">Type of the view to instantiate.</param> /// <param name="dataContext">The data context to inject into the view. In most cases, this will be a view model.</param> /// <returns>The constructed view or <c>null</c> if it was not possible to construct the view.</returns> /// <exception cref="ArgumentNullException">The <paramref name="viewType"/> is <c>null</c>.</exception> public static FrameworkElement ConstructViewWithViewModel(Type viewType, object dataContext) { Argument.IsNotNull("viewType", viewType); Log.Debug("Constructing view for view type '{0}'", viewType.Name); FrameworkElement view; // First, try to constructor directly with the data context if (dataContext != null) { var injectionConstructor = viewType.GetConstructorEx(new[] { dataContext.GetType() }); if (injectionConstructor != null) { view = (FrameworkElement)injectionConstructor.Invoke(new[] { dataContext }); Log.Debug("Constructed view using injection constructor"); return(view); } } Log.Debug("No constructor with data (of type '{0}') injection found, trying default constructor", ObjectToStringHelper.ToTypeString(dataContext)); // Try default constructor var defaultConstructor = viewType.GetConstructorEx(new Type[0]); if (defaultConstructor == null) { Log.Error("View '{0}' does not have an injection or default constructor thus cannot be constructed", viewType.Name); return(null); } try { view = (FrameworkElement)defaultConstructor.Invoke(null); } catch (Exception ex) { string error = string.Format("Failed to construct view '{0}' with both injection and empty constructor", viewType.Name); Log.Error(ex, error); throw new InvalidOperationException(error, ex); } view.DataContext = dataContext; Log.Debug("Constructed view using default constructor and setting DataContext afterwards"); return(view); }
/// <summary> /// Called when the <c>DataContext</c> property of the <see cref="TargetControl"/> has changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> public virtual void OnTargetControlDataContextChanged(object sender, DependencyPropertyValueChangedEventArgs e) { Log.Debug("DataContext of TargetControl '{0}' has changed to '{1}'", TargetControl.GetType().Name, ObjectToStringHelper.ToTypeString(TargetControl.DataContext)); var dataContext = TargetControl.DataContext; if (dataContext == null) { return; } if (BindingHelper.IsSentinelObject(dataContext)) { return; } if (ViewModel == dataContext) { return; } if (dataContext.GetType().IsAssignableFromEx(ViewModelType)) { // Use the view model from the data context, probably set manually ViewModel = (IViewModel)dataContext; } }