将ICommand的CanExecute绑定到FontIcon的前景颜色。

huangapple go评论68阅读模式
英文:

Bind CanExecute of ICommand to Foreground Color of FontIcon

问题

问题

我有一个名为 Button 的按钮,其命令为 ToggleMode,按钮内部包含一个 FontIcon 和一个 TextBlock

在 Styles.xaml 中,当按钮启用时,我将 Button 的前景色设置为白色,当按钮禁用时设置为浅灰色。这导致当 Button 被禁用时,Textblocks 的前景色正确地改变,因为 ToggleMode.CanExecute() 返回 false。然而,FontIcons 的正常颜色是浅蓝色,我是使用 Foreground 属性设置的。当按钮被禁用时,颜色应该更改为淡蓝色,以提高按钮被禁用的印象。

代码示例

布尔转换器

    <local:BooleanConverter x:Key="CanExecuteToIconColorConverter">
        <local:BooleanConverter.True>
            <SolidColorBrush Color="{ThemeResource VividSkyBlueColor}" />
        </local:BooleanConverter.True>
        <local:BooleanConverter.False>
            <SolidColorBrush Color="{ThemeResource PaleVividSkyBlueColor}" />
        </local:BooleanConverter.False>
    </local:BooleanConverter>

按钮

    <Button Grid.Row="0" Width="150" Padding="10"
            Command="{x:Bind ToggleMode, Mode=OneWay}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>
            <FontIcon FontSize="20" Grid.Column="0"
                      Glyph="{StaticResource mdi_toggle_off}"
                      FontFamily="{StaticResource MaterialDesignIconsOutlined}"
                      Foreground="{ThemeResource VividSkyBlueColor}"/>
            <TextBlock Grid.Column="1" Margin="10,0,0,0" Text="Toggle"/>
        </Grid>
    </Button>

我尝试过的

我尝试使用这里提供的通用 Boolean 转换器: https://stackoverflow.com/questions/534575/how-do-i-invert-booleantovisibilityconverter/5182660#5182660。我将浅蓝色设置为 True 值,淡蓝色设置为 False 值,并将我的 FontIconForeground 属性绑定到命令,但这不起作用。

之后,我尝试订阅命令的 CanExecuteChanged 事件,将新值保存在私有字段中,并绑定到它,但这也不起作用,因为在初始化视图时,该命令仍然为空。

我的下一个猜测是订阅命令的属性更改事件,当此事件触发时,命令不再应该为空,我应该能够订阅事件,但这似乎会生成大量冗余代码,特别是如果我有多个需要执行此操作的命令。

有什么更简单的方法可以实现这一点?

编辑1 我如何尝试将命令绑定到 FontIcon

<FontIcon FontSize="20" Grid.Column="0"
          Glyph="{StaticResource mdi_toggle_off}"
          FontFamily="{StaticResource MaterialDesignIconsOutlined}"
          Foreground="{x:Bind ToggleCommand, Mode=OneWay, Converter={StaticResource CanExecuteToIconColorConverter}}"/>

对我有用的方法

根据 @AndrewKeepCoding 的答案,我发现只需将绑定到按钮的 IsEnabled 属性,而不是直接绑定到命令:

<Button Grid.Row="0" Width="150" Padding="10"
        Command="{x:Bind ToggleMode, Mode=OneWay}"
        x:Name="ToggleModeButton">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <FontIcon FontSize="20" Grid.Column="0"
                  Glyph="{StaticResource mdi_toggle_off}"
                  FontFamily="{StaticResource MaterialDesignIconsOutlined}"
                  Foreground="{x:Bind ToggleModeButton.IsEnabled, Mode=OneWay, Converter={StaticResource CanExecuteToIconColorConverter}}"/>
        <TextBlock Grid.Column="1" Margin="10,0,0,0" Text="Toggle"/>
    </Grid>
</Button>
英文:

Problem

I have an Button whose command is ToggleMode, Inside this button is a FontIcon and a TextBlock.

In Styles.xaml I set the foreground color of a Button to white when the Button is enabled and to a light gray if the button is disabled. This leads to the Textblocks foreground color correctly changing when the Button is disabled because the ToggleMode.CanExecute() returns false.
However the FontIcons normal color is a light blue, which I set using the Foreground property.
When the Button is disabled the color should be changed to a pale blue improving the impression of the button being disabled.

Code example

