如何正确地将View控件绑定到ViewModel列表(WPF MVVM)

时间:2016-04-03 20:56:22

标签: c# wpf xaml mvvm

我需要使用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的角度出发这样做。

感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

ViewModel不应该实例化任何UI控件,这应该是View的责任。

因此,在您的代码中,您不应该尝试创建StackPanelsEllipses等。

还尝试使用已有更改通知的类型 - 而不是 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.ItemTemplateConverters:SexToColorConverter的声明及其用于更改Fill绑定中椭圆颜色的用法。

更新4.4.2016 16:30 使用GroupBoxCheckBoxes的窗口显示列表

<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>

同步