Skip to content
分类目录:

WPF Style

Post date:
Author:

https://github.com/MahApps/MahApps.Metro

https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/tree/master/MaterialDesignThemes.Wpf

https://github.com/HandyOrg/HandyControl

Style定义:

如果Style没有定义Key,则会自动应用到所有的相关组件上,如果不想应用默认样式的话,应该在对应的组件上使用Style=”{x:Null}”,移除自动应用的Style

<Style x:Key="BigFontButtonStyle" TargetType="Button">
	<Setter Property="FontSize" Value="18"/>
  <EventSetter Event="TextBlock.MouseLeave" Handler="element_MouseLeaveHandler"/>
</Style  

绑定到附加属性

<Setter Property="Height">
    <Setter.Value>
        <Binding Path="(attachHelper:DataGridHelper.DataGridCellHeight)" 
          RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType=DataGrid}"/>
    </Setter.Value>
</Setter>

Brush属性设置:

Brush存在于两个命名空间,System.Windows.Media和System.Drawing, 控件的属性是System.Windows.Media中的。

Style中依赖属性赋值的优先级:

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-property-value-precedence

本地属性:在控件中直接设置的属性或者通过Binding设置。

本地属性>TemplatedParent>Style trigger中的属性设置 >Template trigger中的属性设置>Style中Setter的属性设置

因此,当本地属性设置后,如果想在Style的trigger中修改,则不会生效

解决办法:将本地属性移到Style 的setter中,这样Style的trigger会优先生效。

demo:

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

在上面的例子中,Button的本地属性Background设置优先级最高,所以会显示Red,Style和Template中的不会生效。

当移除Button的本地属性Background后,Style的trigger的属性设置优先级会变高。

Styles继承

Styles是支持继承的,通过设置BasedOn属性来实现。

<Style BasedOn="{StaticResource .....}" TargetType=""/>

Style继承后,关于Style.Trigger执行的问题:

当父Style和子Style都添加了同一个Trigger,则当触发时,

会先执行父Style的Trigger,再执行子Style的Trigger,如果子Style的Trigger中属性设置没有完全覆盖父Style中的Trigger属性设置,则会先后同时起作用。

<Style x:Key="Styles.DataGridRow.Base" TargetType="DataGridRow">
    <Setter Property="Background" Value="White"/>
    <Setter Property="BorderBrush" Value="#e1e6eb"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BorderBrush" Value="#2FA6FF" />
        </Trigger>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="BorderBrush" Value="Orange" />
            <Setter Property="BorderThickness" Value="4"/>
            <Setter Property="Background" Value="Red" />
        </Trigger>
    </Style.Triggers>
</Style>
<Style x:Key="Styles.DataGridRow.Default" TargetType="DataGridRow" 
  BasedOn="{StaticResource Styles.DataGridRow.Base}">
    <Setter Property="Background" Value="White"/>
    <Setter Property="BorderBrush" Value="#238865"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Style.Triggers>        
        <Trigger Property="IsSelected" Value="True">
            <!--<Setter Property="BorderBrush" Value="#668db3" />-->
            <Setter Property="BorderThickness" Value="10"/>
        </Trigger>
    </Style.Triggers>
</Style>

如上图所示,IsSelected发生时,会先执行Styles.DataGridRow.Base中的Trigger设置,再去执行Styles.DataGridRow.Default中的Triger设置。

在Style中设置EventTrigger

需要在代码中实现Handler。

<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding SchoolList}" Margin="10,10">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <EventSetter Event="PreviewMouseMove" Handler="ListBoxItem_PreviewMouseMove" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate >
            <Grid helper:GridHelper.Cols="* *">
                <TextBlock Grid.Column="0" Text="{Binding Name}" Margin="10,0"/>
                <TextBlock Grid.Column="1" Text="{Binding Location}"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Triggers

Trigger是按照定义的顺序执行的,如果两个Trigger都满足条件,则会依次生效,后面的Trigger会把前面的Trigger中设置的相同的属性值覆盖。

可以定义在:

Style.Trigger, DataTemplate.Triggers, ControlTemplate.Triggers, FrameworkElement.Triggers

注意:FrameworkElement.Triggers中的Trigger只能是EventTrigger。

还可以设置当Trigger进入和退出时,做一些操作,如添加入场或出场动画,

