WinRT XAML - 如何在列表视图中设置所选项目的样式?
WinRT XAML - how do I style a selected item in a list view?
在我的 WinRT 应用程序中,我想显示状态值列表并突出显示当前状态。显示列表时,它应该是只读的,因此不会与用户交互。尽管我使用的是 ListView,但我希望禁用任何选择功能。我认为禁用 ListView 可以解决这个问题。
但是现在在我后面的代码中有;
public IList<JobStatusItem> StatusList
{
get
{
var values = Enum.GetValues(typeof(JobStatus));
var selected = Status.ToString();
var i = 0;
var list = new List<JobStatusItem>();
foreach (var value in values)
{
i++;
var item = GetStatusDisplay(value.ToString());
list.Add(new JobStatusItem
{
Id = i,
Status = item,
Selected = value.ToString().Equals(selected)
});
}
return list;
}
}
对于我的XAML,我有
<ListView x:Name="ListStatus"
IsItemClickEnabled="False"
IsSwipeEnabled="False"
SelectionMode="Single"
ItemsSource="{Binding Path=AssignedJobs.SelectedDay.SelectedJob.StatusList, Mode=OneWay}"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=Selected, Mode=OneWay}"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Status}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
而当我 运行 时,选中状态的样式未设置为选中。为什么会这样,我该如何解决?
您代码中的绑定表达式 (<Setter Property="IsSelected" Value="{Binding Path=Selected, Mode=OneWay}"/>
) 将不起作用,因为上下文不在项目级别。
由于WinRT中缺少祖先绑定,很难使用纯绑定来实现你想要的;但是,检查 IsSelected
属性 代码隐藏非常简单。最后,如果你想要一个纯粹的 XAML 解决方案,你总是可以将下面的代码包装在 Behavior
.
中
您基本上想要订阅 ListView
的 ContainerContentChanging
事件并手动设置 ItemContainer
的 IsSelected
属性 以匹配 Selected
属性 您的模型 JobStatusItem
。像这样 -
private void OnListViewContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.ItemContainer != null && !args.InRecycleQueue && args.Phase == 0)
{
args.ItemContainer.IsSelected = ((JobStatusItem)args.Item).Selected;
另一种可能的解决方案
由于您想要一个带有突出显示选择的只读列表,因此最好通过将 SelectionMode
设置为 ListView
来完全禁用 ListView
上的任何 clicking/tapping 交互None
.
然后在 DataTemplate
中,用 Border
包裹 TextBlock
并在 [=18= 时给 Border
一个不同的 Background
] 属性 是 true
.
所以,我了解到您希望 禁用 ListView 中项目的手动选择,同时保持程序选择。
实现您正在尝试做的事情的最简单方法是将您的 ListView 定义为 SelectionMode="Single"
并将 SelectedItem 属性 绑定到您的 ViewModel 的相应 属性。要阻止任何手动交互,只需在其上放置一个带有 IsHittestVisible="True"
的网格即可阻止任何手动交互。像下面这样:
<Grid>
<ListView x:Name="ListStatus"
SelectionMode="Single"
ItemsSource="{Binding StatusList}"
SelectedItem="{Binding SelectedItem}"
ItemTemplate="{StaticResource UnselectedListDataTemplate}"/>
<Grid IsHitTestVisible="True" Background="Transparent" />
</Grid>
不过,我会选择 完全不同的方法。
更方便的方法是设置 ListView.SelectionMode="None"
并使用 ItemTemplateSelector
或 ItemContainerStyleSelector
在突出显示版本和正常版本之间切换 template/style版本.
public class JobStatusItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate UnselectedTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var element = item as JobStatusItem;
if (element == null) return UnselectedTemplate;
return element.Selected ? SelectedTemplate : UnselectedTemplate;
}
}
您的 ListView 定义为:
<Page.Resources>
<DataTemplate
x:DataType="local:JobStatusItem"
x:Key="SelectedListDataTemplate">
<TextBlock Text="{Binding Status}" FontWeight="ExtraBold" Foreground="DarkOrchid"/>
</DataTemplate>
<DataTemplate
x:DataType="local:JobStatusItem"
x:Key="UnselectedListDataTemplate">
<TextBlock Text="{Binding Status}" />
</DataTemplate>
<local:JobStatusItemTemplateSelector x:Key="ListTemplateSelector"
SelectedTemplate="{StaticResource SelectedListDataTemplate}"
UnselectedTemplate="{StaticResource UnselectedListDataTemplate}"/>
</Page.Resources>
<ListView x:Name="ListStatus"
SelectionMode="None"
ItemsSource="{Binding StatusList}"
ItemTemplateSelector="{StaticResource ListTemplateSelector}">
</ListView>
这将使您能够轻松区分已发生、正在进行和将在未来发生的状态步骤,甚至可以让您轻松地将过去发生的错误步骤等内容整合起来。
根据您使用的是 TemplateSelector
还是 StyleSelector
,您甚至可以通过不同的模板实现符号、图像等。
在我的 WinRT 应用程序中,我想显示状态值列表并突出显示当前状态。显示列表时,它应该是只读的,因此不会与用户交互。尽管我使用的是 ListView,但我希望禁用任何选择功能。我认为禁用 ListView 可以解决这个问题。 但是现在在我后面的代码中有;
public IList<JobStatusItem> StatusList
{
get
{
var values = Enum.GetValues(typeof(JobStatus));
var selected = Status.ToString();
var i = 0;
var list = new List<JobStatusItem>();
foreach (var value in values)
{
i++;
var item = GetStatusDisplay(value.ToString());
list.Add(new JobStatusItem
{
Id = i,
Status = item,
Selected = value.ToString().Equals(selected)
});
}
return list;
}
}
对于我的XAML,我有
<ListView x:Name="ListStatus"
IsItemClickEnabled="False"
IsSwipeEnabled="False"
SelectionMode="Single"
ItemsSource="{Binding Path=AssignedJobs.SelectedDay.SelectedJob.StatusList, Mode=OneWay}"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=Selected, Mode=OneWay}"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Status}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
而当我 运行 时,选中状态的样式未设置为选中。为什么会这样,我该如何解决?
您代码中的绑定表达式 (<Setter Property="IsSelected" Value="{Binding Path=Selected, Mode=OneWay}"/>
) 将不起作用,因为上下文不在项目级别。
由于WinRT中缺少祖先绑定,很难使用纯绑定来实现你想要的;但是,检查 IsSelected
属性 代码隐藏非常简单。最后,如果你想要一个纯粹的 XAML 解决方案,你总是可以将下面的代码包装在 Behavior
.
您基本上想要订阅 ListView
的 ContainerContentChanging
事件并手动设置 ItemContainer
的 IsSelected
属性 以匹配 Selected
属性 您的模型 JobStatusItem
。像这样 -
private void OnListViewContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.ItemContainer != null && !args.InRecycleQueue && args.Phase == 0)
{
args.ItemContainer.IsSelected = ((JobStatusItem)args.Item).Selected;
另一种可能的解决方案
由于您想要一个带有突出显示选择的只读列表,因此最好通过将 SelectionMode
设置为 ListView
来完全禁用 ListView
上的任何 clicking/tapping 交互None
.
然后在 DataTemplate
中,用 Border
包裹 TextBlock
并在 [=18= 时给 Border
一个不同的 Background
] 属性 是 true
.
所以,我了解到您希望 禁用 ListView 中项目的手动选择,同时保持程序选择。
实现您正在尝试做的事情的最简单方法是将您的 ListView 定义为SelectionMode="Single"
并将 SelectedItem 属性 绑定到您的 ViewModel 的相应 属性。要阻止任何手动交互,只需在其上放置一个带有 IsHittestVisible="True"
的网格即可阻止任何手动交互。像下面这样:
<Grid>
<ListView x:Name="ListStatus"
SelectionMode="Single"
ItemsSource="{Binding StatusList}"
SelectedItem="{Binding SelectedItem}"
ItemTemplate="{StaticResource UnselectedListDataTemplate}"/>
<Grid IsHitTestVisible="True" Background="Transparent" />
</Grid>
不过,我会选择 完全不同的方法。
更方便的方法是设置ListView.SelectionMode="None"
并使用 ItemTemplateSelector
或 ItemContainerStyleSelector
在突出显示版本和正常版本之间切换 template/style版本.
public class JobStatusItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate UnselectedTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var element = item as JobStatusItem;
if (element == null) return UnselectedTemplate;
return element.Selected ? SelectedTemplate : UnselectedTemplate;
}
}
您的 ListView 定义为:
<Page.Resources>
<DataTemplate
x:DataType="local:JobStatusItem"
x:Key="SelectedListDataTemplate">
<TextBlock Text="{Binding Status}" FontWeight="ExtraBold" Foreground="DarkOrchid"/>
</DataTemplate>
<DataTemplate
x:DataType="local:JobStatusItem"
x:Key="UnselectedListDataTemplate">
<TextBlock Text="{Binding Status}" />
</DataTemplate>
<local:JobStatusItemTemplateSelector x:Key="ListTemplateSelector"
SelectedTemplate="{StaticResource SelectedListDataTemplate}"
UnselectedTemplate="{StaticResource UnselectedListDataTemplate}"/>
</Page.Resources>
<ListView x:Name="ListStatus"
SelectionMode="None"
ItemsSource="{Binding StatusList}"
ItemTemplateSelector="{StaticResource ListTemplateSelector}">
</ListView>
这将使您能够轻松区分已发生、正在进行和将在未来发生的状态步骤,甚至可以让您轻松地将过去发生的错误步骤等内容整合起来。
根据您使用的是 TemplateSelector
还是 StyleSelector
,您甚至可以通过不同的模板实现符号、图像等。