【问题标题】:how to correctly bind a View control to a ViewModel List (WPF MVVM)如何正确地将视图控件绑定到 ViewModel 列表(WPF MVVM)
【发布时间】:2016-04-03 20:56:22
【问题描述】:

我需要使用 WPF MVVM 将列表绑定到 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 会得到正确的通知。 所以此时我需要将我的视图正确绑定到虚拟机。我是这样弄的:

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

->

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 的角度来看我不应该这样做。

感谢您的帮助。

【问题讨论】:

    标签: c# wpf xaml mvvm


    【解决方案1】:

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

    因此,您不应尝试在您的代码中创建 StackPanels、Ellipses 等。

    也尝试使用已经有更改通知的类型 - 代替 List&lt;T&gt; 使用 ObservableCollection&lt;T&gt; MSDN,我不建议在其值更改时替换整个列表。

    在 MVVM 模式中执行此操作的正确方法是为Rat 创建一个DataTemplate,如下所示:

    视图模型:

    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>
    

    注意分配给ComboBox.ItemTemplate 属性的DataTemplate 和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 属性并将 ItemsSource 绑定到 @987654343 @并将此列表与您的模型同步List&lt;Rat&gt;

    【讨论】:

    • 绝妙的答案。万分感谢。在我结束问题之前,您是否知道如何坚持使用 Groupbox 而不是切换到 ComboBox 或 ListBox?我需要使用 GroupBox 来保留应用程序的设计。但是 GroupBox 没有任何 ItemsSource 属性,没有 UniformGrid。
    • 抱歉错过了GroupBox - GroupBox 只是一个并非真正用于绑定的容器,您应该将自定义数量的项目绑定到ItemControl,例如ComboBox。您必须在此处编辑模板以包含复选框。你在那里追求的目标是什么?之后需要获取选定的老鼠吗?
    • 它就像一个魅力,感谢您的帮助。为我赢得了很多时间!
    猜你喜欢
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    相关资源
    最近更新 更多