1.问题需求
有一个界面,界面上有一组单选按钮(一个是“可选”,另一个是“必填”),还有一个“备注”信息的文本输入框。现在要求在选择“必填”时,“备注”信息不能为空,此时应该在“备注”文本框前显示必填标志(常用的是“*”),如图2所示;而在选在“可选”时,“备注”信息可为空,此时必填标志“*”应该隐藏起来,如图1所示。
图1 备注可选
图2 备注必填
2.问题分析
当然,这个问题实际上很简单。如果是文本“备注”是通过TextBlock和文本输入框放在同一个GroupBox中,那么我们可以在“备注”TextBlock和文本输入框之间加一个显示“*”的TextBlock(为了描述方便,假设将其命名为tbStar)。则在选择“可选”时,将tbStar的Visibility属性设置为Hidden(隐藏);而在选择“必填”时,将tbStar的Visibility属性设置为Visible(可见)。至于,如何响应选择“可选”还是选择“必填”,我们可以使用事件和可以使用命令,这个不是这里要讲的重点,我们下面的代码中用了命令。
但是,通过图1和图2,我们看到此处的难点是:“备注”和“*”不是显示在GroupBox控件内部的,而是显示在GroupBox的Header属性中的。对于Header属性的设置,我们不可能直接将两个TextBlock控件(一个用于显示“备注”,一个用于显示“*”)设置为其值。于是,我们这里采用样式Style来实现。
3.实现代码
关于界面的XAML代码如下所示:
1 <Window x:Class="WPFTestDemo.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="350" Width="525"> 5 <Window.Resources> 6 <Style x:Key="setHeaderStyle" TargetType="GroupBox"> 7 <Setter Property="Header"> 8 <Setter.Value> 9 <StackPanel Orientation="Horizontal"> 10 <TextBlock Text="备注" /> 11 <TextBlock x:Name="txtStar" Text="*" Foreground="Red" Visibility="Collapsed"/> 12 </StackPanel> 13 </Setter.Value> 14 </Setter> 15 </Style> 16 </Window.Resources> 17 <Grid> 18 <Grid.RowDefinitions> 19 <RowDefinition Height="*" /> 20 <RowDefinition Height="3*" /> 21 </Grid.RowDefinitions> 22 <GroupBox Grid.Row="0" Margin="4" > 23 <StackPanel Orientation="Horizontal"> 24 <RadioButton VerticalAlignment="Stretch" Content="可选" IsChecked="True" 25 Command="{Binding Command}" CommandParameter="NoNeed"/> 26 <RadioButton VerticalAlignment="Stretch" Content="必填" 27 Command="{Binding Command}" CommandParameter="Need"/> 28 </StackPanel> 29 </GroupBox> 30 <GroupBox Grid.Row="1" Margin="4" Style="{StaticResource setHeaderStyle}"> 31 <TextBox ></TextBox> 32 </GroupBox> 33 </Grid> 34 </Window>
从上面的代码第30行可见,控件GroupBox引用了静态资源setHeaderStyle。而从第6-15行可见,资源setHeaderStyle通过Setter设置了GroupBox的Header属性为一个StackPanel控件,而在StackPanel中提供了两个TextBlock,一个显示“备注”,一个显示“*”。第24-27行代码显示我们通过命令来响应单选按钮“可选”和“必填”的选择,这里的命令绑定到DataContext的Command属性。下面是界面的视图代码实现,
1 public partial class MainWindow : Window 2 { 3 private ViewModel viewModel = new ViewModel(); 4 5 public MainWindow() 6 { 7 InitializeComponent(); 8 this.DataContext = this.viewModel; 9 this.viewModel.Command.ExecuteEvent += new Action<Object>( 10 delegate(object obj) 11 { 12 OptionEnum oe = OptionEnum.NoNeed; 13 Enum.TryParse<OptionEnum>((string)obj, out oe); 14 if (oe == OptionEnum.NoNeed) 15 { 16 TextBlock tb = GetTextBlockStar(); 17 tb.Visibility = Visibility.Hidden; 18 } 19 else 20 { 21 TextBlock tb = GetTextBlockStar(); 22 tb.Visibility = Visibility.Visible; 23 } 24 }); 25 } 26 private TextBlock GetTextBlockStar() 27 { 28 Style style = (Style)this.FindResource("setHeaderStyle"); 29 Setter sb = (Setter)style.Setters[0]; 30 StackPanel sp = (StackPanel)sb.Value; 31 return (TextBlock)sp.Children[1]; 32 } 33 }
上面代码中的第9-24行的代码就是定义了用于响应单选按钮选择的命令,在第16-17行时,判断出选择了“可选”单选按钮,此时设置显示“*”的TextBlock控件的Visibility属性为Hidden(隐藏);而在第21-22行判断出选择了“必填”单选按钮,此时设置显示“*”的TextBlock控件的Visibility属性为Visible(可见)。而我们如何获取显示“*”的TextBlock控件呢?在辅助方法GetTextBlockStar()中,我们通过Window的FindResource()方法来获取Key为setHeaderStyle的资源,然后再一步步地深入去找到显示“*”的TextBlock控件(这里就不一一说明了)。关于其他的辅助代码如下所示(这些代码就不一一分析)。
1 public class ViewModel 2 { 3 private MyCommand command; 4 public MyCommand Command 5 { 6 get { return command; } 7 set { command = value; } 8 } 9 10 public ViewModel() 11 { 12 command = new MyCommand(); 13 } 14 } 15 16 public class MyCommand : ICommand 17 { 18 public event Action<Object> ExecuteEvent; 19 20 public bool CanExecute(object parameter) 21 { 22 return ExecuteEvent != null; 23 } 24 25 public void Execute(Object parameter) 26 { 27 if (ExecuteEvent != null) 28 { 29 ExecuteEvent(parameter); 30 } 31 } 32 33 public event EventHandler CanExecuteChanged; 34 } 35 36 public enum OptionEnum 37 { 38 NoNeed, 39 Need 40 }