<Trigger>
  <Trigger.EnterActions>
  </Trigger.EnterActions>
  <Trigger.ExitActions>
  </Trigger.ExitActions>
</Trigger>

Basic Property Trigger

当控件的属性改变时,会Trigger对应的操作。

<Style TargetType="Button">
	<Style.Triggers>
  	<Trigger Property="Control.IsFocused" Value="True">
    	<Setter Property="Control.ForeGround" Value="DardRed"/>
    </Trigger>
    <Trigger Property="Grid.Row" Value="1">
    	<Setter Property="Control.ForeGround" Value="DardRed"/>
    </Trigger>
  </Style.Triggers>
</Style>
<Style x:Key="NavigationBarButton" TargetType="Button">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Height" Value="146"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="Margin" Value="20,0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border BorderBrush="{TemplateBinding BorderBrush}" 
                  Background="{TemplateBinding Background}" 
                  BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Image Stretch="Uniform" 
                          Source="{Binding (attach:ButtonHelper.NormalImageSource),RelativeSource={RelativeSource Mode=TemplatedParent}}" Width="64"/>
                        <ContentPresenter Grid.Row="1" 
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsPressed" Value="True">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" 
                          Background="{TemplateBinding Background}" 
                          BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*"/>
                                    <RowDefinition Height="Auto"/>
                                </Grid.RowDefinitions>
                                <Image Stretch="Uniform" 
                                  Source="{Binding (attach:ButtonHelper.PressedImageSource),RelativeSource={RelativeSource Mode=TemplatedParent}}" Width="64"/>
                                <ContentPresenter Grid.Row="1" 
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Trigger>
    </Style.Triggers>
</Style>

EventTrigger

当事件发生时,会Trigger相应的操作。

<Style x:Key="BigFontStyle">
	<Style.Trigers>
  	<EventTrigger RoutedEvent="Mouse.MouseEnter">
    	<EventTrigger.Actions>
      	<BeginStoryborad>
        	<DoubleAnimation
              Duration="0:0:0.2"
              Storyboard.TargetProperty="FontSize"
              To="22" />
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>
  </Style.Triggers>
	
</Style>

MultiTrigger

注意MultiTrigger和MultiDataTrigger的使用区别:MultiTrigger中只能使用Property和Value来设置条件

MultiTrigge

当多个条件同时满足要求后,才会Trigger对应的操作。

<Style TargetType="{x:Type DataGridCell}">
    <Style.Triggers>
        <MultiTrigger>          
            <MultiTrigger.Conditions>
                <Condition Property="IsSelected" Value="True"/>
                <Condition Property="IsFocused" Value="False"/>              
            </MultiTrigger.Conditions>
          //一个Condition多种Setter
          	<MultiTrigger.Setters>
            	<Setter Property="Background" Value="Transparent"/>
            	<Setter Property="Opacity" Value="0.9"/>
          	</MultiTrigger.Setters>

            <MultiTrigger.EnterActions>
                <BeginStoryboard Name="bb">
                    <Storyboard>
                        <ColorAnimation 
                        Storyboard.TargetName="ellipseFlashUI"
                        Duration="0:0:0.5"
                        Storyboard.TargetProperty="(Fill).(SolidColorBrush.Color)" 
                        From="{StaticResource flashColor}" To="White" 
                        AutoReverse="True" RepeatBehavior="Forever"/>
                    </Storyboard>
                </BeginStoryboard>
            </MultiTrigger.EnterActions>
            <MultiTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="bb" />
            </MultiTrigger.ExitActions>          
        </MultiTrigger>      
    </Style.Triggers>
</Style>

DataTrigger & MultiDataTrigger

DataTrigger

可以根据不同的Data Value,来设置控件不同的Template

普通的Trigger中使用的是控件本身的属性,而DataTrigger的使用范围要更大,通过使用Binding,可以Binding到自身或者其他Control的属性上(需要设置ElementName或者RelativeSource),或者ViewModel上的属性,或者使用附加属性

在DataTrigger中使用绑定,默认是绑定到DataContext上的属性,如果不是绑定到ViewModel中的属性,则无论是绑定到自身控件设置的属性和附加属性或者其他,都需要设置RelativeSource才会起作用

<!--绑定到控件自身的属性上 -->
<ToggleButton x:Name="btn" Content="Go" >
    <ToggleButton.Style>
        <Style TargetType="ToggleButton">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsChecked,
                  RelativeSource={RelativeSource Self}}" Value="True">
                    <Setter Property="Foreground" Value="Blue"/>
                </DataTrigger> 
            </Style.Triggers>
        </Style>
    </ToggleButton.Style>
