Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何更改WPF按钮中的图像,然后运行其他方法?_C#_Wpf_Xaml - Fatal编程技术网

C# 如何更改WPF按钮中的图像,然后运行其他方法?

C# 如何更改WPF按钮中的图像,然后运行其他方法?,c#,wpf,xaml,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" W

我可以在单击按钮时更改图像,但在更改后尝试运行方法时,更改从未发生

我有两种方法来更改按钮单击事件时的图像

第一:

    <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>
第二:

    <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>
我需要使用
if()
来验证按钮的“开”或“关”状态,以更改图像并运行另一个代码

if (btnEXE.Content == FindResource("ImOFF"))
{
    btnEXE.Content = FindResource("ImON");
    ThingsToDo();
}
else
{
    btnEXE.Content = FindResource("ImOFF");
}
ThingsToDo()
运行良好,但图像的更改会一直持续到方法结束

我需要先更改图像,然后更改代码的其余部分。
有什么想法吗?

ThingsToDo
是一个长期运行的操作时,您应该通过
任务运行它,然后
等待它:

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>

即使您不打算使用完整的MVVM路径,您仍然可以使用WPF命令来处理这种UI更新

假设ThingsToDo()是一个长时间运行的任务,您应该通过ICommand实现使用异步/等待构造

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);
}
然后,可以使用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),从而防止用户多次触发命令。

谢谢!整洁。不幸的死亡。
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;
    }
}
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);
}
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));
    }
}
<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>