You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms中多张图片透明度批量设置问题

Solution for Batch Updating Image Opacity in Xamarin.Forms

Got it, let's fix this without having to manually reference each Image control one by one—super tedious, right? Here are two solid approaches you can use, depending on how you want to structure your code:

Approach 1: Traverse Parent Container (Quick Fix for Existing Code)

First, let's group all your image-containing StackLayouts into a single parent container (like a Grid) so we can easily loop through all Image controls.

Step 1: Update Your XAML

Wrap the five StackLayouts in a named Grid to make them easier to access:

<!-- Severity Section -->
<Label Grid.Row="0" Grid.Column="0" Text="SEVERITY" XAlign="Start" YAlign="Center" Style="{StaticResource Labelfont}" />
<Label x:Name="lbSeverity" Grid.Column="1" Text="" YAlign="Center" HorizontalOptions="StartAndExpand" Style="{StaticResource Labelfont}" />

<!-- Parent Grid for all severity images -->
<Grid x:Name="severityImagesGrid" Grid.Row="1" ColumnSpacing="4">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <StackLayout Grid.Column="0" Orientation="Horizontal" Spacing="4">
        <Image Source="severity_5.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="0.4"/>
        <StackLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSeverityTapped" NumberOfTapsRequired="1"/>
        </StackLayout.GestureRecognizers>
    </StackLayout>

    <StackLayout Grid.Column="1" Orientation="Horizontal" Spacing="4">
        <Image Source="severity_4.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="0.4"/>
        <StackLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSeverityTapped" NumberOfTapsRequired="1"/>
        </StackLayout.GestureRecognizers>
    </StackLayout>

    <StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="4">
        <Image Source="severity_3.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="1"/>
        <StackLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSeverityTapped" NumberOfTapsRequired="1"/>
        </StackLayout.GestureRecognizers>
    </StackLayout>

    <StackLayout Grid.Column="3" Orientation="Horizontal" Spacing="4">
        <Image Source="severity_2.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="0.4"/>
        <StackLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSeverityTapped" NumberOfTapsRequired="1"/>
        </StackLayout.GestureRecognizers>
    </StackLayout>

    <StackLayout Grid.Column="4" Orientation="Horizontal" Spacing="4">
        <Image Source="severity_1.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="0.4"/>
        <StackLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSeverityTapped" NumberOfTapsRequired="1"/>
        </StackLayout.GestureRecognizers>
    </StackLayout>
</Grid>

Step 2: Modify Your C# Code

Update the OnSeverityTapped method to loop through all Image controls, reset their opacity, then set the selected one to 1:

public async void OnSeverityTapped(object sender, EventArgs args)
{
    String[] buttons = new String[SeverityValues.Count];
    for (int n = 0; n < SeverityValues.Count; ++n)
    {
        buttons[n] = SeverityValues[n].Name;
    }
    var res = await this.DisplayActionSheet("Pick Severity", "Cancel", "", buttons);
    if (res != "Cancel")
    {
        var selectedSeverity = SeverityValues.FirstOrDefault(item => item.Name == res);
        if (selectedSeverity != null)
        {
            Issue.SeverityEx = selectedSeverity.Value;
            lbSeverity.Text = selectedSeverity.Name;

            // 1. Reset all images to 0.25 opacity
            foreach (var child in severityImagesGrid.Children)
            {
                if (child is StackLayout stackLayout && stackLayout.Children.FirstOrDefault() is Image img)
                {
                    img.Opacity = 0.25;
                }
            }

            // 2. Set the selected image to opacity 1
            string selectedImageSource = $"severity_{selectedSeverity.Value}.png";
            foreach (var child in severityImagesGrid.Children)
            {
                if (child is StackLayout stackLayout && stackLayout.Children.FirstOrDefault() is Image img)
                {
                    if (img.Source.ToString() == selectedImageSource)
                    {
                        img.Opacity = 1;
                        break;
                    }
                }
            }
        }
    }
}

