我试图找到这个问题的答案(列表框项目根据视图模型属性显示复选框或单选按钮),我遇到了一个非常奇怪的行为。
如果我使用
DataTrigger
设置列表框中某个项目的 ContentControl
中 DataTemplate
的内容,则只有最后一项似乎可以正常工作。
为什么只有最后一项有复选框/单选按钮?
这是 XAML,其中窗口视图模型属性
AllowMultiItem
定义我是否需要复选框或单选按钮:
<Window x:Class="TestSpace.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestSpace"
mc:Ignorable="d"
Title="MyWindow" Height="450" Width="800">
<Grid>
<!-- the list box -->
<ListBox x:Name="MyList" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyListItem}">
<StackPanel Orientation="Horizontal">
<!-- this content control selects between check and radio-->
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<!-- trigger that creates checkbox -->
<DataTrigger Value="True"
Binding="{Binding Path=DataContext.AllowMultiItem, ElementName=MyList}">
<Setter Property="Content">
<Setter.Value>
<CheckBox IsChecked="{Binding IsChecked}"/>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- trigger that creates radio button -->
<DataTrigger Value="False"
Binding="{Binding Path=DataContext.AllowMultiItem, ElementName=MyList}">
<Setter Property="Content">
<Setter.Value>
<RadioButton IsChecked="{Binding IsChecked}" GroupName="RadioGroup"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- item text -->
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
当我这样做时,无论
AllowMultiItem
为真或假,只有列表框中的最后一项收到复选框/单选按钮。
为什么会出现这种情况?我该如何解决?
其他代码 - 可能不相关
不确定它是否有用,但是如果您想检查窗口视图模型和项目视图模型,或者如果您想完全重现我的测试,代码如下。我尽可能简单地进行了此测试,因此没有不相关的错误影响结果。
窗口视图模型(带有静态打开窗口的函数):
// didn't feel the need for INotifyPropertyChanged
// since nothing here is supposed to change
public class MyViewModel
{
public List<MyListItem> Items { get; private set; }
public bool AllowMultiItem { get; private set; }
public MyViewModel(List<MyListItem> items, bool allowMultiItem)
{
Items = items;
AllowMultiItem = allowMultiItem;
}
public static void ShowWindow(bool allowMultiItem)
{
// just creating items with names from 1 to 10
List<MyListItem> items = Enumerable.Range(1, 10)
.Select(index=> new MyListItem("Item " + index.ToString()))
.ToList();
// create view model
MyViewModel vm = new MyViewModel(items, allowMultiItem);
// create and show window
MyWindow window = new MyWindow();
window.DataContext = vm;
window.ShowDialog();
}
}
项目视图模型:
public class MyListItem : INotifyPropertyChanged
{
private bool _checked;
private string _text;
public event PropertyChangedEventHandler PropertyChanged;
public string Text { get { return _text; } set { SetText(value); } }
public bool IsChecked { get { return _checked; } set { SetChecked(value); } }
public MyListItem(string text)
{
SetText(text);
SetChecked(false);
}
private void SetChecked(bool value)
{
if (value == _checked) return;
_checked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
}
private void SetText(string value)
{
if (value == _text)
return;
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
}
测试用例只需调用
MyViewModel.ShowWindow(true)
和 MyViewModel.ShowWindow(false)
我尝试过的一些事情
INotifyPropertyChanged
添加到 MyViewModel
并在 AllowMultiItem
之后将更改触发到 OnContentRendered
- 没有任何变化DataTrigger
绑定更改为针对窗口的RelativeSource
:奇怪的是,现在first项目获得了盒子,但只有第一个因为在 Content 中您编写了 RadioButton 或 CheckBox 元素的实例。并且一个实例只能显示在一个视觉位置(框架)。
这是正确的实现,能够切换到运行时。
// This is the namespace for my implementation of the ViewModelBase class.
// Since you will have your own implementation or use some kind of package,
// you need to change this `using` to the one that is relevant for you.
using Simplified;
using System.Collections.ObjectModel;
namespace Core2024.SO.Daniel_Möller.question78429551
{
public class MyViewModel : ViewModelBase
{
public ObservableCollection<MyListItem> Items { get; }
public bool AllowMultiItem { get => Get<bool>(); set => Set(value); }
public MyViewModel(IEnumerable<MyListItem> items, bool allowMultiItem)
{
Items = new(items);
AllowMultiItem = allowMultiItem;
}
public MyViewModel()
: this(Enumerable.Range(1, 10).Select(index => new MyListItem("Item " + index.ToString())),
true)
{ }
}
public class MyListItem : ViewModelBase
{
public string Text { get => Get<string>(); set => Set(value); }
public bool IsChecked { get => Get<bool>(); set => Set(value); }
public MyListItem(string text)
{
Text = text;
}
}
}
<Window x:Class="Core2024.SO.Daniel_Möller.question78429551.TemplateSwitcherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Core2024.SO.Daniel_Möller.question78429551"
mc:Ignorable="d"
Title="TemplateSwitcherWindow" Height="450" Width="800"
DataContext="{DynamicResource vm}">
<Window.Resources>
<local:MyViewModel x:Key="vm"/>
<DataTemplate x:Key="ListItem.Temlate.RadioButton"
DataType="{x:Type local:MyListItem}">
<RadioButton IsChecked="{Binding IsChecked}" GroupName="RadioGroup"
Content="{Binding Text}"/>
</DataTemplate>
<DataTemplate x:Key="ListItem.Temlate.CheckBox"
DataType="{x:Type local:MyListItem}">
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Text}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="AllowMultiItem" IsChecked="{Binding AllowMultiItem}"/>
<ListBox Grid.Row="1" ItemsSource="{Binding Items}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{DynamicResource ListItem.Temlate.RadioButton}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AllowMultiItem}"
Value="True">
<Setter Property="ItemTemplate" Value="{DynamicResource ListItem.Temlate.CheckBox}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
</Grid>
</Window>