如何在WPF按钮中更改图像然后运行其他方法?

时间:2019-07-16 22:13:50

标签: c# wpf xaml

单击按钮时可以更改图像,但是当尝试在更改后运行方法时,更改永远不会发生。

我有两种方法可以在按钮的单击事件上更改图像

第一:

    <Window.Resources>
        <Image x:Key="ImON" Source="InterruptorON.png" Height="315" Width="435" />
        <Image x:Key="ImOFF" Source="InterruptorOFF.png" Height="315" Width="435" />
    </Window.Resources>
    <Grid>
        <Button Name="btnEXE" Height="315" Width="435" Click="Button_Click">
            <DynamicResource ResourceKey="ImOFF"/>
        </Button>
    </Grid>
btnEXE.Content = FindResource("ImON");

第二:

    <Window.Resources>
        <ControlTemplate x:Key="ImOFF" TargetType="Button">
            <Image Source="InterruptorOFF.png" Height="315" Width="435" />
        </ControlTemplate>
        <ControlTemplate x:Key="ImON" TargetType="Button">
            <Image Source="InterruptorON.png" Height="315" Width="435" />
        </ControlTemplate>
    </Window.Resources>
    <Grid>
        <Button Name="btnEXE" Height="315" Width="435" Click="Button_Click" Template="{StaticResource ImOFF}"/>
    </Grid>
btnEXE.Template = (ControlTemplate)FindResource("ImON")

我需要使用if()来验证按钮的“ ON”或“ OFF”状态以更改图像并运行其他代码

if (btnEXE.Content == FindResource("ImOFF"))
{
    btnEXE.Content = FindResource("ImON");
    ThingsToDo();
}
else
{
    btnEXE.Content = FindResource("ImOFF");
}

ThingsToDo()运行完美,但是图像的更改一直发生到方法结束为止。

我需要先更改图像,然后再更改其余代码。 有想法吗?

2 个答案:

答案 0 :(得分:1)

如果ThingsToDo是长期运行的操作,则应通过Taskawait运行它:

await Task.Run(() => ThingsToDo());

我还建议使用BitmapImage资源,而不是使用重量较大的Image元素:

<Window.Resources>
    <BitmapImage x:Key="ImON" UriSource="InterruptorON.png"/>
    <BitmapImage x:Key="ImOFF" UriSource="InterruptorOFF.png"/>
</Window.Resources>
<Grid>
    <Button Height="315" Width="435" Click="Button_Click">
        <Image Source="{StaticResource ImOFF}" Height="315" Width="435"/>
    </Button>
</Grid>

并在异步事件处理程序中使用它们。此外,请确保只要该操作在运行,就禁用按钮。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var button = (Button)sender;
    var image = (Image)button.Content;
    var imOff = (ImageSource)FindResource("ImOFF");

    if (image.Source == imOff)
    {
        image.Source = (ImageSource)FindResource("ImON");
        button.IsEnabled = false;

        await Task.Run(() => ThingsToDo());

        button.IsEnabled = true;
    }
    else
    {
        image.Source = imOff;
    }
}

答案 1 :(得分:0)

即使您不打算走完整的MVVM路线,仍然可以使用WPF命令来处理此类UI更新。

假设ThingsToDo()是一个长期运行的任务,则应通过ICommand实现使用async / await构造。

public class RelayCommandAsync : ObservableObject, ICommand
{
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;

    public RelayCommandAsync(Func<Task> execute) : this(execute, () => true)
    {
    }

    public RelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    private bool _isExecuting;

    public bool IsExecuting
    {
        get => _isExecuting;
        set
        {
            if (Set(nameof(IsExecuting), ref _isExecuting, value))
                RaiseCanExecuteChanged();
        }
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter) => !IsExecuting && _canExecute();

    public async void Execute(object parameter)
    {
        if (!CanExecute(parameter))
            return;

        IsExecuting = true;
        try
        {
            await _execute().ConfigureAwait(true);
        }
        finally
        {
            IsExecuting = false;
        }
    }

    public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

ObservableObject来自MVVMLight框架,但是您可以将其替换为任何INotifyPropertyChanged实现。

然后可以在窗口后面的代码中分配它

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        btnExe.Command = new RelayCommandAsync(ThingsToDo);
    }

    private async Task ThingsToDo()
    {
        // simulate a long running operation
        await Task.Delay(TimeSpan.FromSeconds(3));
    }
}

然后,您可以使用Windows的xaml文件中的样式定义按钮的外观,其中包括绑定到用于更新图像的命令的IsExecuting属性的触发器。

<Button x:Name="btnExe">
    <Button.Resources>
        <Image x:Key="ImgOff" Source="InterruptorOFF.png" />
        <Image x:Key="ImgOn" Source="InterruptorON.png" />
    <Button.Resources>

    <Button.Style>
        <Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
            <Setter Property="Content" Value="{StaticResource ImgOff}" />

            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Command.IsExecuting}" Value="True">
                    <Setter Property="Content" Value="{StaticResource ImgOn}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

使用此构造可带来额外的好处,即在执行命令时禁用按钮(由于Command.CanExecute),从而防止用户多次触发命令。