我遇到了一个我认为可能是WPF错误的问题。我有一个WPF视图,它包含两个ItemsControl元素,它们的ItemsSource属性都绑定到ViewModel上的相同ObservableCollection属性。我看到的行为是,只有在XAML中最后声明的ItemsControl才能正确呈现其内容。
我的观点包含以下内容:
<Window x:Class="ItemsControlBindingTest.Views.ItemsControlBindingTestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ItemsControlBindingTestView" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" TextWrapping="Wrap">
Note that of the two ItemsControls in this view, only the last declared in XAML renders its contents properly.
</TextBlock>
<ItemsControl Grid.Column="0" Grid.Row="1" ItemsSource="{Binding ButtonList}"/>
<ItemsControl Grid.Column="1" Grid.Row="1" ItemsSource="{Binding ButtonList}"/>
</Grid>
</Window>
你可以看到我只是得到一个带有文本块和两个ItemsControls的网格 - 每个都绑定到ViewModel上的相同属性。
View的代码隐藏非常简单: 使用System.Windows; 使用ItemsControlBindingTest.ViewModels;
namespace ItemsControlBindingTest.Views
{
/// <summary>
/// Interaction logic for ItemsControlBindingTestView.xaml
/// </summary>
public partial class ItemsControlBindingTestView : Window
{
public ItemsControlBindingTestView()
{
InitializeComponent();
DataContext = new ItemsControlBindingTestViewModel();
}
}
}
您可以看到我只是将View的DataContext属性设置为ItemsControlBindingTestViewModel的新实例。
ItemsControlBindingTestViewModel的代码:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace ItemsControlBindingTest.ViewModels
{
public class ItemsControlBindingTestViewModel : MVVM.ViewModelBase
{
private ObservableCollection<Button> buttonList;
public ObservableCollection<Button> ButtonList
{
get { return buttonList; }
set
{
buttonList = value;
NotifyPropertyChanged(() => ButtonList);
}
}
public ItemsControlBindingTestViewModel()
{
List<Button> tempList = new List<Button>();
tempList.Add(new Button { Content = "Button 1" });
tempList.Add(new Button { Content = "Button 2" });
tempList.Add(new Button { Content = "Button 3" });
ButtonList = new ObservableCollection<Button>(tempList);
}
}
}
我们在这里所做的就是在ButtonList属性中添加三个按钮。
现在,当我显示这个视图时,我只看到在XAML中最后声明的ItemsControl中呈现的按钮:
如果我在两个ItemsControls上切换指定的列值:
<ItemsControl Grid.Column="1" Grid.Row="1" ItemsSource="{Binding ButtonList}"/>
<ItemsControl Grid.Column="0" Grid.Row="1" ItemsSource="{Binding ButtonList}"/>
然后第0列中的ItemsControl(左侧)将呈现内容,右侧的ItemsControl将保持空白:
查看Snoop中的元素会产生一些有趣的信息:
ItemsControls的ItemsSource属性都正确地反映了对ButtonList的绑定。
事实上,我可以深入研究这个ItemsSource并查看ButtonList中的三个按钮:
最后声明的ItemsControl还显示了一个有效的ItemsSource属性:
我也可以深入研究这个ItemsSource,看看ButtonList中的三个按钮:
Snoop还显示了最后声明的ItemsControl的StackPanel有子节点:
虽然第一次声明的ItemsControl的StackPanel没有子节点:
我在这里遗漏了什么,或者这是WPF中的错误?
答案 0 :(得分:3)
如果Button
为System.Windows.Controls.Button
,则行为正常。 Button
(与任何其他视觉)一样,只能有一个父母。当最后一项控件呈现一个项目(按钮)时,该项目与前一个父项分离并附加到下一个项目。
事实上,这是一个非常糟糕的主意 - 在视图模型中使用视觉效果
此外,在这种情况下,视图模型的概念失去了意义。如果要在视图中生成按钮,请为项目控件设置适当的项目模板,并在视图模型的ObservableCollection
中保留数据项(不是视觉效果!)。