WPF ToggleButton样式,基于IsChecked触发器更改StackPanel中TextBlock的文本



我有一个WPF样式的切换按钮,它使用堆栈面板来实现堆叠的垂直文本。我希望按钮文本根据切换按钮的IsChecked状态进行更改。

此外,字符数也发生了变化,因此我需要在堆栈面板中隐藏其中一个文本块。我尝试将Letter4文本块的Visibility属性设置为隐藏,但文本没有垂直居中。

下面的代码很有效,但这只是一个很俗气的解决方法——我把字体大小改为1,这样它就好像消失了。(我去掉了所有的格式以使其更简单。(做我需要的事情的正确方法是什么?

谢谢。

<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="Letter1"/>                                                                                                                         
<TextBlock x:Name="Letter2"/>                                                                                                                          
<TextBlock x:Name="Letter3"/>                                                                                             
<TextBlock x:Name="Letter4"/>                                                                                              
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="S"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="T"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="O"/>
<Setter Property="TextBlock.Text" TargetName="Letter4" Value="P"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="R"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="U"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="N"/>
<Setter Property="TextBlock.Text" TargetName="Letter4" Value=""/>
<Setter Property="TextBlock.FontSize" TargetName="Letter4" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

</Setter.Value>
</Setter>

</Style>

IMHO,代码的最大问题是您试图在XAML中执行所有操作,而不是让视图模型中介可变值。一个较小的问题是如何实际实现垂直堆叠的文本。

关于垂直堆叠的文本,已经有另一个问题提供了很好的建议。参见Wpf TextBlock 中的垂直文本

我们可以将那里的建议结合起来,他们使用ItemsControl垂直显示文本,同时使用一个视图模型提供实际文本,以及一个隐藏的占位符ItemsControl但没有折叠(这样它仍然占用空间(,以比现在的代码中更简单地显示切换按钮。

首先,视图模型:

class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get => _isChecked;
set => _UpdateField(ref _isChecked, value, _OnIsCheckedChanged);
}
private string _buttonText;
public string ButtonText
{
get => _buttonText;
set => _UpdateField(ref _buttonText, value);
}
public ViewModel()
{
ButtonText = _GetTextForButtonState();
}
public event PropertyChangedEventHandler PropertyChanged;
private void _OnIsCheckedChanged(bool previous)
{
ButtonText = _GetTextForButtonState();
}
private string _GetTextForButtonState()
{
return IsChecked ? "STOP" : "RUN";
}
private void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

这个视图模型只提供了一个属性来接收切换按钮的状态,并为该状态提供适当的按钮文本。

接下来,XAML使用这个视图模型:

<Window x:Class="TestSO68091382ToggleVerticalText.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO68091382ToggleVerticalText"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>

<Grid>
<ToggleButton IsChecked="{Binding IsChecked}"
HorizontalAlignment="Left" VerticalAlignment="Top">
<ToggleButton.Content>
<Grid>
<ItemsControl ItemsSource="STOP" Visibility="Hidden"/>
<ItemsControl ItemsSource="{Binding ButtonText}" VerticalAlignment="Center"/>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</Grid>
</Window>

ToggleButton.IsChecked属性绑定到视图模型中的IsChecked属性,以便它可以根据需要更新文本。则ToggleButton的内容包括将垂直显示文本的ItemsControl

注意,按钮的直接派生实际上是一个Grid。这样就可以提供两个不同的ItemsControl元素:一个显示文本本身,并绑定到ButtonText属性;另一个对可能显示的两个字符串中较长的一个进行了硬编码。这样可以确保ToggleButton的大小始终相同,足够大以容纳较长的文本。绑定的CCD_ 11然后垂直居中;当然,你可以使用任何你喜欢的对齐方式,但从你的问题的措辞来看,听起来你希望文本垂直居中。


值得一提的是,如果你真的想在XAML中做所有事情,那是可能的。我个人更喜欢避免使用这种触发器,但我承认没有硬性规定说你不能。我的偏好主要与我希望XAML尽可能简单有关,因为我发现它是一种可读性较差的语言,并且更难在心理上跟踪所有不同的相关元素,添加触发器往往会使其变得更加复杂。如果你确实更喜欢只使用XAML的解决方案,它看起来像这样:

<Window x:Class="TestSO68091382ToggleVerticalText.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO68091382ToggleVerticalText"
xmlns:s="clr-namespace:System;assembly=netstandard"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<s:String x:Key="runText">RUN</s:String>
<s:String x:Key="stopText">STOP</s:String>
</Window.Resources>
<Grid>
<ToggleButton HorizontalAlignment="Left" VerticalAlignment="Top">
<ToggleButton.Content>
<Grid>
<ItemsControl ItemsSource="STOP" Visibility="Hidden"/>
<ItemsControl VerticalAlignment="Center">
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Setter Property="ItemsSource" Value="{StaticResource runText}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}" Value="True">
<Setter Property="ItemsSource" Value="{StaticResource stopText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</Grid>
</Window>

从机械上讲,这与上面基于视图模型的示例非常相似,只是使用DataTrigger来响应ToggleButton.IsChecked状态的变化,而不是在视图模型中这样做。

请注意,您实际上只需要一个触发器。您可以使用Setter来提供未检查状态的值,然后使用单个触发器来覆盖已检查状态的该值。

您需要更改可见性:

<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="Letter1"/>
<TextBlock x:Name="Letter2"/>
<TextBlock x:Name="Letter3"/>
<TextBlock x:Name="Letter4" Text="P"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="S"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="T"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="O"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="R"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="U"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="N"/>
<Setter Property="TextBlock.Visibility" TargetName="Letter4" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

但我同意@Peter Duniho的观点——你最好对垂直文本使用不同的方法。

对于永久文本,要在每行打印一个字母,在字母之间插入换行符& # xA;就足够了
示例:

<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="PART_TextBlock" Text="R&#xA;U&#xA;N"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="PART_TextBlock" Value="S&#xA;T&#xA;O&#xA;P"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

另请参阅我对垂直文本转换器的回答:https://stackoverflow.com/a/68094601/13349759

最新更新