我遇到了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在这里似乎不是问题。
答案 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
不是依赖属性,因此需要DataTrigger
或MultiDataTrigger
。
尚未完成!由于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));
}
}