Boolean converter

    <local:BooleanConverter x:Key="CanExecuteToIconColorConverter">
        <local:BooleanConverter.True>
            <SolidColorBrush Color="{ThemeResource VividSkyBlueColor}" />
        </local:BooleanConverter.True>
        <local:BooleanConverter.False>
            <SolidColorBrush Color="{ThemeResource PaleVividSkyBlueColor}" />
        </local:BooleanConverter.False>
    </local:BooleanConverter>

Button

    <Button Grid.Row="0" Width="150" Padding="10"
            Command="{x:Bind ToggleMode, Mode=OneWay}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="3*"/>
            </Grid.ColumnDefinitions>
            <FontIcon FontSize="20" Grid.Column="0"
                      Glyph="{StaticResource mdi_toggle_off}"
                      FontFamily="{StaticResource MaterialDesignIconsOutlined}"
                      Foreground="{ThemeResource VividSkyBlueColor}"/>
            <TextBlock Grid.Column="1" Margin="10,0,0,0" Text="Toggle"/>
        </Grid>
    </Button>

What I tried

I tried using the generic BooleanConverter given here: https://stackoverflow.com/questions/534575/how-do-i-invert-booleantovisibilityconverter/5182660#5182660
Setting a SolidColorBrush with the light blue color as True Value and the pale blue as the False Value and Binding the Foreground property of my FontIcon to the command but this does not work.

After that I tried to subscribe to the CanExecuteChanged Event of the Command, saving the new value in a private field and binding to that but this does not work as well because at the time of initializing my view this command is still null.

My next guess would be to subscribe to the property changed event of the command when this is fired the command should'nt be null anymore and I should be able to subscribe to the Event but this seems like a lot boiler code especially if I have various commands I need to do this to.

How can I achieve this easier?

Edit1 How I tried to bind the command to theFontIcon

<FontIcon FontSize="20" Grid.Column="0"
          Glyph="{StaticResource mdi_toggle_off}"
          FontFamily="{StaticResource MaterialDesignIconsOutlined}"
          Foreground="{x:Bind ToggleCommand, Mode=OneWay, Converter={StaticResource CanExecuteToIconColorConverter}}"/>

What worked for me

Following the anser by @AndrewKeepCoding I found out that just binding to the IsEnabled property of the button instead of binding to the command itself:

<Button Grid.Row="0" Width="150" Padding="10"
        Command="{x:Bind ToggleMode, Mode=OneWay}"
        x:Name="ToggleModeButton">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <FontIcon FontSize="20" Grid.Column="0"
                  Glyph="{StaticResource mdi_toggle_off}"
                  FontFamily="{StaticResource MaterialDesignIconsOutlined}"
                  Foreground="{x:Bind ToggleModeButton.IsEnabled, Mode=OneWay, Converter={StaticResource CanExecuteToIconColorConverter}}"/>
        <TextBlock Grid.Column="1" Margin="10,0,0,0" Text="Toggle"/>
    </Grid>
</Button>

答案1

得分: 1

以下是您要翻译的代码的部分:

BooleanToBrushConverter.cs

使用ValueConverter来实现另一种方法

namespace FontIconExample
{
    public class BooleanToBrushConverter : DependencyObject, IValueConverter
    {
        public static readonly DependencyProperty TrueBrushProperty =
            DependencyProperty.Register(
                nameof(TrueBrush),
                typeof(Brush),
                typeof(BooleanToBrushConverter),
                new PropertyMetadata(default));

        public static readonly DependencyProperty FalseBrushProperty =
            DependencyProperty.Register(
                nameof(FalseBrush),
                typeof(Brush),
                typeof(BooleanToBrushConverter),
                new PropertyMetadata(default));

        public Brush TrueBrush
        {
            get => (Brush)GetValue(TrueBrushProperty);
            set => SetValue(TrueBrushProperty, value);
        }

        public Brush FalseBrush
        {
            get => (Brush)GetValue(FalseBrushProperty);
            set => SetValue(FalseBrushProperty, value);
        }

        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return (bool)value is true
                ? TrueBrush
                : FalseBrush;
        }

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

MainPageViewModel.cs

使用CommunityToolkit.Mvvm库

namespace FontIconExample
{
    public partial class ViewModel : ObservableObject
    {
        [RelayCommand(CanExecute = nameof(CanToggleMode))]
        private void ToggleMode()
        {
            CanToggleMode = false;
        }

        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(ToggleModeCommand))]
        private bool canToggleMode = true;
    }
}

