【问题标题】:Set focus to an item in ItemsControl WPF将焦点设置到 ItemsControl WPF 中的项目
【发布时间】:2014-07-23 05:15:40
【问题描述】:

我在滚动查看器中水平显示图像列表,我正在使用下面的代码行来做它

<ScrollViewer Name="lviewThumbnails" Height="230"   >
<ItemsControl ItemsSource="{Binding ThumbsCollection}"  MouseWheel="ItemsControl_MouseWheel" > 
    <ItemsControl.ItemTemplate >
        <DataTemplate>
            <DockPanel Height="230">
                <Button Name="pageThumbnail" DockPanel.Dock="Top" Tag="{Binding}" VerticalAlignment="Top"  Margin="5,2">
                    <Grid>
                        <Image MaxWidth="140" Height="200" Source="{Binding ThubnailPath,IsAsync=True}" Stretch="None"></Image>                             
                    </Grid>
                </Button>
                <Label  HorizontalAlignment="Center"  FontSize="14" FontWeight="Bold" Content="text" Foreground="White" Padding="5"></Label>
            </DockPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel  Orientation="Horizontal"  />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

我想在打开视图时将焦点设置到此列表中的特定图像项 例如,如果列表中有 150 张图像,并且我想在打开列表时将焦点设置为图像 75

我正在做的是点击按钮我正在设置此代码

  lviewThumbnails.Visibility = Visibility.Visible; 

使图像可见,但第一项是每次默认选择的一项。 我正在使用触发器来检测所选图像,就像这样

  <DataTrigger Binding="{Binding IsSelected}" Value="True">                                            
                                        <Setter TargetName="pageThumbnail" Property="BorderBrush" Value="Yellow"/>
                                        <Setter TargetName="pageThumbnail" Property="BorderThickness" Value="3"/>
 </DataTrigger>

但问题是如果图像 75 是选定的,我可以看到图像 75 周围的黄色边框,但是当我单击按钮时焦点仍然在图像 1 上。我必须滚动查看器的上一个、下一个按钮才能到达图 75。 我正在使用具有特定样式的滚动查看器,例如第 1 列按钮第 2 列内容和第 3 列再次按钮(希望它不会影响默认行为),并且我正在防止鼠标在任何地方滚动查看器滚动

 <ControlTemplate TargetType="{x:Type ScrollViewer}" x:Key="ButtonOnlyScrollViewer">
        <ControlTemplate.Resources>
        </ControlTemplate.Resources>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20" />
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>
            <Button Style="{StaticResource ResourceKey=ViewPreviousButton}" Grid.Column="0"   Command="ScrollBar.PageUpCommand" MouseWheel="ItemsControl_MouseWheel" 
                      Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"   HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Button>

            <ScrollContentPresenter
            CanContentScroll="{TemplateBinding CanContentScroll}"
            Grid.Column="1" 
            Content="{TemplateBinding Content}"  
            Width="{TemplateBinding Width}"
            Height="{TemplateBinding Height}" 
            Margin="{TemplateBinding Margin}"/>
            <Button Style="{StaticResource ResourceKey=ViewNextButton}" Grid.Column="2"   Command="ScrollBar.PageDownCommand"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseWheel="ItemsControl_MouseWheel"
                     ></Button>

        </Grid>
    </ControlTemplate>

