[ImageResizer] Fix issues with blank Width and Height controls (#37373)

* Allow custom preset's dimensions to be blank in the UI while still persisted as 0.

* XAML formatting - reorder namespaces.

* Add "(auto)" text to zero-value Width/Height in Settings. Ensure Width and Height fields in flyout are formatted to empty when their value is 0.
This commit is contained in:
Dave Rayment
2025-02-25 08:23:30 +00:00
committed by GitHub
parent 744316c400
commit a5a354a70f
11 changed files with 309 additions and 27 deletions

View File

@@ -21,6 +21,8 @@
<v:VisibilityBoolConverter x:Key="VisibilityBoolConverter" />
<v:EnumToIntConverter x:Key="EnumToIntConverter" />
<v:AccessTextToTextConverter x:Key="AccessTextToTextConverter" />
<v:NumberBoxValueConverter x:Key="NumberBoxValueConverter" />
<v:ZeroToEmptyStringNumberFormatter x:Key="ZeroToEmptyStringNumberFormatter" />
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -10,29 +10,55 @@ using System.Windows.Data;
using ImageResizer.Properties;
namespace ImageResizer.Views
namespace ImageResizer.Views;
/// <summary>
/// Converts between double and string for text-based controls bound to Width or Height fields.
/// Optionally returns localized "Auto" text when the underlying value is 0, letting the UI show,
/// for example "(auto) x 1024 pixels".
/// </summary>
[ValueConversion(typeof(double), typeof(string))]
internal class AutoDoubleConverter : IValueConverter
{
[ValueConversion(typeof(double), typeof(string))]
internal class AutoDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
/// <summary>
/// Converts a double to a string, optionally showing "Auto" for 0 values. NaN values are
/// converted to empty strings.
/// </summary>
/// <param name="value">The value to convert from <see cref="double"/> to
/// <see cref="string"/>.</param>
/// <param name="targetType">The conversion target type. <see cref="string"/> here.</param>
/// <param name="parameter">Set to "Auto" to return the localized "Auto" string if the
/// value is 0.</param>
/// <param name="culture">The <see cref="CultureInfo"/> to use for the number formatting.
/// </param>
/// <returns>The string representation of the passed-in value.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value switch
{
var d = (double)value;
double d => d switch
{
double.NaN => "0",
0 => (string)parameter == "Auto" ? Resources.Input_Auto : "0",
_ => d.ToString(culture),
},
return d != 0
? d.ToString(culture)
: (string)parameter == "Auto"
? Resources.Input_Auto
: string.Empty;
}
_ => "0",
};
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
/// <summary>
/// Converts the string representation back to a double, returning 0 if the string is empty,
/// null or not a valid number in the specified culture.
/// </summary>
/// <param name="value">The string value to convert.</param>
/// <param name="targetType">The conversion target type. <see cref="double"/> here.</param>
/// <param name="parameter">Converter parameter. Unused.</param>
/// <param name="culture">The <see cref="CultureInfo"/> to use for the text parsing.</param>
/// <returns>The corresponding double value.</returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
value switch
{
var text = (string)value;
return !string.IsNullOrEmpty(text)
? double.Parse(text, culture)
: 0;
}
}
null or "" => 0,
string text when double.TryParse(text, NumberStyles.Any, culture, out double result) => result,
_ => 0,
};
}

View File

@@ -4,7 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:ImageResizer.Models"
xmlns:p="clr-namespace:ImageResizer.Properties"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:v="clr-namespace:ImageResizer.Views">
<Grid>
<Grid.RowDefinitions>
@@ -114,8 +115,12 @@
KeyDown="Button_KeyDown"
Minimum="0"
SpinButtonPlacementMode="Inline">
<ui:NumberBox.NumberFormatter>
<v:ZeroToEmptyStringNumberFormatter />
</ui:NumberBox.NumberFormatter>
<ui:NumberBox.Value>
<Binding
Converter="{StaticResource NumberBoxValueConverter}"
ElementName="SizeComboBox"
Mode="TwoWay"
Path="SelectedValue.Width"
@@ -143,8 +148,12 @@
Minimum="0"
SpinButtonPlacementMode="Inline"
Visibility="{Binding ElementName=SizeComboBox, Path=SelectedValue.ShowHeight, Converter={StaticResource BoolValueConverter}}">
<ui:NumberBox.NumberFormatter>
<v:ZeroToEmptyStringNumberFormatter />
</ui:NumberBox.NumberFormatter>
<ui:NumberBox.Value>
<Binding
Converter="{StaticResource NumberBoxValueConverter}"
ElementName="SizeComboBox"
Mode="TwoWay"
Path="SelectedValue.Height"

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Windows.Data;
namespace ImageResizer.Views;
public class NumberBoxValueConverter : IValueConverter
{
/// <summary>
/// Converts the underlying double value to a display-friendly format. Ensures that NaN values
/// are not propagated to the UI.
/// </summary>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is double d && double.IsNaN(d) ? 0 : value;
/// <summary>
/// Converts the user input back to the underlying double value. If the input is not a valid
/// number, 0 is returned.
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
value switch
{
null => 0,
double d when double.IsNaN(d) => 0,
string str when !double.TryParse(str, out _) => 0,
_ => value,
};
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using Wpf.Ui.Controls;
namespace ImageResizer.Views;
public class ZeroToEmptyStringNumberFormatter : INumberFormatter, INumberParser
{
public string FormatDouble(double? value) => value switch
{
null => string.Empty,
0 => string.Empty,
_ => value.Value.ToString(CultureInfo.CurrentCulture),
};
public double? ParseDouble(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return 0;
}
return double.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out double result) ? result : 0;
}
public string FormatInt(int? value) => throw new NotImplementedException();
public string FormatUInt(uint? value) => throw new NotImplementedException();
public int? ParseInt(string value) => throw new NotImplementedException();
public uint? ParseUInt(string value) => throw new NotImplementedException();
}