Saturday, March 1, 2008

Style Button in WPF

. Saturday, March 1, 2008

Questo post nasce da una richiesta di un utente su di un forum, in cui si cercava di utilizzare una tecnologia di ben 20 anni fa, GDI, per la costruzione di un'interfaccia abbastanza complessa che a mio modesto parere può essere resa, dal punto di vista della costruzione, molto semplice, se si utilizza una tecnologia moderna come WPF.
L'articolo è suddiviso in due parti.
La prima in cui tramite uno screencast di circa 5 minuti, mostro come con la potenza di un'ambiente come Blend è possibile creare facilmente qualsiasi interfaccia.
Nella seconda parte invece ci spostiamo in Visual Studio 2008, in cui con il codice XAML prodotto è possibile definire gli stili dei 4 button.
(NB: Purtroppo per problemi tecnici il video non presenta l'audio, ma spero sia comunque autoesplicativo).
Per il video con una risoluzione più alta, cliccare qui.


 
Il codice XAML, come detto in precedenza, è alla base dell'oggetto Style che rappresenta il punto principale in cui definire le caratteristiche grafiche dei controlli presenti a livello di Page se stiamo creando una WPF Browser Application o a livello di Windows nel caso di WPF Application.
Il concetto di stile se vogliamo è molto simile a quello dei css per il web, ma come avrete già compreso tale concetto è stato esteso anche alle applicazioni windows. 
Il file di risorse Application.Resources della WPF Browser Application in cui definisco gli stili è così formato:
   1: <Application.Resources>
   2:  <Style x:Key="TestButton1" TargetType="Button">
   3:   <Setter Property="Template">
   4:    <Setter.Value>
   5:     <ControlTemplate TargetType="{x:Type Button}">
   6:      <Grid Cursor="Hand">
   7:       <Path Name="btnUpRight" Width="50" Height="50" Fill="#FFF44E06" Stretch="Fill" 
StrokeThickness="1"
Data="M0,0 C27.614237,0 50,22.385763 50,50 L25,50 C25,36.192883 13.807119,25 0,25 L0,0 z">
   8:        <Path.Stroke>
   9:         <RadialGradientBrush>
  10:          <GradientStop Color="#FF000000" Offset="0"/>
  11:          <GradientStop Color="#FF000000" Offset="1"/>
  12:          <GradientStop Color="#FF00E7FA" Offset="0.553"/>
  13:         </RadialGradientBrush>
  14:        </Path.Stroke>
  15:       </Path>
  16:      </Grid>
  17:      <ControlTemplate.Triggers>
  18:       <Trigger Property="IsFocused" Value="True"/>
  19:       <Trigger Property="IsDefaulted" Value="True"/>
  20:       <Trigger Property="IsMouseOver" Value="True">
  21:        <Setter Property="Stroke"  TargetName="btnUpRight">
  22:         <Setter.Value>
  23:          <RadialGradientBrush>
  24:           <GradientStop Color="#FF000000" Offset="0"/>
  25:           <GradientStop Color="#FF000000" Offset="1"/>
  26:           <GradientStop Color="#FF002FFA" Offset="0.553"/>
  27:          </RadialGradientBrush>
  28:         </Setter.Value>
  29:        </Setter>
  30:       </Trigger>
  31:       <Trigger Property="IsPressed" Value="True">
  32:        <Setter Property="Fill" Value="#FF633319" TargetName="btnUpRight"/>
  33:       </Trigger>
  34:       <Trigger Property="IsEnabled" Value="False"/>
  35:      </ControlTemplate.Triggers>
  36:     </ControlTemplate>
  37:    </Setter.Value>
  38:   </Setter>
  39:  </Style>
  40:  <Style x:Key="TestButton2" TargetType="Button">
  41:   <Setter Property="Template">
  42:    <Setter.Value>
  43:     <ControlTemplate TargetType="{x:Type Button}">
  44:      <Grid Cursor="Hand">

45: <Path Name="btnBottomRight" Width="50" Height="50" Fill="#FFFC750A" Stretch="Fill"
StrokeThickness="1"
Data="M0,0 C27.614237,0 50,22.385763 50,50 L25,50 C25,36.192883 13.807119,25 0,25 L0,0 z"
RenderTransformOrigin="0.5,0.5">

  46:        <Path.Stroke>
  47:         <RadialGradientBrush>
  48:          <GradientStop Color="#FF000000" Offset="0"/>
  49:          <GradientStop Color="#FF000000" Offset="1"/>
  50:          <GradientStop Color="#FF00E7FA" Offset="0.553"/>
  51:         </RadialGradientBrush>
  52:        </Path.Stroke>
  53:        <Path.RenderTransform>
  54:         <TransformGroup>
  55:          <ScaleTransform ScaleX="1" ScaleY="1"/>
  56:          <SkewTransform AngleX="0" AngleY="0"/>
  57:          <RotateTransform Angle="90"/>
  58:          <TranslateTransform X="0" Y="0"/>
  59:         </TransformGroup>
  60:        </Path.RenderTransform>
  61:       </Path>
  62:      </Grid>
  63:      <ControlTemplate.Triggers>
  64:       <Trigger Property="IsFocused" Value="True"/>
  65:       <Trigger Property="IsDefaulted" Value="True"/>
  66:       <Trigger Property="IsMouseOver" Value="True">
  67:        <Setter Property="Stroke"  TargetName="btnBottomRight">
  68:         <Setter.Value>
  69:          <RadialGradientBrush>
  70:           <GradientStop Color="#FF000000" Offset="0"/>
  71:           <GradientStop Color="#FF000000" Offset="1"/>
  72:           <GradientStop Color="#FF002FFA" Offset="0.553"/>
  73:          </RadialGradientBrush>
  74:         </Setter.Value>
  75:        </Setter>
  76:       </Trigger>
  77:       <Trigger Property="IsPressed" Value="True">
  78:        <Setter Property="Fill" Value="#FF633319" TargetName="btnBottomRight"/>
  79:       </Trigger>
  80:       <Trigger Property="IsEnabled" Value="False"/>
  81:      </ControlTemplate.Triggers>
  82:     </ControlTemplate>
  83:    </Setter.Value>
  84:   </Setter>
  85:  </Style>
  86:  <Style x:Key="TestButton3" TargetType="Button">
  87:   <Setter Property="Template">
  88:    <Setter.Value>
  89:     <ControlTemplate TargetType="{x:Type Button}">
  90:      <Grid Cursor="Hand">
  91:       <Path Name="btnBottomLeft" Width="50" Height="50" Fill="#FFFF9400" Stretch="Fill" 
StrokeThickness="1"
Data="M0,0 C27.614237,0 50,22.385763 50,50 L25,50 C25,36.192883 13.807119,25 0,25 L0,0 z"
RenderTransformOrigin="0.5,0.5">
  92:        <Path.Stroke>
  93:         <RadialGradientBrush>
  94:          <GradientStop Color="#FF000000" Offset="0"/>
  95:          <GradientStop Color="#FF000000" Offset="1"/>
  96:          <GradientStop Color="#FF00E7FA" Offset="0.553"/>
  97:         </RadialGradientBrush>
  98:        </Path.Stroke>
  99:        <Path.RenderTransform>
 100:         <TransformGroup>
 101:          <ScaleTransform ScaleX="1" ScaleY="1"/>
 102:          <SkewTransform AngleX="0" AngleY="0"/>
 103:          <RotateTransform Angle="180"/>
 104:          <TranslateTransform X="0" Y="0"/>
 105:         </TransformGroup>
 106:        </Path.RenderTransform>
 107:       </Path>
 108:      </Grid>
 109:      <ControlTemplate.Triggers>
 110:       <Trigger Property="IsFocused" Value="True"/>
 111:       <Trigger Property="IsDefaulted" Value="True"/>
 112:       <Trigger Property="IsMouseOver" Value="True">
 113:        <Setter Property="Stroke"  TargetName="btnBottomLeft">
 114:         <Setter.Value>
 115:          <RadialGradientBrush>
 116:           <GradientStop Color="#FF000000" Offset="0"/>
 117:           <GradientStop Color="#FF000000" Offset="1"/>
 118:           <GradientStop Color="#FF002FFA" Offset="0.553"/>
 119:          </RadialGradientBrush>
 120:         </Setter.Value>
 121:        </Setter>
 122:       </Trigger>
 123:       <Trigger Property="IsPressed" Value="True">
 124:        <Setter Property="Fill" Value="#FF633319" TargetName="btnBottomLeft"/>
 125:       </Trigger>
 126:       <Trigger Property="IsEnabled" Value="False"/>
 127:      </ControlTemplate.Triggers>
 128:     </ControlTemplate>
 129:    </Setter.Value>
 130:   </Setter>
 131:  </Style>
 132:  <Style x:Key="TestButton4" TargetType="Button">
 133:   <Setter Property="Template">
 134:    <Setter.Value>
 135:     <ControlTemplate TargetType="{x:Type Button}">
 136:      <Grid Cursor="Hand">
 137:       <Path Name="btnUpLeft" Width="50" Height="50" Fill="#FFFFC300" Stretch="Fill" 
StrokeThickness
="1"
Data="M0,0 C27.614237,0 50,22.385763 50,50 L25,50 C25,36.192883 13.807119,25 0,25 L0,0 z"
RenderTransformOrigin="0.5,0.5">
 138:        <Path.Stroke>
 139:         <RadialGradientBrush>
 140:          <GradientStop Color="#FF000000" Offset="0"/>
 141:          <GradientStop Color="#FF000000" Offset="1"/>
 142:          <GradientStop Color="#FF00E7FA" Offset="0.553"/>
 143:         </RadialGradientBrush>
 144:        </Path.Stroke>
 145:       <Path.RenderTransform>
 146:        <TransformGroup>
 147:         <ScaleTransform ScaleX="1" ScaleY="1"/>
 148:          <SkewTransform AngleX="0" AngleY="0"/>
 149:          <RotateTransform Angle="-90.000000000000014"/>
 150:          <TranslateTransform X="0" Y="0"/>
 151:        </TransformGroup>
 152:       </Path.RenderTransform>
 153:      </Path>
 154:     </Grid>
 155:     <ControlTemplate.Triggers>
 156:      <Trigger Property="IsFocused" Value="True"/>
 157:      <Trigger Property="IsDefaulted" Value="True"/>
 158:      <Trigger Property="IsMouseOver" Value="True">
 159:       <Setter Property="Stroke"  TargetName="btnUpLeft">
 160:        <Setter.Value>
 161:         <RadialGradientBrush>
 162:          <GradientStop Color="#FF000000" Offset="0"/>
 163:          <GradientStop Color="#FF000000" Offset="1"/>
 164:          <GradientStop Color="#FF002FFA" Offset="0.553"/>
 165:         </RadialGradientBrush>
 166:        </Setter.Value>
 167:       </Setter>
 168:      </Trigger>
 169:      <Trigger Property="IsPressed" Value="True">
 170:       <Setter Property="Fill" Value="#FF633319" TargetName="btnUpLeft"/>
 171:      </Trigger>
 172:      <Trigger Property="IsEnabled" Value="False"/>
 173:     </ControlTemplate.Triggers>
 174:    </ControlTemplate>
 175:   </Setter.Value>
 176:  </Setter>
 177: </Style>
 178: </Application.Resources>


Come si può notare a differenza dei css, gli stili in WPF presentano il concetto dei trigger, cioè la possibilità di indicare delle condizioni per regolare lo stile dei controlli, per esempio come deve apparire un button nel caso dello stato IsPressed.
A questo punto non ci resta che associare ai 4 button i relativi stili:

   1: <Grid Height="336" Name="grid1" Width="460">
   2:  <Button Name="button1" Width="50" Height="50" 
   3:         Style="{StaticResource TestButton1}" 
   4:         Canvas.Left="330" Canvas.Top="216"
   5:         Click="button1_Click">
   6: </Button>
   7: <Button Name="button2" Width="50" Height="50" 
   8:         Style="{StaticResource TestButton2}" 
   9:         Canvas.Left="330" Canvas.Top="216"
  10:         Click="button2_Click"  
  11:         HorizontalAlignment="Right" 
  12:         Margin="0,0,205,93" VerticalAlignment="Bottom">
  13: </Button>
  14: <Button Name="button3" Width="50" Height="50"
  15:         Style="{StaticResource TestButton3}" 
  16:         Canvas.Left="280" Canvas.Top="216" 
  17:         Click="button3_Click"
  18:         VerticalAlignment="Bottom" 
  19:         HorizontalAlignment="Left" Margin="155,0,0,93">
  20: </Button>
  21: <Button Name="button4" Width="50" Height="50"
  22:         Style="{StaticResource TestButton4}" 
  23:         Canvas.Left="280" Canvas.Top="216"
  24:         Click="button4_Click" Margin="155,143,0,143"
  25:         HorizontalAlignment="Left">
  26: </Button>
  27: <Label Height="28" Margin="182,0,0,130" 
  28:        Name="lblIdButton" VerticalAlignment="Bottom"
  29:        HorizontalAlignment="Left" Width="46">Tasto:
  30: </Label>       
  31: </Grid>



Per completezza ho implementato anche l'evento Click in cui semplicemente modifico il testo della label lblIdButton posta nel centro descritto dai 4 tasselli:

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:   lblIdButton.Content = "1";
   4: }



Risultato finale:


ButtonWPF