用于IsSelected的ListBox DataTrigger ==对人口进行虚假解雇?

时间:2012-03-02 19:41:21

标签: c# wpf triggers listbox

我遇到了ListBox的问题,但它涉及一些自定义控件。我将简要介绍一下这个问题。

短版

我有一个ListBox,通过设置其DataContext来填充多个项目。通过触发器将这些项的行为部分绑定到IsSelected属性。似乎每当ListBox填充了项目(设置了DataContext)时,对于列表中的所有项目,IsSelected属性都显式设置为false,这会导致触发器触发每个项目。我希望每个项目默认情况下IsSelected为false,并且不会触发每个项目的触发器。这是ListBox的预期行为吗?

详细版本

这是我想要实现的:一个自定义ListBox(SequenceNavigator),包含我正在调用ThumbnailViewer作为ListBoxItems。 ThumbnailViewer显示图像,并且还在图像上叠加了一个(或多个)IconOverlayButtons。 IconOverlayButton,顾名思义,是一个用图标表示的Button(Rectangle上的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部分,导致IconOverlayButton暂时出现并淡出列表中的每个项目。这当然不是理想的行为。我希望IsSelected默认为新项目是假的,因此没有触发器。

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

1 个答案:

答案 0 :(得分:3)

您需要将触发器设置为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不是依赖属性,因此您必须在更新后通过代码隐藏通知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));
        }
    }