【问题讨论】:

    标签: c# wpf itemscontrol


    【解决方案1】:

    所有这些答案都忽略了关于 ItemsControls 的那一点 关键 信息。他们使用虚拟化,因此只有实际在视图中的项目(以及一些周围的项目)实际上“存在”。使用上面给出的答案,OP 试图做的事情是不可能的,因为第 75 张图像实际上还不存在,所以你不能将焦点放在它上面。

    你需要:

    1) 订阅 ItemsControl ItemContainerGenerator.StatusChanged 事件 2)设置您的滚动位置,以便将第 75 个项目滚动到视图中 3) 在 StatusChanged 事件处理程序中,等待第 75 项的 status = ContainerGenerated,然后取消订阅并设置焦点

    是的,这太复杂了,是的,代码将“立即”执行,但您必须等到容器生成后才能将焦点设置为第 75 个项目。这就是虚拟化的工作原理。

    当仅创建项目 1 - 10 时,您正尝试将焦点设置到第 75 个项目。

    【讨论】:

      【解决方案2】:

      在这种情况下,您可以使用附加属性FocusManager.FocusedElement

      <DataTrigger Binding="{Binding IsSelected}" Value="True">                                            
          <Setter TargetName="pageThumbnail" Property="BorderBrush" Value="Yellow"/>
          <Setter TargetName="pageThumbnail" Property="BorderThickness" Value="3"/>
          <Setter TargetName="pageThumbnail" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
      </DataTrigger>
      

      <DataTrigger Binding="{Binding IsSelected}" Value="True">                                            
          <Setter TargetName="pageThumbnail" Property="BorderBrush" Value="Yellow"/>
          <Setter TargetName="pageThumbnail" Property="BorderThickness" Value="3"/>
          <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=pageThumbnail}"/>
      </DataTrigger>
      

      如果上面没有正确聚焦,请先尝试聚焦容器,然后聚焦 pageThumbnail

      <DataTrigger Binding="{Binding IsSelected}" Value="True">                                            
          <Setter TargetName="pageThumbnail" Property="BorderBrush" Value="Yellow"/>
          <Setter TargetName="pageThumbnail" Property="BorderThickness" Value="3"/>
          <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
          <Setter TargetName="pageThumbnail" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
      </DataTrigger>
      

      更新

      问题不仅在于焦点,还在于元素的位置,因为它位于视图之外

      试试这个 xaml

      <ItemsControl ItemsSource="{Binding ThumbsCollection}"  MouseWheel="ItemsControl_MouseWheel" Name="singleThumbs">
          <ItemsControl.Resources>
              <Style TargetType="ContentPresenter" xmlns:l="clr-namespace:WPFPerfomanceTest">
                  <Setter Property="l:ScrollProvider.ScrollIntoView"
                          Value="{Binding IsSelected}" />
              </Style>
          </ItemsControl.Resources>
          <ItemsControl.ItemTemplate >
              <DataTemplate>
                  <DockPanel Height="230">
                      <Button Name="pageThumbnail"  DockPanel.Dock="Top" Tag="{Binding}" VerticalAlignment="Top"  Margin="5,2" Width="140">
                          <Grid>
                              <Image Height="200" Source="{Binding ThubnailPath,IsAsync=True}" Stretch="None"></Image>                                        
                          </Grid>
                      </Button>
                      <Label   HorizontalAlignment="Center"  FontSize="14" FontWeight="Bold" Content="{Binding PageNo}" Foreground="White" Padding="5"></Label>
                  </DockPanel>
                  <DataTemplate.Triggers>
                      <DataTrigger Binding="{Binding IsSelected}" Value="True" >
                          <Setter TargetName="pageThumbnail" Property="BorderBrush" Value="Yellow"/>
                          <Setter TargetName="pageThumbnail" Property="BorderThickness" Value="3"/>
                          <Setter TargetName="pageThumbnail" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
                      </DataTrigger>
      
                  </DataTemplate.Triggers>
              </DataTemplate>
          </ItemsControl.ItemTemplate>
          <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                  <WrapPanel  Orientation="Horizontal"  />
              </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>
      </ItemsControl>
      

      请注意,我已经为 ContentPresenter 添加了样式并将 ScrollProvider.ScrollIntoView 绑定到 IsSelected 属性

      还从图像中删除了最大宽度,并为按钮指定了 Width="140"。最大宽度会导致按钮尺寸缩小,直到加载图像,这实际上使显示行为无效

      并使其正常工作

      我们需要一些附加行为,将此类添加到您的项目中

      class ScrollProvider : DependencyObject
      {
          public static bool GetScrollIntoView(DependencyObject obj)
          {
              return (bool)obj.GetValue(ScrollIntoViewProperty);
          }
      
          public static void SetScrollIntoView(DependencyObject obj, bool value)
          {
              obj.SetValue(ScrollIntoViewProperty, value);
          }
      
          // Using a DependencyProperty as the backing store for ScrollIntoView.  This enables animation, styling, binding, etc...
          public static readonly DependencyProperty ScrollIntoViewProperty =
              DependencyProperty.RegisterAttached("ScrollIntoView", typeof(bool), typeof(ScrollProvider), new PropertyMetadata(false, OnScrollIntoViewChanged));
      
          private static void OnScrollIntoViewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              FrameworkElement element = d as FrameworkElement;
              if ((bool)e.NewValue)
              {
                  element.BringIntoView();
                  element.Focus();
              }
          }
      }
      

      【讨论】:

      • 我尝试了 3 个选项,但列表中的第一项始终是默认选择的一项
      • 如何设置 IsSelected 属性,通过后台功能,或通过单击/按下按钮?是否可以看到您的代码的工作示例?这可能有助于轻松完成。
      【解决方案3】:

      您可以使用触发器来执行此操作,并为所选项目或任何适合您的方式使用单独的模板。

      类似这样,假设您将图像 ID 作为属性;

      <DataTrigger Binding="{Binding ThumbsCollection.ImageId}" Value="75">
         <Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
      </DataTrigger>
      

      【讨论】:

      • 查看问题的编辑。我已经使用触发器来设置所选项目的样式。但问题是如果选择第 75 张图像,我必须使用滚动条滚动条才能到达该图像,默认情况下将加载第 1 项
      • 好的。所以你想让滚动条自动滚动到图像 75。我没有先得到它。
      • 那么以下链接可能对您有所帮助 (stackoverflow.com/questions/6336441/…)
      【解决方案4】:

      如果要设置焦点或显示一些效果,可以绑定 IsFocus 并使用触发器

      private void Button_Click(object sender, RoutedEventArgs e)
              {
                  lviewThumbnails.Visibility = Visibility.Visible;
      
                  int index = 75;
                  ThumbsCollection[index].IsFocus = true;
      
                  Button button = (Button)FindFirstVisualChild(lviewThumbnails, "pageThumbnail");
      
                  lviewThumbnails.ScrollToHorizontalOffset((button.ActualWidth + 6.5) * index); // set it with pageThumbnail's location or margin
              }
      
              public object FindFirstVisualChild(DependencyObject obj, string childName)
              {
                  for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                  {
                      DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                      if (child != null && child.GetValue(NameProperty).ToString() == childName)
                      {
                          return child;
                      }
                      else
                      {
                          object childOfChild = FindFirstVisualChild(child, childName);
                          if (childOfChild != null)
                          {
                              return childOfChild;
                          }
                      }
                  }
                  return null;
              }
      

      我想你想在屏幕上显示第 75 个(中间项目)项目,所以你可以使用ScrollToHorizontalOffset。首先获取按钮的实际值,并在计算中获取它的边距/填充,然后您将获得偏移量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-15
        • 2012-07-29
        • 1970-01-01
        • 1970-01-01
        • 2010-11-11
        • 2015-09-19
        • 2011-06-30
        • 2011-06-09
        相关资源
        最近更新 更多