IsSelected的ListBox DataTrigger==填充时错误触发

本文关键字:错误 填充 ListBox DataTrigger IsSelected | 更新日期: 2023-09-27 18:26:33

我有一个ListBox问题,但它涉及一些自定义控件。我将对这个问题作简短和详细的描述。

简短版本

我有一个ListBox,它通过设置DataContext填充了几个项。这些项的行为通过触发器与IsSelected属性部分绑定。似乎每当ListBox中填充了项(设置了DataContext)时,列表中所有项的IsSelected属性都会显式设置为false,这会导致为每个项触发。我希望每个项目的IsSelected默认为false,而不是为每个项目触发。这是ListBox的预期行为吗?

详细版本

以下是我试图实现的:一个自定义的ListBox(SequenceNavigator),包含我称之为ThumbnailViewer的ListBoxItems。ThumbnailViewer显示一幅图像,图像上还覆盖了一个(或多个)IconOverlayButtons。顾名思义,IconOverlayButton是一个由图标(矩形上的DrawingBrush)表示的按钮。IconOverlayButton应仅显示列表中所选项目的图标,并且应淡入淡出。

我几乎有这个工作,但遇到了一个问题。为了通知IconOverlayButton淡入/淡出,我在ListBox中设置了触发器,如下所示:

                    <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type s:SurfaceListBoxItem}}}" Value="True">
                        <Setter TargetName="uiThumbnailViewer" Property="Mode" Value="{x:Static local:DisplayMode.Playback}"/>
                        ...
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type s:SurfaceListBoxItem}}}" Value="False">
                        ...
                        <Setter TargetName="uiThumbnailViewer" Property="IsEnabled" Value="False"/>
                    </DataTrigger> 

然后,我依次设置IconOverlayButton的ControlTemplate,以对IsEnabled属性更改值做出反应:

<UserControl x:Class="...UIIconOverlayButton"
         ...>
<Grid>
    <Button>
        <Button.Template>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid>
                    <Rectangle Opacity="0" x:Name="rctIcon"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter TargetName="rctIcon" Property="Fill" Value="{Binding Path=ButtonDownBrush, RelativeSource={RelativeSource AncestorType={x:Type local:UIIconOverlayButton}}}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="False">
                        <Setter TargetName="rctIcon" Property="Fill" Value="{Binding Path=ButtonUpBrush, RelativeSource={RelativeSource AncestorType={x:Type local:UIIconOverlayButton}}}" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard FillBehavior="Stop">
                                    <DoubleAnimation Storyboard.TargetName="rctIcon" Storyboard.TargetProperty="Opacity"
                                 From="0" To="1" Duration="0:0:1" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.Setters>
                            <Setter TargetName="rctIcon" Property="Opacity" Value="1"/> 
                        </Trigger.Setters>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard FillBehavior="Stop">
                                    <DoubleAnimation Storyboard.TargetName="rctIcon" Storyboard.TargetProperty="Opacity"
                                 From="1" To="0" Duration="0:0:0.25" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Button.Template>
    </Button>
</Grid>

这个解决方案90%有效。当我选择一个ListBoxItem(ThumbnailViewer)时,IconOverlayButton会正确淡入,当我选择另一个项目时,上一个项目会淡出,而所选项目会淡入。

问题:当ListBox被填充时,似乎每个项目的IsSelected都被明确设置为False(据我所知,不是我)。这会触发IconOverlayButton的DataTrigger的ExitAction部分,导致IconOverlay按钮暂时出现,并为列表中的每个项目淡出。这当然是不可取的行为。我希望IsSelected对于新项目默认为false,因此没有触发器。

编辑:更新标题以更准确。此外,我用ListBox/ListBoxItem而不是SurfaceListBox/SurfaceListBox项进行了尝试,其行为是相同的。Surface SDK在这里似乎没有问题。

IsSelected的ListBox DataTrigger==填充时错误触发

您需要将触发器设置为MultiDataTrigger,并使用IsLoaded状态作为触发器之一。这将防止在加载控件之前触发。有一些关键的修改来实现这一点。

首先,你需要修改这个触发器:

<Trigger Property="IsEnabled" Value="True">

将其更改为MultiDataTrigger:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True" />
        <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type my:actionButton}}, Path=IsLoaded}" Value="True" />
    </MultiDataTrigger.Conditions>
....

my:actionButton是UserControl(您希望查看其IsLoaded属性)。我特别使用MultiDataTrigger,因为IsLoaded不是依赖属性,因此需要DataTriggerMultiDataTrigger

还没有完成!由于IsLoaded不是依赖属性,因此在更新时必须从codeehind通知UI。

    public actionButton()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(actionButton_Loaded);
    }
    void actionButton_Loaded(object sender, RoutedEventArgs e)
    {
        Notify("IsLoaded");
    }

这应该可以解决你的问题!作为免责声明,我不能保证这是最好的解决方案,但我试着选择了一个相当干净的。

编辑:通知:

    public event PropertyChangedEventHandler PropertyChanged;
    private void Notify(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }