更新:我重新审视了这个并找到了一个更好的解决方案。我原来的那个还在下面,但我最终解决这个问题的方法是通过在 ControlTemplate 中使用 ViewGrid 而不是 DataTemplate。然后可以使用RelativeSource TemplatedParent 绑定绑定到ListBox 的IsSelected 属性。因此,将以下内容添加到列表框或页面或用户控件的资源中:
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<StackPanel>
<ViewGrid IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
<!-- other controls may go here -->
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
原来的:
所以七年后,你几乎肯定不再需要这个问题的答案了……然而,我最近花了一个上午的时间来解决这个问题,并认为我会给出我的解决方案,以防万一出现类似的不幸事件。
首先,任何使用 Silverlight 5 的人都很幸运,因为 AncestorType 现在显然可用于 RelativeSource,让您可以直接绑定到 ListBoxItem 的 IsSelected 属性。对于我们这些坚持 4 或以下的人来说,我想出的唯一真正的解决方法是通过使用背后代码中的事件来“伪造”绑定。
为此,假设您的 YourView XAML 带有一个名为“lbYourListBox”的 ListBox,它的 ItemsSource 和 SelectedItem 属性绑定到 YourViewModel 类的适当属性,以及其 ItemTemplate 中的 ViewGrid,其 IsChecked 属性未绑定到任何东西。然后,在文件后面的代码中,按如下方式连接事件:
public YourView()
{
InitializeComponent();
this.Loaded += (sender, e) =>
{
((YourViewModel)this.DataContext).PropertyChanged += vm_PropertyChanged;
UpdateViewGrids();
};
}
// this part propagates changes from the view to the view model
private void viewGrid_Checked(object sender, RoutedEventArgs e)
{
var selectedVM = ((ViewGrid)sender).DataContext as SourceItemType;
((YourViewModel)this.DataContext).SelectedViewGridItem = selectedVM;
}
private void vm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, "SelectedViewGridItem"))
{
UpdateViewGrids();
}
}
// this part propagates changes from the view model to the view
private void UpdateViewGrids()
{
var viewGrids = this.lbYourListBox.GetVisualDescendants().OfType<ViewGrid>();
var selectedVM = ((YourViewModel)this.DataContext).SelectedViewGridItem;
foreach (var grid in viewGrids)
{
grid.IsChecked = selectedVM == grid.DataContext;
}
}
viewGrid_Checked 事件处理程序应连接到 ItemTemplate 中视图网格的 Checked 事件。GetVisualDescendants() 方法来自 Silverlight 工具包。
重要警告:
- ViewGrid.Checked 事件不应触发,除非是 unchecked->checked 转换,并且一次只能选择一个视图网格。如果这两件事不正确,则必须进行适当的编辑以确保此代码不会导致无限的事件驱动循环。(当然,如果您不需要双向绑定,则只需要其中一个事件处理程序,而事件 ping-pong 不是问题。)
- 我为在 XAML 中设置了数据上下文的用户控件编写了这个,这就是为什么视图模型的 PropertyChanged 事件的事件处理程序仅在视图加载后分配的原因。根据您的视图和视图模型相互绑定的方式和时间,您可能必须更早/更晚/不同地分配它。
- 如果视图网格不可见,这将不起作用,GetVisualDescendants 似乎忽略了隐藏/折叠的控件。