我需要使用WPF MVVM将List绑定到UniformGrid到WP窗口。
我记得做过这样的事情:进入我的虚拟机:
private List<Rat> rats;
private UniformGrid uniformGrid;
public List<Rat> Rats
{
get { return rats; }
set
{
if (rats != value)
{
//update local list value
rats = value;
//create View UniformGrid
if (uniformGrid == null)
uniformGrid = new UniformGrid() { Rows=10};
else
uniformGrid.Children.Clear();
foreach(Rat rat in value)
{
StackPanel stackPanel = new StackPanel();
Ellipse ellipse = new Ellipse(){Height=20, Width=20, Stroke= Brushes.Black};
if (rat.Sex== SexEnum.Female)
ellipse.Fill= Brushes.Pink;
else
ellipse.Fill= Brushes.Blue;
stackPanel.Children.Add(ellipse );
TextBlock textBlock = new TextBlock();
textBlock.Text= rat.Name + " (" + rat.Age +")";
stackPanel.Children.Add( textBlock );
uniformGrid.Children.Add(stackPanel);
}
OnPropertyChanged("Rats");
}
}
}
当需要通过事件将列表刷新到视图中时,会正确通知VM。 所以此时我需要将我的View正确绑定到VM。我是这样做的:
<GroupBox x:Name="GB_Rats" Content="{Binding Rats}" Header="Rats" HorizontalAlignment="Left" Height="194" Margin="29,10,0,0" VerticalAlignment="Top" Width="303">
这是正确的全球性问题吗?
具体地说,在尝试运行代码时,此行无法执行:
uniformGrid = new UniformGrid() { Rows=10};
- &GT;
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationCore.dll
Additional information: The calling thread must be STA, because many UI components require this.
这让我觉得我不应该从MVVM的角度出发这样做。
感谢您的帮助。
答案 0 :(得分:4)
ViewModel
不应该实例化任何UI控件,这应该是View的责任。
因此,在您的代码中,您不应该尝试创建StackPanels
,Ellipses
等。
还尝试使用已有更改通知的类型 - 而不是
List<T>
使用ObservableCollection<T>
MSDN,我建议在值改变时替换整个列表。
在MVVM模式中执行此操作的正确方法是为DataTemplate
创建Rat
,如下所示:
视图模型:
public class MainWindowViewModel
{
public ObservableCollection<Rat> Rats { get; set; } =
new ObservableCollection<Rat>()
{
new Rat()
{
Name = "Fred",
Age = "19",
Sex = SexEnum.Male
},
new Rat()
{
Name = "Martha",
Age = "21",
Sex = SexEnum.Female
}
};
}
模特 - 鼠与性:
public class Rat
{
public SexEnum Sex { get; set; }
public string Name { get; set; }
public string Age { get; set; }
}
public enum SexEnum
{
Female,
Male
}
如果您想以两种颜色之一呈现性别的模型值,则应使用IValueConverter
:
[ValueConversion(typeof(SexEnum), typeof(Brush))]
public class SexToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is SexEnum))
throw new ArgumentException("value not of type StateValue");
SexEnum sv = (SexEnum)value;
//sanity checks
if (sv == SexEnum.Female)
return Brushes.Red;
return Brushes.Blue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然后在您的窗口中使用它:
窗口:
<Window x:Class="WpfApplication1.MainWindow"
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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
xmlns:ViewModel="WpfApplication1.VM"
xmlns:Converters ="clr-namespace:WpfApplication1.Converters"
>
<Grid>
<Grid.Resources>
<Converters:SexToColorConverter x:Key="SexToBrushConverter"></Converters:SexToColorConverter>
</Grid.Resources>
<ComboBox x:Name="comboBox" ItemsSource="{Binding Rats}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="120">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Fill="{Binding Sex, Converter={StaticResource SexToBrushConverter}}"></Ellipse>
<TextBlock Margin="5" Text="{Binding Name}"></TextBlock>
<TextBlock Margin="5" Text="{Binding Age}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
请注意分配给DataTemplate
属性的ComboBox.ItemTemplate
和Converters:SexToColorConverter
的声明及其用于更改Fill
绑定中椭圆颜色的用法。
更新4.4.2016 16:30
使用GroupBox
和CheckBoxes
的窗口显示列表
<Window x:Class="WpfApplication1.MainWindow"
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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
xmlns:Converters ="clr-namespace:WpfApplication1.Converters"
xmlns:vm="clr-namespace:WpfApplication1.VM">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.Resources>
<Converters:SexToColorConverter x:Key="SexToBrushConverter"></Converters:SexToColorConverter>
</Grid.Resources>
<GroupBox>
<ItemsControl ItemsSource="{Binding Rats}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Margin="5"></CheckBox>
<Ellipse Width="10" Height="10" Fill="{Binding Sex, Converter={StaticResource SexToBrushConverter}}"></Ellipse>
<TextBlock Margin="5" Text="{Binding Name}"></TextBlock>
<TextBlock Margin="5" Text="{Binding Age}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</Grid>
</Window>
我想目标也是选择Rats,这取决于您想要成为MVVM纯粹主义者的方式,并添加一个RatViewModels
列表,它具有bool IsChecked属性并绑定{{1转到ItemsSource
并将此列表与模型ObservableCollection<RatViewModel>