</ToggleButton>
<!--绑定到控件自身的属性上 -->
<ToggleButton x:Name="btn" Content="Go" >
    <ToggleButton.Style>
        <Style TargetType="ToggleButton">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsChecked,ElementName=btn}" 
                        Value="True">
                    <Setter Property="Foreground" Value="Blue"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=IsChecked,ElementName=btn}" 
                        Value="False">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                
            </Style.Triggers>
        </Style>
    </ToggleButton.Style>
</ToggleButton>

DataTrigger在Style.Triggers中和ControlTemplate.Triggers使用有没有什么区别?

在DataTrigger中绑定到ViewModel:

[ObservableObject]
public partial class MainWindowViewModel
{
    [ObservableProperty]
    private bool _Start;
    [RelayCommand]
    private void Change()
    {
        Start = !_Start;
    }
}
<Style x:Key="rec" TargetType="Rectangle">
    <Setter Property="Stroke" Value="Black"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Start}" Value="False">
            <DataTrigger.Setters>
                <Setter Property="Fill" Value="Orange"/>
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Start}" Value="True">
            <DataTrigger.Setters>
                <Setter Property="Fill" Value="Red"/>
            </DataTrigger.Setters>
        </DataTrigger>
    </Style.Triggers>
</Style>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>        
    </Grid.RowDefinitions>
    <Button Content="Change" Command="{Binding ChangeCommand}"/>
    <Rectangle Style="{StaticResource rec}" Grid.Row="1"/>
</Grid>

在DataTrigger中绑定到附加属性,需要显示指定RelativeSource.

 <Style TargetType="Button" x:Key="btnStyle">  
   <Style.Triggers>
       <MultiDataTrigger>
           <MultiDataTrigger.Conditions>
               <Condition
                 Binding="{Binding (local:ButtonHelper.Num),
                       RelativeSource={RelativeSource Self}}" Value="1"/>
           </MultiDataTrigger.Conditions>
           <MultiDataTrigger.EnterActions>
               <BeginStoryboard Name="storyBoard2">
                   <Storyboard Storyboard.TargetProperty="Width">
                       <DoubleAnimation Duration="0:0:5" 
                          From="100" To="200" 
                          RepeatBehavior="Forever" 
                          AutoReverse="True"/>
                   </Storyboard>
               </BeginStoryboard>
           </MultiDataTrigger.EnterActions>
           <MultiDataTrigger.ExitActions>
               <StopStoryboard BeginStoryboardName="storyBoard2"/>
           </MultiDataTrigger.ExitActions>
       </MultiDataTrigger>
   </Style.Triggers>
 <Style>

在DataTrigger中可以使用Binding来绑定变量。

<Style TargetType="Button" x:Key="reagentCellStyle">
 <Style.Triggers>
   <DataTrigger 
       Binding="{Binding (temphelper:AttachedHelper.UseContext), 
                 RelativeSource={
                   RelativeSource AncestorType=local:ViewControl, 
                                  Mode=FindAncestor}}"
       Value="0">
     <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
              <ControlTemplate.Triggers> 
                <MultiTrigger>
                	<MultiDataTrigger.Conditions>
                  	//绑定属性和值
                    <Condition Binding="{Binding Path=PuttingFlow}" 
                      Value="{x:Static bo:PuttingFlow.WaitingPut}" />
                  </MultiDataTrigger.Conditions>                  
                </MultiTrigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
     </Setter>
   </DataTrigger>   
 </Style.Triggers>
  
MultiDataTrigger

MultiDataTrigger可以在Condition中使用Property或Binding来设置条件,使用Binding设置时,需要显示的设置RelativeSource

同时MultiDataTrigger中的Binding可以结合MultiBinding使用

<MultiTrigger>
	<MultiDataTrigger.Conditions>
  	//绑定属性和值
    <Condition Binding="{Binding Path=PuttingFlow}" 
      Value="{x:Static bo:PuttingFlow.WaitingPut}" />
  </MultiDataTrigger.Conditions>                  
</MultiTrigger>

如果需要在Condition中判断不等于或者大于,小于等情况,则需要通过ValueConverter来转换为bool来实现。

豫ICP备2021008859号-1