Silverlight从绑定列表中获取ListBoxItem
我在名为 Downloads.XAML 的文件中有一个列表框。绑定到该 ListBox 的项目来自我的 ViewModel 中的绑定列表。我有在不同 XAML 控件中的 ListBoxItem 样式中定义的状态,需要根据绑定项上设置的属性来触发这些状态。我遇到的问题是我无法从 ListBox 获取 ListBoxItem,因为 .SelectedItem 引用实际绑定对象而不是 ListBox。我尝试过使用 ListBox.ItemContainerGenerator,但这仅在大约 80% 的情况下返回 ListItem,另外 20% 的情况下返回 null,因此 VisualState 不会在相关 ListBoxItem 上设置。
我还尝试过通过 XAML 中的数据触发器设置状态,但这同样不能 100% 有效。有谁知道我如何轻松地从绑定对象中获取 ListBoxItem 以便触发我需要的 VisualState ?
绑定到 ListBox 中的列表的项目是从不同的页面添加的 - 下载页面此时不可见,我想这就是为什么未找到 ListBoxItems 或状态未被触发的原因?
以下是样式中 ListBoxItem 控件模板的 XAML:
<Style x:Key="PrimaryDownloadsListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem" >
<Grid Margin="0" Height="71">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="5"/>
<RowDefinition Height="52"/>
<RowDefinition Height="5"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="184"/>
<ColumnDefinition Width="116"/>
<ColumnDefinition Width="220"/>
<ColumnDefinition Width="67"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0" To="#FF191919" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="fillColor2" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="LayoutStates">
<VisualState x:Name="AfterLoaded"/>
<VisualState x:Name="BeforeLoaded"/>
<VisualState x:Name="BeforeUnloaded"/>
</VisualStateGroup>
<VisualStateGroup x:Name="DownloadStates">
<VisualState x:Name="DownloadInProgress">
...
这是我在后面的代码中使用的代码来尝试设置状态:
private void SetDownloadState(object[] itemDetails)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var item = itemDetails;
if (item != null)
{
string state = (string)item[0];
VideoItem vi = (VideoItem)item[1];
if (DownloadsListBox.ItemContainerGenerator != null)
{
DownloadsListBox.ScrollIntoView(DownloadsListBox.Items[0]);
foreach (var videoitem in
DownloadsListBox.Items.Where(videoitem => videoitem == vi))
{
DownloadsListBox.ScrollIntoView(videoitem);
}
var listBoxItem = DownloadsListBox.ItemContainerGenerator.ContainerFromItem(vi) as ListBoxItem;
//show the status state on this item.
if (listBoxItem != null)
{
if (state.ToUpper() == "DOWNLOADING")
{
bool success = VisualStateManager.GoToState(listBoxItem, "DownloadInProgress", true);
if (!success)
{
Debug.WriteLine("Error changing state in DownloadsView to Downloading!");
}
}
else if (state.ToUpper() == "COMPLETED")
{
bool success = VisualStateManager.GoToState(listBoxItem, "DownloadComplete",
true);
if (!success)
{
Debug.WriteLine("Error changing state in DownloadsView to completed!");
}
}
}
else
{
Debug.WriteLine("listBoxItem is null");
}
}
}
});
}
我确实尝试将触发器添加到样式中的 ContentTemplate 中,但它没有 100% 工作时间:
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding DownloadState}" Value="Downloading">
<ei:GoToStateAction StateName="Focused"/>
<ei:GoToStateAction StateName="DownloadInProgress"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding DownloadState}" Value="DownloadComplete">
<ei:GoToStateAction StateName="Focused"/>
<ei:GoToStateAction StateName="DownloadComplete"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
I have a ListBox in a file called Downloads.XAML. The items bound to that ListBox come from a bound list in my ViewModel. I have states that are defined in the ListBoxItem style in a different XAML control which need to be fired depending on a property set on the bound item. The problem I have is that I cannot get the ListBoxItem from the ListBox, as the .SelectedItem refers to the actual bound object rather than the ListBox. I have tried using the ListBox.ItemContainerGenerator but this only returns a ListItem about 80% of the time, the other 20% of the time this returns null and therefore the VisualState does not get set on the relevant ListBoxItem.
I've also tried setting the states through datatriggers in the XAML but again this doesnt work 100% of the time. Does anyone know how I can easily get the ListBoxItem from the bound object in order to fire off the VisualState on it that I require?
The items that are bound to the list in the ListBox are added from a different page - the downloads page is not visible at this time and I am thinking that this is why the ListBoxItems arent being found or why the states arent being fired?
Here is the XAML of the ListBoxItem control template within the style:
<Style x:Key="PrimaryDownloadsListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem" >
<Grid Margin="0" Height="71">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="5"/>
<RowDefinition Height="52"/>
<RowDefinition Height="5"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="184"/>
<ColumnDefinition Width="116"/>
<ColumnDefinition Width="220"/>
<ColumnDefinition Width="67"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0" To="#FF191919" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="fillColor2" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="LayoutStates">
<VisualState x:Name="AfterLoaded"/>
<VisualState x:Name="BeforeLoaded"/>
<VisualState x:Name="BeforeUnloaded"/>
</VisualStateGroup>
<VisualStateGroup x:Name="DownloadStates">
<VisualState x:Name="DownloadInProgress">
...
Here is the code I'm using in the code behind to attempt to set the state:
private void SetDownloadState(object[] itemDetails)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var item = itemDetails;
if (item != null)
{
string state = (string)item[0];
VideoItem vi = (VideoItem)item[1];
if (DownloadsListBox.ItemContainerGenerator != null)
{
DownloadsListBox.ScrollIntoView(DownloadsListBox.Items[0]);
foreach (var videoitem in
DownloadsListBox.Items.Where(videoitem => videoitem == vi))
{
DownloadsListBox.ScrollIntoView(videoitem);
}
var listBoxItem = DownloadsListBox.ItemContainerGenerator.ContainerFromItem(vi) as ListBoxItem;
//show the status state on this item.
if (listBoxItem != null)
{
if (state.ToUpper() == "DOWNLOADING")
{
bool success = VisualStateManager.GoToState(listBoxItem, "DownloadInProgress", true);
if (!success)
{
Debug.WriteLine("Error changing state in DownloadsView to Downloading!");
}
}
else if (state.ToUpper() == "COMPLETED")
{
bool success = VisualStateManager.GoToState(listBoxItem, "DownloadComplete",
true);
if (!success)
{
Debug.WriteLine("Error changing state in DownloadsView to completed!");
}
}
}
else
{
Debug.WriteLine("listBoxItem is null");
}
}
}
});
}
I did try adding the triggers into the ContentTemplate within the style but it didnt work 100% of the time:
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding DownloadState}" Value="Downloading">
<ei:GoToStateAction StateName="Focused"/>
<ei:GoToStateAction StateName="DownloadInProgress"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding DownloadState}" Value="DownloadComplete">
<ei:GoToStateAction StateName="Focused"/>
<ei:GoToStateAction StateName="DownloadComplete"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我设法通过将视图模型中的属性设置为 ContentTemplate 中网格的 templatedParent 来获取 ListBoxItem,然后引用 listboxitem:
希望这有时会对某人有所帮助:)
I managed to get hold of the ListBoxItem by setting a property in my view model to the templatedParent of the grid within the ContentTemplate which then referred to the listboxitem:
Hope this will help someone sometime :)