Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
520 views
in Technique[技术] by (71.8m points)

xaml - Windows Phone 7 - Setting style for specific control within selected ListBoxItem

let's say i have something like this:

<Grid>
    <ListBox x:Name="list" 
        ItemsSource="{Binding SomeCollection, Mode=TwoWay}" 
        SelectedItem="{Binding SomeItem, Mode=TwoWay}">                    
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock x:Name="first" Text="{Binding SomeProperty}" />
                <TextBlock x:Name="second" Text="{Binding OtherProperty}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Now, how do i alter some style property (f.ex. FontSize) of only the TextBlock called "second" when a ListBoxItem gets selected? If i'd like to set the FontSize for all ListBoxItem's contents, then i'd have no problem. That scenario is quite well documented here and elsewhere on the web.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I will not give you an exact solution, but a good point to start with: check out the file

C:Program FilesMicrosoft SDKsWindows PhonevX.YDesignSystem.Windows.xaml

you have to adjust X.Y to 7.0/7.1 along with your setup. There you will find exactly the same Control-Templates that are being used all the basic UI Controls of the WP7/Silverlight. Open it in VisualStudio-or-whateverelse and search for:

<Style TargetType="ListBoxItem">
  (... and immediatelly following ~40 lines of xaml)

ah well, since I've opened that file, here's that

    <!--x:Key="PhoneListBoxItem"-->
    <Style TargetType="ListBoxItem">
      <Setter Property="Background" Value="Transparent"/>
      <Setter Property="BorderThickness" Value="0" />
      <Setter Property="BorderBrush" Value="Transparent" />
      <Setter Property="Padding" Value="0" />
      <Setter Property="HorizontalContentAlignment" Value="Left"/>
      <Setter Property="VerticalContentAlignment" Value="Top"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="ListBoxItem">
            <Border x:Name="LayoutRoot" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal"/>
                  <VisualState x:Name="MouseOver" />
                  <VisualState x:Name="Disabled">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Background">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentBrush}"/>
                      </ObjectAnimationUsingKeyFrames>
                      <DoubleAnimation Storyboard.TargetName="ContentContainer" Storyboard.TargetProperty="Opacity" Duration="0" To=".5" />
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="SelectionStates">
                  <VisualState x:Name="Unselected"/>
                  <VisualState x:Name="Selected">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentContainer" Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>
              <ContentControl x:Name="ContentContainer" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Foreground="{TemplateBinding Foreground}" />
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

This is the complete style for your DEFAULT ListBoxItem - the thing you want to alter. Skim through the code and note the 'ContentPresenter' and preceding 'VisualStateGroup x:Name="SelectionStates"'.

ContentPresenter is the thing that will show your DataTemplate for the item.
VisualStates in that group define the changes from the normal state that should occur if a "selected state" is fired of on an list element.
Once the "selection state" diminishes, the element returns to the unselected state automatically and his visuals follow. Note also that the Unselected visual state does not enforce any changes - so it preserves your plain DataTemplate style.

The last thing to notice is that this is a style for ListBoxItem, and not for your data-item, nor your data-template. Your DataTemplate is never touched, it is directly displayed by the ContentPresenter. The ListBox wraps all your items in "ListBoxItem" instances, then displays those ListBoxItems and applies that style to them.

IMHO, this is the point you will have to work with.

You may want to copy&alter this style to your needs, and then set your ListBox.ItemContainerStyle to that new style. One of the ways is:

<YourPage.Resources>
    <Style x:Key="mylistboxitemoverride" .....
        ........
    </Style>
</YourPage.Resources>
...
...
<ListBox ......... ItemContainerStyle="{StaticResource mylistboxitemoverride}"
    ...
    ...
</ListBox>

Now, the trick is to modify the 'Selected' VisualState, and make it alter not the Foreground (doing that would restyle both your TextBoxes!), but some other property which will affect only one of your txbs. Unfortunatelly, that may be harder/uglier. I don't at that moment any idea how to make it "prettier" than hard-replacing the ContentPresenter with your DataTemplate and referencing your exact leaf-textbox in the VisualState like that:

    <Style .... TargetType="ListBoxItem">
      <Setter Property="Background" Value="Transparent"/>
      <Setter Property="BorderThickness" Value="0" />
      <Setter Property="BorderBrush" Value="Transparent" />
      <Setter Property="Padding" Value="0" />
      <Setter Property="HorizontalContentAlignment" Value="Left"/>
      <Setter Property="VerticalContentAlignment" Value="Top"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="ListBoxItem">
            <Border x:Name="LayoutRoot" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal"/>
                  <VisualState x:Name="MouseOver" />
                  <VisualState x:Name="Disabled">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Background">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentBrush}"/>
                      </ObjectAnimationUsingKeyFrames>
                      <DoubleAnimation Storyboard.TargetName="SECOND" Storyboard.TargetProperty="Opacity" Duration="0" To=".5" /> <!-- #### RETARGETTED -->
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="SelectionStates">
                  <VisualState x:Name="Unselected"/>
                  <VisualState x:Name="Selected">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SECOND" Storyboard.TargetProperty="Foreground"> <!-- #### RETARGETTED -->
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>

              <!-- #### INLINED YOUR DATATEMPLATE -->
              <StackPanel Orientation="Vertical"
                          Margin="{TemplateBinding Padding}"
                          DataContext="{TemplateBinding Content}">  <!-- #### careful with the bindings. the DataCtx may be needed or is spurious. do check that! -->
                <TextBlock Text="{Binding SomeProperty}" />  <!-- #### referenced from nowhere, so I removed the name -->
                <TextBlock x:Name="SECOND" Text="{Binding OtherProperty}" />
              </StackPanel>

            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

This should be almost what you want, or at least very close to it. I have not tested it, you may need to tinker with proper data-binding (I've included a DataContent=binding:Content, but that's a quick guess) and probably you will want to add your own animations. I think you have now tons of bits to experiment with. Have fun!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...