13

典型的な MVVM シナリオがあります。StepsViewModels のリストにバインドされた ListBox があります。StepViewModels が StepViews としてレンダリングされるように DataTemplate を定義します。StepView UserControl には、ラベルと TextBox のセットがあります。

私がしたいのは、textBox がフォーカスされているときに StepView をラップしている ListBoxItem を選択することです。次のトリガーを使用して、TextBox のスタイルを作成しようとしました。

<Trigger Property="IsFocused" Value="true">
    <Setter TargetName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Property="IsSelected" Value="True"/>
</Trigger>

しかし、TextBox に IsSelected プロパティがないというエラーが表示されます。私は今、ターゲットはListBoxItemです。どうすればそれを機能させることができますか?

4

5 に答える 5

32

子がフォーカスされている場合に true に設定される読み取り専用プロパティ IsKeyboardFocusWithin があります。これを使用して、トリガーで ListBoxItem.IsSelected を設定できます。

<ListBox ItemsSource="{Binding SomeCollection}" HorizontalAlignment="Left">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="IsSelected" Value="True" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Width="100" Margin="5" Text="{Binding Name}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
于 2010-06-03T02:27:35.670 に答える
5

Jordan0Day が正しく指摘したように、IsKeyboardFocusWithin ソリューションを使用すると、実際に大きな問題が発生する可能性があります。私の場合、ListBox に関するツールバーのボタンも機能しなくなりました。フォーカスと同じ問題。ボタンをクリックすると、ListBoxItem はフォーカスを失い、Button はその CanExecute メソッドを更新したため、ボタン クリック コマンドが実行される直前にボタンが無効になりました。

私にとってより良い解決策は、この投稿で説明されているように ItemContainerStyle EventSetter を使用することでした: ListboxItem selection when the controls inside is used

XAML:

<Style x:Key="MyItemContainer.Style" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Background" Value="LightGray"/>
    <EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border x:Name="backgroundBorder" Background="White">
                    <ContentPresenter Content="{TemplateBinding Content}"/>
                </Border>
            <ControlTemplate.Triggers>
                 <Trigger Property="IsSelected" Value="True">
                     <Setter TargetName="backgroundBorder" Property="Background" Value="#FFD7E6FC"/>
                 </Trigger>
             </ControlTemplate.Triggers>
         </ControlTemplate>
     </Setter.Value>
 </Setter>
</Style>

ビューの背後にあるコードの EventHandler:

private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e)
{
    (sender as ListBoxItem).IsSelected = true;
}
于 2013-01-14T09:41:01.213 に答える
2

これを実現する 1 つの方法は、添付プロパティを使用してカスタム動作を実装することです。基本的に、添付プロパティはListBoxItemスタイルを使用して適用され、GotFocusイベントに接続されます。これは、コントロールの子孫がフォーカスを取得した場合にも発生するため、このタスクに適しています。イベント ハンドラでIsSelectedは、 に設定されtrueます。

私はあなたのために小さな例を書きました:

動作クラス:

public class MyBehavior
{
    public static bool GetSelectOnDescendantFocus(DependencyObject obj)
    {
        return (bool)obj.GetValue(SelectOnDescendantFocusProperty);
    }

    public static void SetSelectOnDescendantFocus(
        DependencyObject obj, bool value)
    {
        obj.SetValue(SelectOnDescendantFocusProperty, value);
    }

    public static readonly DependencyProperty SelectOnDescendantFocusProperty =
        DependencyProperty.RegisterAttached(
            "SelectOnDescendantFocus",
            typeof(bool),
            typeof(MyBehavior),
            new UIPropertyMetadata(false, OnSelectOnDescendantFocusChanged));

    static void OnSelectOnDescendantFocusChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ListBoxItem lbi = d as ListBoxItem;
        if (lbi == null) return;
        bool ov = (bool)e.OldValue;
        bool nv = (bool)e.NewValue;
        if (ov == nv) return;
        if (nv)
        {
            lbi.GotFocus += lbi_GotFocus;
        }
        else
        {
            lbi.GotFocus -= lbi_GotFocus;
        }
    }

    static void lbi_GotFocus(object sender, RoutedEventArgs e)
    {
        ListBoxItem lbi = sender as ListBoxItem;
        lbi.IsSelected = true;
    }
}

ウィンドウ XAML:

<Window x:Class="q2960098.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:q2960098">
    <Window.Resources>
        <DataTemplate x:Key="UserControlItemTemplate">
            <Border BorderBrush="Black" BorderThickness="5" Margin="10">
                <my:UserControl1/>
            </Border>
        </DataTemplate>
        <XmlDataProvider x:Key="data">
            <x:XData>
                <test xmlns="">
                    <item a1="1" a2="2" a3="3" a4="4">a</item>
                    <item a1="a" a2="b" a3="c" a4="d">b</item>
                    <item a1="A" a2="B" a3="C" a4="D">c</item>
                </test>
            </x:XData>
        </XmlDataProvider>
        <Style x:Key="MyBehaviorStyle" TargetType="ListBoxItem">
            <Setter Property="my:MyBehavior.SelectOnDescendantFocus" Value="True"/>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox ItemTemplate="{StaticResource UserControlItemTemplate}"
                 ItemsSource="{Binding Source={StaticResource data}, XPath=//item}"
                 HorizontalContentAlignment="Stretch"
                 ItemContainerStyle="{StaticResource MyBehaviorStyle}">

        </ListBox>
    </Grid>
</Window>

ユーザー コントロール XAML:

<UserControl x:Class="q2960098.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UniformGrid>
        <TextBox Margin="10" Text="{Binding XPath=@a1}"/>
        <TextBox Margin="10" Text="{Binding XPath=@a2}"/>
        <TextBox Margin="10" Text="{Binding XPath=@a3}"/>
        <TextBox Margin="10" Text="{Binding XPath=@a4}"/>
    </UniformGrid>
</UserControl>
于 2010-06-02T20:40:36.090 に答える
1

ユーザーコントロールを作成し、それをDataTemplateとして使用すると、よりクリーンに機能するようです。そうすれば、100%機能しないダーティなスタイルトリガーを使用する必要はありません。

于 2012-10-26T18:54:33.907 に答える
1

編集:他の誰かが別の質問ですでに同じ答えを持っていました:https://stackoverflow.com/a/7555852/2484737

Maexs の回答を続けると、EventSetter の代わりに EventTrigger を使用すると、コード ビハインドの必要がなくなります。

<Style.Triggers>
    <EventTrigger RoutedEvent="GotKeyboardFocus">
        <BeginStoryboard>
            <Storyboard >
                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected" >
                    <DiscreteBooleanKeyFrame Value="True" KeyTime="0:0:0"/>
                </BooleanAnimationUsingKeyFrames>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</Style.Triggers>
于 2013-10-16T13:49:43.933 に答える