C# 更改按钮内容MVVM
我有一个包含以下内容的按钮:C# 更改按钮内容MVVM,c#,wpf,C#,Wpf,我有一个包含以下内容的按钮: <StackPanel Orientation="Horizontal"> <TextBlock Text="Connect"/> <materialDesign:PackIcon Kind="Arrow"/> </StackPanel> 我搜索并找到了这个:但我不知道当我有三个:Stackpanel、PackIcon(对象)和Textblo
<StackPanel Orientation="Horizontal">
<TextBlock Text="Connect"/>
<materialDesign:PackIcon Kind="Arrow"/>
</StackPanel>
我搜索并找到了这个:但我不知道当我有三个:Stackpanel、PackIcon(对象)和Textblock时,如何应用解决方案
我有一个progressBar,我将它显示在按钮下:
<ProgressBar x:Name="XZ" Foreground="Black" Grid.Row="4" Grid.Column="1"
Visibility="{Binding Connecting, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Value="50"
IsIndeterminate="True" />
当我点击按钮时,我想这样做,而不是现在显示ProgressBar的位置,基本上删除文本和PackIcon,并将ProgressBar放在按钮中。实际更改控件可以通过数据触发器来完成;不过在这种情况下,这似乎有点过头了 我只想切换两个控件的可见性:
<Grid>
<StackPanel Orientation="Horizontal" Visibility="{Binding Connecting, Converter={StaticResource BooleanToCollapsedConverter}}"">
<TextBlock Text="Connect"/>
<materialDesign:PackIcon Kind="Arrow"/>
</StackPanel>
<ProgressBar x:Name="XZ" Foreground="Black" Grid.Row="4" Grid.Column="1"
Visibility="{Binding Connecting, Converter={StaticResource BooleanToVisibilityConverter}}"
Value="50"
IsIndeterminate="True" />
</Grid>
您可以为按钮创建一个模板来实现这一点,然后在需要加载按钮的任何地方重用该模板,如下所示:
<Button Width="120" Height="40" Tag="False" Name="loadingButton" Click="loadingButton_Click">
<Button.Template>
<ControlTemplate>
<Border Name="PART_Border" BorderBrush="Black" BorderThickness="1" CornerRadius="2" Background="Transparent">
<Grid Name="PART_Root">
<TextBlock Name="PART_Text" HorizontalAlignment="Center" VerticalAlignment="Center">Data</TextBlock>
<ProgressBar IsIndeterminate="True" Name="PART_Loading"></ProgressBar>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="True">
<Setter TargetName="PART_Text" Property="Visibility" Value="Collapsed"></Setter>
<Setter TargetName="PART_Loading" Property="Visibility" Value="Visible"></Setter>
</Trigger>
<Trigger Property="Tag" Value="False" >
<Setter TargetName="PART_Text" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="PART_Loading" Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
请注意,如果使用MVVM模式,还可以为视图模型内的属性绑定标记属性。可以使用数据模板。比如:
XAML:
C#:
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
this.DataContext=新的MainWindowViewModel();
}
}
公共类ViewModelBase:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
[NotifyPropertyChangedInvocator]
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共类MainWindowViewModel:ViewModelBase
{
公共主窗口视图模型()
{
ButtonInfo=新建ButtonInfo(){Label=“Button Info”};
ProcessCommand=新的DelegateCommand(进程);
}
私人按钮信息(u ButtonInfo);;
公共按钮信息按钮信息
{
获取{return\u buttonInfo;}
设置
{
_按钮信息=值;
OnPropertyChanged();
}
}
公共DelegateCommand ProcessCommand{get;set;}
私有异步无效进程()
{
ButtonInfo=new ProgressInfo(){Label=“ProgressInfo”};
等待ProcessAsync();
}
专用任务ProcessAsync()
{
返回任务。运行(()=>
{
对于(int i=0;i<100;i++)
{
Application.Current.Dispatcher.Invoke(()=>
{
按钮信息进度=i;
如果(i==99)
{
ButtonInfo=新建ButtonInfo(){Label=“Button reach”};
}
});
睡眠(100);
}
});
}
}
公共类按钮信息:ViewModelBase
{
私有字符串标签;
私人互联网进展;
私有布尔处理;
公共字符串标签
{
获取{return\u label;}
设置
{
_标签=值;
OnPropertyChanged();
}
}
公共信息技术进步
{
获取{return\u progress;}
设置
{
_进步=价值;
OnPropertyChanged();
}
}
公共布尔处理
{
获取{return\u isProcessing;}
设置
{
_i处理=值;
OnPropertyChanged();
}
}
}
公共类ProgressInfo:按钮信息{}
使用数据触发器。通常最好在所有内容上放置一个低透明度面板,并在其中显示微调器/忙碌/跳动器。当你这样做的时候,集中注意力。这样,当用户点击任何可能会损坏的东西时,它会阻止用户点击任何东西,并且它完全清楚地表明用户不应该继续操作。所以你是建议我隐藏控件并显示进度条?我不知道我之前怎么没有想到这一点,我觉得自己很愚蠢。我很感激,伙计!thanks@BradleyDotNet,我可以问一下为什么你会在数据模板
@BradleyDotNet上使用触发器
,所以我读到的是,我所做的并不是不正确的,只是有点过火了?@3xGuy在我看来;是的,这似乎太过分了。扩展您的概念意味着您可以为程序中UI控件的每个可能状态创建类。很难很快管理好。我所做的或一个数据触发器似乎是正确的答案me@BradleyDotNet,谢谢,因为我在这里的回答是为了变得更好,并尝试将我的答案与其他答案进行比较。谢谢你的意见。看起来DataTrigger比滥用这样的标记要好。使用这样的模板似乎是合理的,但我认为标记不是最好的属性。可能是具有附加属性的派生用户控件?(例如,称为StatefulButton
)我通常会这样做,创建自定义控件或至少是用户控件,但如果不使用标记,则更容易理解WPFMaybe中的新成员,并将其应用于WPFMaybe isenabled将是一个好主意。如果此进程运行时未禁用此按钮,那么如果用户再次单击该按钮,会发生什么情况?通常情况下,那会是件坏事。
private async void loadingButton_Click(object sender, RoutedEventArgs e)
{
loadingButton.Tag = true.ToString();//display loading
await Task.Run(() => { Thread.Sleep(4000); });//fake data loading
loadingButton.Tag = false.ToString();//hide loading
}
<Window.Resources>
<DataTemplate DataType="{x:Type local:ButtonInfo}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Press me"></Label>
<Label Grid.Row="1" Content="{Binding Label}"></Label>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ProgressInfo}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ProgressBar Height="30" Value="{Binding Progress}"></ProgressBar>
<Label Grid.Row="1" Content="{Binding Label}"></Label>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Command="{Binding ProcessCommand}" Content="{Binding ButtonInfo}">
</Button>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
public class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel:ViewModelBase
{
public MainWindowViewModel()
{
ButtonInfo = new ButtonInfo(){Label = "Button Info"};
ProcessCommand = new DelegateCommand(Process);
}
private ButtonInfo _buttonInfo;
public ButtonInfo ButtonInfo
{
get { return _buttonInfo; }
set
{
_buttonInfo = value;
OnPropertyChanged();
}
}
public DelegateCommand ProcessCommand { get; set; }
private async void Process()
{
ButtonInfo = new ProgressInfo(){Label = "Progress Info"};
await ProcessAsync();
}
private Task ProcessAsync()
{
return Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
Application.Current.Dispatcher.Invoke(() =>
{
ButtonInfo.Progress = i;
if (i==99)
{
ButtonInfo = new ButtonInfo(){Label = "Button Again"};
}
});
Thread.Sleep(100);
}
});
}
}
public class ButtonInfo:ViewModelBase
{
private string _label;
private int _progress;
private bool _isProcessing;
public string Label
{
get { return _label; }
set
{
_label = value;
OnPropertyChanged();
}
}
public int Progress
{
get { return _progress; }
set
{
_progress = value;
OnPropertyChanged();
}
}
public bool IsProcessing
{
get { return _isProcessing; }
set
{
_isProcessing = value;
OnPropertyChanged();
}
}
}
public class ProgressInfo : ButtonInfo { }