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>