0

我试图将焦点设置到 ItemsControl 中的第一个按钮,该按钮已模板化并绑定到 ViewModel 中的 BindableCollection NamesList。简而言之,我有一组显示的按钮,我希望第一个(最左上角的按钮)在面板出现时具有焦点。

我的 XAML 中相关的部分是:

<StackPanel Visibility="{Binding IsNamesListVisible, Converter={StaticResource visibilityConverter}}">
   <StackPanel.Style>
      <Style>
         <Style.Triggers>
            <DataTrigger Binding="{Binding IsNamesListVisible}" Value="True">
               <Setter Property="FocusManager.FocusedElement" Value="{Binding Path=Items[0], ElementName=NamesList}"/>
            </DataTrigger>
         </Style.Triggers>            
      </Style>
   </StackPanel.Style>
   <ItemsControl x:Name="NamesList" IsTabStop="False" Focusable="False" VerticalAlignment="Top" HorizontalAlignment="Center" MaxWidth="500">
       <ItemsControl.ItemsPanel>
           <ItemsPanelTemplate>
               <WrapPanel Orientation="Horizontal" Focusable="False" />
           </ItemsPanelTemplate>
       </ItemsControl.ItemsPanel>
       <ItemsControl.ItemTemplate>
           <DataTemplate>
               <Button x:Name="NameButton" Content="{Binding Name}" Margin="10,10,0,10" Width="200" Height="100" />
           </DataTemplate>
       </ItemsControl.ItemTemplate>
   </ItemsControl>
   <Button x:Name="OtherButton" Content="Other" Margin="10,10,0,10" Width="200" Height="100" />
</StackPanel>

此 StackPanel 是所有包含在同一屏幕上的几个之一,并且在 ViewModel 中填充 NamesList 时可见。(填充“NamesList”时,“IsNamesListVisible”标志设置为 TRUE。)
我尝试了几种不同的方法,但都没有产生任何结果。

我尝试了上面看到的方法:

<Setter Property="FocusManager.FocusedElement" Value="{Binding Path=Items[0], ElementName=NamesList}"/>

我努力了:

<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=NamesList[0]}"/>

和:

<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=NamesList}"/>

我还尝试添加一个转换器(位于:https ://social.msdn.microsoft.com/Forums/vstudio/en-US/9551f7c9-a067-4921-9006-fb4ead912e7d/set-focusedelement-to-selected-listboxitem ?forum=wpf)到我的解决方案并使用它:

转换器:

public class ItemsControlContainerConverter: MarkupExtension, IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ItemsControl items = (ItemsControl)value;
        return items.ItemContainerGenerator.ContainerFromIndex(System.Convert.ToInt32(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

二传手:

FocusManager.FocusedElement="{Binding ElementName=list, Converter={loc:ItemsControlContainerConverter}, ConverterParameter=0}">

在这一点上,我相信所有这些方法只有在我的 ItemsControl 在运行时完全填充时才有效。我已经多次将触发器与 FocusManager.FocusedElement 属性一起使用,并且它始终可以正常工作,但我之前从未将它与 ItemsControl 一起使用。

也许我遗漏了一些明显的东西,但是在将我的头撞到众所周知的墙上一天之后,我看不到它。

如前所述,当此面板可见时,我需要顶部最左侧的按钮具有焦点。非常感谢任何建议。

4

1 回答 1

1

FocusManager.FocusedElement 是一个附加属性,仅接受可以具有焦点的 UIElement 派生元素。您正在为其分配一个无法获得焦点的常规 CLR 元素。

您需要一个转换器,根据第一个元素,该转换器将从 ItemsControl 项的可视树中接收第一个子项。

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;

namespace Converters
{
    public class GetElementFromItemsControlContainerConverter : IMultiValueConverter
    {
        #region IValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (values == null 
                || values.Length < 2
                || !(values[0] is ItemsControl itemsControl)
                || values[1] is null)
                return DependencyProperty.UnsetValue;

            var item = itemsControl.ItemContainerGenerator.ContainerFromItem(values[1]);

            if (item == null)
                return DependencyProperty.UnsetValue;

            return VisualTreeHelper.GetChild(item, 0);
        }

        object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion

        public static GetElementFromItemsControlContainerConverter Instance { get; } 
            = new GetElementFromItemsControlContainerConverter();
    }

    public class GetElementFromItemsControlContainerConverterExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return GetElementFromItemsControlContainerConverter.Instance;
        }
    }
}
<StackPanel Height="600" Width="800">
    <FocusManager.FocusedElement>
        <MultiBinding Converter="{converters:GetElementFromItemsControlContainerConverter}">
            <Binding ElementName="itemsControl"/>
            <Binding ElementName="itemsControl" Path="ItemsSource[0]"/>
        </MultiBinding>
    </FocusManager.FocusedElement>
    <TextBox Text="12345"/>
    <ItemsControl x:Name="itemsControl">
        <ItemsControl.ItemsSource>
            <x:Array Type="{x:Type sys:String}">
                <sys:String>One</sys:String>
                <sys:String>Two</sys:String>
                <sys:String>Three</sys:String>
            </x:Array>
        </ItemsControl.ItemsSource>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding}">
                    <Button.Style>
                        <Style TargetType="Button">
                            <Style.Triggers>
                                <Trigger Property="IsFocused" Value="True">
                                    <Setter Property="Background" Value="LightGreen"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

您是否能够将您的 ItemsSource 绑定到 ViewModel 中的 BindableCollection 并仍然生成所需的行为?

没有任何问题:

public class DataViewModel
{
    public ObservableCollection<string> Numbers { get; }
        = new ObservableCollection<string>()
        {
            "One",
            "Two",
            "Three"
        };
}
    <StackPanel Height="200" Width="200">
        <StackPanel.DataContext>
            <local:DataViewModel/>
        </StackPanel.DataContext>
        <FocusManager.FocusedElement>
            <MultiBinding Converter="{converters:GetElementFromItemsControlContainerConverter}">
                <Binding ElementName="itemsControl"/>
                <Binding ElementName="itemsControl" Path="ItemsSource[1]"/>
            </MultiBinding>
        </FocusManager.FocusedElement>
        <TextBox x:Name="tBox" Text="12345"/>
        <ItemsControl x:Name="itemsControl"
                      ItemsSource="{Binding Numbers}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding}">
                        <Button.Style>
                            <Style TargetType="Button">
                                <Style.Triggers>
                                    <Trigger Property="IsFocused" Value="True">
                                        <Setter Property="Background" Value="LightGreen"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>

您甚至可以异步更改集合,但这不会影响为焦点选择的元素。在示例中,它将始终使用索引 [1] 进行选择。

public class DataViewModel
{
    public ObservableCollection<string> Numbers { get; }
        = new ObservableCollection<string>()
        {
            "One",
            "Two",
            "Three"
        };

    private readonly DispatcherTimer timer = new DispatcherTimer();

    public DataViewModel()
    {
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += (s, e) =>
        {
            Numbers.Insert(0, Numbers[Numbers.Count - 1]);
            Numbers.RemoveAt(Numbers.Count - 1);
        };
        timer.Start();
    }
}
于 2021-04-20T18:48:10.007 回答