protected object ProduceValue(
            LocalizableProperty targetProperty,
            Type propertyType,
            ResourceManager resourceManager,
            CultureInfo culture,
            CultureInfo uiCulture,
            bool dataBound,
            object dataBoundValueOrValues
            )
        {
            if (propertyType == null)
            {
                throw new ArgumentNullException(nameof(propertyType));
            }
            if (resourceManager == null)
            {
                throw new ArgumentNullException(nameof(resourceManager));
            }
            if (culture == null)
            {
                throw new ArgumentNullException(nameof(culture));
            }
            if (uiCulture == null)
            {
                throw new ArgumentNullException(nameof(uiCulture));
            }

            /* Flow:
             * 1. Obtain value
             * 1.1. Binding -> Get binding value
             * 1.2. Multi-binding -> Get all values
             * 1.3. Resource -> Get resource value
             * 2. Convert value
             * 2.1. If Callback -> invoke (with data-bound value only since the resource value (if any) will be used as "StringFormat")
             * 2.2. If Converter -> invoke
             * 2.3. If NO Bindings AND NO Converter AND NO Callback AND NO StringFormat AND Different-Type -> Use default converter
             * 3. Format value
             * 3.1. If StringFormat -> Format
             * 3.2. If NO StringFormat AND (Bindings OR Callback) AND Resource is string -> Use resource for formatting
             */

            #region Obtain value

            object resourceValue;

            if (string.IsNullOrEmpty(this.Key))
            {
                // No resource value specified
                // Even if no data bindings are specified it is possible that a value is produced by the callback.
                // At the very least the StringFormat should be used even if "null" is passed

                /*
                 * if (false == dataBound)
                 * {
                 *  // No data bindings specified so return the default value
                 *  // return TypeUtils.GetDefaultValue(propertyType);
                 *  // Do NOT throw an exception in order to avoid design-time errors
                 *  //throw new InvalidOperationException("At least a resource or a binding must be specified.");
                 * }
                 */
                resourceValue = null;
            }
            else
            {
                if (resourceManager == null)
                {
                    // Resource manager not found - return an error message if possible
                    return(propertyType.IsAssignableFrom(typeof(string)) ? ErrorMessage_ResourceManagerNotFound
                        : targetProperty != null ? targetProperty.DefaultValue : TypeUtils.GetDefaultValue(propertyType));
                }
                else
                {
                    resourceValue = resourceManager.GetObject(this.Key, uiCulture);
                    if (resourceValue == null)
                    {
                        // Resource value not found - return an error message if possible
                        return(propertyType.IsAssignableFrom(typeof(string)) ? string.Format(ErrorMessage_ResourceKeyNotFound, Key)
                            : targetProperty != null ? targetProperty.DefaultValue : TypeUtils.GetDefaultValue(propertyType));
                    }
                }
            }

            #endregion

            if (dataBound && resourceValue != null && false == resourceValue is string)
            {
                throw new NotSupportedException("Bindings can be combined only with string resources (since the string resource is used as 'StringFormat').");
            }
            if (Callback != null && resourceValue != null && false == resourceValue is string)
            {
                throw new NotSupportedException("A callback can be combined only with string resources (since the string resource is used as 'StringFormat').");
            }

            // List of data bound values (multi-bindings only)
            var dataBoundValueList = dataBoundValueOrValues as object[];

            // Indicates if the value is multi-binding
            var isMultiBinding = dataBoundValueList?.Length > 1;

            if (isMultiBinding)
            {
                // Converter is not supported for multi-bindings

                if (Callback != null)
                {
                    throw new NotSupportedException("Callback is not supported for multi-bindings.");
                }
                if (Converter != null)
                {
                    throw new NotSupportedException("Converter is not supported for multi-bindings.");
                }

                // Format the value

                var stringFormat = StringFormat.NullIfEmpty() ?? (resourceValue as string).NullIfEmpty();

                if (stringFormat == null)
                {
                    throw new InvalidOperationException("Either 'StringFormat' or a resource must be specified when multi-binding is used.");
                }

                return(string.Format(culture, stringFormat, dataBoundValueList));
            }
            else if (dataBound)
            {
                // There is a single data-bound value
                var dataValue = dataBoundValueList == null ? dataBoundValueOrValues : dataBoundValueList[0];

                if (Callback != null)
                {
                    // Pass the data-bound value to the callback
                    dataValue = Callback(culture, uiCulture, CallbackParameter, dataValue);
                }
                if (Converter != null)
                {
                    dataValue = Converter.Convert(dataValue, propertyType, ConverterParameter, culture);
                }

                // Format the value
                var stringFormat = StringFormat.NullIfEmpty() ?? (resourceValue as string).NullIfEmpty();
                return(stringFormat == null ? dataValue : string.Format(culture, stringFormat, dataValue));
            }
            else if (Callback != null)
            {
                // The callback will produce the value
                var dataValue = Callback(culture, uiCulture, CallbackParameter, null);

                if (Converter != null)
                {
                    dataValue = Converter.Convert(dataValue, propertyType, ConverterParameter, culture);
                }

                // Format the value
                var stringFormat = StringFormat.NullIfEmpty() ?? (resourceValue as string).NullIfEmpty();
                return(stringFormat == null ? dataValue : string.Format(culture, stringFormat, dataValue));
            }
            else if (resourceValue != null)
            {
                // There is a resource value only
                var dataValue = resourceValue;

                if (Converter != null)
                {
                    dataValue = Converter.Convert(dataValue, propertyType, ConverterParameter, culture);
                }
                else if (false == propertyType.IsAssignableFrom(dataValue.GetType()))
                {
                    // Use the default converter to convert the resource value to the type of the property
                    dataValue = DefaultValueConverter.Instance.Convert(dataValue, propertyType, null, culture);
                }

                if (string.IsNullOrEmpty(StringFormat))
                {
                    return(dataValue);
                }
                else
                {
                    // Format the value
                    return(string.Format(culture, StringFormat, dataValue));
                }
            }
            else
            {
                // There is no binding, callback, or a resource value

                if (string.IsNullOrEmpty(StringFormat))
                {
                    return(targetProperty != null ? targetProperty.DefaultValue : TypeUtils.GetDefaultValue(propertyType));
                }
                else
                {
                    // Format the value
                    return(StringFormat);
                }
            }
        }