In universal windows apps, how to change the backg

2019-04-10 10:58发布

问题:

Universal windows apps don't support data triggers.

Without data triggers, how can I change the background color of a button using xaml and data binding only when a boolean property in the view model changes?

For example given this XAML:

<StackPanel>
    <Button Name="ButtonA" Click="ButtonA_Click" Content="A" />
    <Button Name="ButtonB" Click="ButtonB_Click" Content="B" />
    <Button Name="ButtonC" Click="ButtonC_Click" Content="C" />
</StackPanel>

with this code behind

private void ButtonA_Click(object sender, RoutedEventArgs e)
{
    Model.IsOnA = !Model.IsOnA;
}

private void ButtonB_Click(object sender, RoutedEventArgs e)
{
    Model.IsOnB = !Model.IsOnB;
}

private void ButtonC_Click(object sender, RoutedEventArgs e)
{
    Model.IsOnC = !Model.IsOnC;
}

What is the best approach to change the background color of the buttons using data binding when the corresponding property in the view model is changed?

I was able to make it work for one button only using the VisualStateManager manager:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualState>
            <VisualState.StateTriggers>
                <StateTrigger IsActive="{x:Bind Model.IsOnA, Mode=OneWay}" />
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="ButtonA.Background" Value="Red"></Setter>
                <Setter Target="ButtonA.Foreground" Value="White"></Setter>
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

But with multiple buttons that bind to different properties in the view model this approach is not working.

回答1:

You can check my previous answer in the following link. Delete button on ListView items

You just need to create a converter which converts Boolean to SolidColorBrush. For example:

public class BooleanToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (value is bool && (bool)value) ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Green);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new Exception("Not Implemented");
    }
}

And to add it to your Xaml Binding.

<Page.Resources>
    <local:BooleanToColorConverter x:Key="ColorConverter"/>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView ItemsSource="{x:Bind Activities}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:Activity">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="3*"/>
                        <ColumnDefinition Width="1*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock x:Name="txt" Text="{x:Bind Name}" Grid.Column="0"/>
                    <Button x:Name="delItem" Click="delItem_Click" Grid.Column="1" Foreground="{x:Bind Visible, Mode=OneWay, Converter={StaticResource ColorConverter}}" Background="Transparent" Margin="100, 0, 0, 0">
                        <SymbolIcon Symbol="Delete"/>
                    </Button>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

Hope this helps.



回答2:

An alternative to converters are attached properties. Useful if more than one property should be changed or if one needs access to the view model (through the DataContext of the control) to decide how to change the user interface properties.

Here is a simple example:

public class IsFavoriteBehavior
{
    public static bool GetIsFavorite(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsFavoriteProperty);
    }

    public static void SetIsFavorite(DependencyObject obj, bool value)
    {
        obj.SetValue(IsFavoriteProperty, value);
    }

    public static readonly DependencyProperty IsFavoriteProperty =
        DependencyProperty.RegisterAttached("IsFavorite", typeof(bool), typeof(Button), new PropertyMetadata(false, (o, e) =>
        {
            var button = o as Button;

            if (button == null)
                return;

            if ((bool)e.NewValue)
            {
                button.Background = (SolidColorBrush)Application.Current.Resources["HighlightBackgroundColorBrush"];
                button.Foreground = (SolidColorBrush)Application.Current.Resources["HighlightTextColorBrush"];
            }
            else
            {
                button.Background = (SolidColorBrush)Application.Current.Resources["NormalBackgroundColorBrush"];
                button.Foreground = (SolidColorBrush)Application.Current.Resources["NormalTextColorBrush"];
            }

            o.SetValue(IsFavoriteProperty, e.NewValue);
        }));
}

It can be used like this in XAML:

<Button Name="FavoriteButton" Content="Favorite" local:IsFavoriteBehavior.IsFavorite="{x:Bind ViewModel.Favorite, Mode=OneWay}" >


回答3:

One could put a property with the background color brush directly into the view model.

For example in the view model:

SolidColorBrush IsOnABackground
{
    get
    {
        if(IsOnA)
            return (SolidColorBrush)Application.Current.Resources["HighlightBackgroundColorBrush"];
        else
            return (SolidColorBrush)Application.Current.Resources["NormalBackgroundColorBrush"];
    }
}

bool isOnA = false;
bool IsOnA
{
    set
    {
        if (isOnA != value)
        {
            isOnA = value;

            OnPropertyChanged("IsOnA");
            OnPropertyChanged("IsOnABackground");
        }
    }
    get { return isOnA; }
}

and in XAML:

<Button Name="ButtonA" Content="A" Background="{x:Bind ViewModel.IsOnABackground, Mode=OneWay}" />