MainPage.xaml

XAML部分代码,包括了布局和按钮的定义,以及使用了BooleanToBrushConverter来设置按钮的前景色。
英文:

Let me show you another way to achieve this using a ValueConverter.

BooleanToBrushConverter.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
using System;

namespace FontIconExample;

public class BooleanToBrushConverter : DependencyObject, IValueConverter
{
    public static readonly DependencyProperty TrueBrushProperty =
        DependencyProperty.Register(
            nameof(TrueBrush),
            typeof(Brush),
            typeof(BooleanToBrushConverter),
            new PropertyMetadata(default));

    public static readonly DependencyProperty FalseBrushProperty =
        DependencyProperty.Register(
            nameof(FalseBrush),
            typeof(Brush),
            typeof(BooleanToBrushConverter),
            new PropertyMetadata(default));

    public Brush TrueBrush
    {
        get => (Brush)GetValue(TrueBrushProperty);
        set => SetValue(TrueBrushProperty, value);
    }

    public Brush FalseBrush
    {
        get => (Brush)GetValue(FalseBrushProperty);
        set => SetValue(FalseBrushProperty, value);
    }

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (bool)value is true
            ? TrueBrush
            : FalseBrush;
    }

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

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace FontIconExample;

public partial class ViewModel : ObservableObject
{
    [RelayCommand(CanExecute = nameof(CanToggleMode))]
    private void ToggleMode()
    {
        CanToggleMode = false;
    }

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(ToggleModeCommand))]
    private bool canToggleMode = true;
}

MainPage.xaml

<Page
    x:Class="FontIconExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:FontIconExample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">
    <Page.DataContext>
        <local:ViewModel x:Name="ViewModel" />
    </Page.DataContext>
    <Page.Resources>
        <local:BooleanToBrushConverter
            x:Key="BooleanToBrushConverter"
            FalseBrush="LightGreen"
            TrueBrush="SkyBlue" />
    </Page.Resources>

    <Grid>
        <Button
            x:Name="ToggleModeButton"
            Grid.Row="0"
            Width="150"
            Padding="10"
            Command="{x:Bind ViewModel.ToggleModeCommand, Mode=OneWay}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="3*" />
                </Grid.ColumnDefinitions>
                <FontIcon
                    Grid.Column="0"
                    FontFamily="Segoe MDL2 Assets"
                    FontSize="20"
                    Foreground="{x:Bind ToggleModeButton.IsEnabled, Mode=OneWay, Converter={StaticResource BooleanToBrushConverter}}"
                    Glyph="" />
                <TextBlock
                    Grid.Column="1"
                    Margin="10,0,0,0"
                    Text="Toggle" />
            </Grid>
        </Button>
    </Grid>
</Page>

答案2

得分: 0

在我的经验中,FontIcon 有点不稳定,因为在某些方面它的行为不如预期。也许修改FontIcon的颜色可以适应您的特定情况(如果您可以让它起作用),但在更复杂的情况下,您可能需要一个复杂的控件,它必须看起来被禁用,那么这种方法就行不通。

在这些情况下,我更喜欢不修改单个元素的颜色,而是执行以下之一:

  • 修改整个控件的透明度
  • 在控件上添加另一个控件,几乎是透明的但不完全透明的,以淡化底层控件的颜色。

当某个深蓝色稍微透明时(或者被一个几乎透明但不完全透明的白色覆盖时),它将呈现为浅蓝色,而深橙色也将呈现为浅橙色,依此类推。

英文:

In my experience FontIcon is a bit flaky because in some ways it does not behave as expected. Modifying the color of the FontIcon could perhaps suit your particular situation, (if you could get it to work,) but in more complicated situations where you might have a complex control that must look disabled, it won't work.

What I prefer to do in these cases is to refrain from modifying the colors of individual elements and instead do one of the following:

  • modify the transparency of the entire control
  • add another control on top of the control, which is almost transparent but not entirely transparent, to wash out the colors of the underlying control.

When a certain deep blue is made slightly transparent, (or when it is overlaid by a white but almost transparent shade,) it will appear as light blue, and also a deep orange will appear as light orange, etc.

huangapple
  • 本文由 发表于 2023年4月13日 16:43:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003414.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定