Xamarin Forms:代码中修改ImageSource尺寸并处理空值显示
Got it, let's break down what's going wrong here and fix your issues step by step.
First, the core problem with your existing size-modification code: ImageSource doesn't control UI element dimensions—that's the job of the Image widget that displays the source. The WidthRequest property belongs to the Image control, not the ImageSource itself, so setting it on the source does nothing for the actual displayed size.
Let's tackle both your requirements:
1. Fix Empty/Unknown Image Display Logic
Instead of returning null when the type is unknown or invalid, return a placeholder image resource. This ensures consistent UI instead of blank space:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Clean up type checking with pattern matching if (value is TYPE type && type != TYPE.UNKNOWN) return GetTypeImageResource(type); // Return a default placeholder for unknown/invalid types return ImageSource.FromResource("Pokedex.Resources.Types.UnknownPlaceholder.png"); } private ImageSource GetTypeImageResource(TYPE type) { return ImageSource.FromResource($"Pokedex.Resources.Types.{type.ToString()}.png"); }
2. Control Image Size in Code (No XAML)
Since ImageSource can't handle sizing, here are three practical approaches depending on your needs:
Option A: Set Size Directly on the Image Control
If you're creating the Image widget in code (not XAML), just set its WidthRequest and HeightRequest properties directly after binding the source:
// Example: Creating the Image control in code var typeImage = new Image(); // Bind the source using your converter typeImage.SetBinding( Image.SourceProperty, new Binding("PokemonType", converter: new TypeToImageSourceConverter()) ); // Set fixed size (adjust values as needed) typeImage.WidthRequest = 48; typeImage.HeightRequest = 48; // Add to your layout yourLayout.Children.Add(typeImage);
Option B: Dynamic Sizing Based on Type (MultiValueConverter)
If you need different sizes for different types, use a IMultiValueConverter to return both the image source and size, then bind both properties to the result:
First, create the multi-converter:
class TypeToImageAndSizeConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length == 0 || !(values[0] is TYPE type)) return new { Source = ImageSource.FromResource("Pokedex.Resources.Types.UnknownPlaceholder.png"), Size = 48 }; if (type == TYPE.UNKNOWN) return new { Source = ImageSource.FromResource("Pokedex.Resources.Types.UnknownPlaceholder.png"), Size = 48 }; // Define size rules per type int size = type switch { TYPE.FIRE or TYPE.WATER => 60, TYPE.DRAGON => 64, _ => 48 }; return new { Source = ImageSource.FromResource($"Pokedex.Resources.Types.{type.ToString()}.png"), Size = size }; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Then use it in code to bind both source and size:
var typeImage = new Image(); var multiBinding = new MultiBinding { Converter = new TypeToImageAndSizeConverter(), Bindings = { new Binding("PokemonType") } }; // Bind source and size properties to the converter's result typeImage.SetBinding(Image.SourceProperty, new Binding(".Source", Source = multiBinding)); typeImage.SetBinding(Image.WidthRequestProperty, new Binding(".Size", Source = multiBinding)); typeImage.SetBinding(Image.HeightRequestProperty, new Binding(".Size", Source = multiBinding));
Option C: Custom Image Control (Cleanest for Reusable Code)
For a reusable solution, create a custom Image subclass that handles type-to-source and type-to-size logic internally:
public class TypeImage : Image { public static readonly BindableProperty PokemonTypeProperty = BindableProperty.Create( nameof(PokemonType), typeof(TYPE), typeof(TypeImage), propertyChanged: OnPokemonTypeChanged ); public TYPE PokemonType { get => (TYPE)GetValue(PokemonTypeProperty); set => SetValue(PokemonTypeProperty, value); } private static void OnPokemonTypeChanged(BindableObject bindable, object oldValue, object newValue) { var image = bindable as TypeImage; if (image == null || !(newValue is TYPE type)) { image.Source = ImageSource.FromResource("Pokedex.Resources.Types.UnknownPlaceholder.png"); image.WidthRequest = image.HeightRequest = 48; return; } if (type == TYPE.UNKNOWN) { image.Source = ImageSource.FromResource("Pokedex.Resources.Types.UnknownPlaceholder.png"); image.WidthRequest = image.HeightRequest = 48; return; } // Set image source image.Source = ImageSource.FromResource($"Pokedex.Resources.Types.{type.ToString()}.png"); // Set size based on type image.WidthRequest = image.HeightRequest = type switch { TYPE.FIRE or TYPE.WATER => 60, TYPE.DRAGON => 64, _ => 48 }; } }
Then use it in code like this:
var typeImage = new TypeImage(); typeImage.SetBinding(TypeImage.PokemonTypeProperty, "PokemonType"); yourLayout.Children.Add(typeImage);
Why Your Original Size Code Failed
To recap:
WidthRequestis a property of theImageUI control, not theImageSourcedata objectImageSourceonly provides the image data; it has no control over how the UI displays that data- Creating a custom
BindablePropertyonImageSourcedoesn't link to theImagecontrol's sizing logic, so the change never affects the UI
内容的提问来源于stack exchange,提问作者Faithium