Approach 2: MVVM with Data Binding (Cleaner, More Maintainable)

If you want a scalable solution (great for future changes), use MVVM with data binding. This eliminates manual control references entirely.

Step 1: Create a Severity Item Model

Make a model to track selection state:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class SeverityItem : INotifyPropertyChanged
{
    private string _name;
    private int _value;
    private bool _isSelected;

    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    public int Value
    {
        get => _value;
        set { _value = value; OnPropertyChanged(); }
    }

    public bool IsSelected
    {
        get => _isSelected;
        set { _isSelected = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Step 2: Add ViewModel Logic

Create a view model to handle selection:

using System.Collections.ObjectModel;
using System.Windows.Input;
using Xamarin.Forms;

public class YourPageViewModel : INotifyPropertyChanged
{
    public ObservableCollection<SeverityItem> SeverityItems { get; set; }
    public ICommand SelectSeverityCommand { get; }
    public IssueModel Issue { get; set; }
    public string SelectedSeverityName { get; set; }

    public YourPageViewModel()
    {
        SeverityItems = new ObservableCollection<SeverityItem>
        {
            new SeverityItem { Name = "Critical", Value = 5, IsSelected = false },
            new SeverityItem { Name = "High", Value = 4, IsSelected = false },
            new SeverityItem { Name = "Medium", Value = 3, IsSelected = true },
            new SeverityItem { Name = "Low", Value = 2, IsSelected = false },
            new SeverityItem { Name = "Info", Value = 1, IsSelected = false }
        };

        SelectSeverityCommand = new Command<SeverityItem>(SelectSeverity);
    }

    private void SelectSeverity(SeverityItem selectedItem)
    {
        foreach (var item in SeverityItems)
        {
            item.IsSelected = item == selectedItem;
        }

        Issue.SeverityEx = selectedItem.Value;
        SelectedSeverityName = selectedItem.Name;
        OnPropertyChanged(nameof(SelectedSeverityName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Step 3: Add Converters

Create converters to link data to UI properties:

using System;
using System.Globalization;
using Xamarin.Forms;

public class BoolToOpacityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1.0 : 0.25;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class SeverityToImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int severityValue = (int)value;
        return $"severity_{severityValue}.png";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Step 4: Update XAML for Binding

Replace your image section with an ItemsControl bound to the view model:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.YourPage">

    <ContentPage.Resources>
        <local:BoolToOpacityConverter x:Key="BoolToOpacityConverter"/>
        <local:SeverityToImageConverter x:Key="SeverityToImageConverter"/>
    </ContentPage.Resources>

    <ContentPage.BindingContext>
        <local:YourPageViewModel/>
    </ContentPage.BindingContext>

    <Grid>
        <Label Grid.Row="0" Grid.Column="0" Text="SEVERITY" XAlign="Start" YAlign="Center" Style="{StaticResource Labelfont}" />
        <Label Grid.Column="1" Text="{Binding SelectedSeverityName}" YAlign="Center" HorizontalOptions="StartAndExpand" Style="{StaticResource Labelfont}" />

        <ItemsControl Grid.Row="1" ItemsSource="{Binding SeverityItems}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackLayout Orientation="Horizontal" Spacing="4"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <Image Source="{Binding Value, Converter={StaticResource SeverityToImageConverter}}"
                               HeightRequest="40"
                               Opacity="{Binding IsSelected, Converter={StaticResource BoolToOpacityConverter}}"/>
                        <StackLayout.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type local:YourPageViewModel}}, Path=SelectSeverityCommand}"
                                                  CommandParameter="{Binding}"/>
                        </StackLayout.GestureRecognizers>
                    </StackLayout>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</ContentPage>

This approach keeps your code clean, scalable, and follows MVVM best practices—no more manual control hunting!

内容的提问来源于stack exchange,提问作者JsonDork

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:58:27