将WPF ComboBox绑定到我的ViewModel

7

我正在尝试将我的ViewModel与ComboBox绑定。 我定义的ViewModel类如下:

class ViewModel
{
    public ViewModel()
    {
        this.Car= "VW";
    }

    public string Car{ get; set; }
}

我在Window_Load中将这个ViewModel设置为DataContext:

   private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        this.DataContext = new CarModel();
    }

然后在我的XAML中,我使用以下代码将我的ComboBox绑定到这个ViewModel。我希望“VW”默认显示为我的ComboBox中的选中项:

        <ComboBox Name="cbCar" SelectedItem="{Binding Car, UpdateSourceTrigger=PropertyChanged}">
            <ComboBoxItem Tag="Mazda">Mazda</ComboBoxItem>
            <ComboBoxItem Tag="VW">VW</ComboBoxItem>
            <ComboBoxItem Tag="Audi">Audi</ComboBoxItem>
        </ComboBox>

我有两个问题:

  1. 如何将下拉框的默认值设置为“VW”(表单加载后,应在下拉框中显示“VW”)。
  2. 不是像上面在xaml中设置ComboBoxItems,而是如何在我的ViewModel中设置它们,然后在ComboBox中加载这些内容?

谢谢。

更新: 到目前为止,我已经实现了这一点,但是在ViewModel c-tor中出现了以下错误:

namespace MyData
{
    class ViewModel
    {
        public ViewModel()
        {
            this.Make = "";
            this.Year = 1;

            this.DefaultCar = "VW"; //this is where I get error 'No string allowed'
        }

        public IEnumerable<Car> Cars
        {
            get
            {
                var cars = new Car[] { new Car{Model="Mazda"}, new Car{Model="VW"}, new Car{Model="Audi"} };
                DefaultCar = cars.FirstOrDefault(car => car.Model == "VW"); 
            }
        }

        public string Make { get; set; }
        public int Year { get; set; }
        public Car DefaultCar { get; set; }
    }

    class Car
    {
        public string Model { get; set; }
    }
}

根据您的新更新,Make和Year属性应该存在于Car类中,而不是ViewModel中。DefaultCar应该分配给一个Car实例,而不是一个字符串,并从您的ViewModel构造函数中删除它。 - E-Bat
@E-Bat,你是说ViewModel类只应该保存IEnumerable<Car> Cars,其他所有的东西都应该移到Car类中吗?还是我应该把它从ViewModel的构造函数中移除?在这种情况下,如何将DefaultCar分配给车辆实例?难道它不是已经使用上面的表达式分配了吗?能否请您展示一下这个更改? - pixel
1
在这种情况下,ViewModel 应该只包含 Cars 和 DefaultCar 属性。可以在从数据存储获取 Cars 的那一刻分配 DefaultCar。当您的视图加载并尝试解析其绑定时,它们将从您的视图查询 Cars 属性(由 ComboBox 的 ItemsSource 属性触发)。然后在此时,您可以在返回列表之前分配 DefaultCar。请参见我的下面的答案。 - E-Bat
1
顺便提一下,应该是SelectedCar而不是DefaultCar,因为这是ComboBox的SelectedItem绑定中定义的方式。 - E-Bat
@E-Bat 我觉得我的问题有误导性。所以我将其标记为已回答,并在http://stackoverflow.com/questions/34326241/wpf-how-to-bind-combobox上创建了一个新的问题。它更好地解释了我想要实现的内容。 - pixel
要接受答案并关闭问题,您必须检查我向您发布代码的下面的帖子。谢谢。 - E-Bat
2个回答

12

如果你要实现 MVVM,那最好从对象的角度考虑如何在应用程序中代表汽车:

    public class ViewModel
    {    
            public Car SelectedCar{ get; set; }
            public ObservableCollection<Car> Cars{ 
                get  {
                    var cars = new ObservableCollection(YOUR_DATA_STORE.Cars.ToList());
                    SelectedCar = cars.FirstOrDefault(car=>car.Model == "VW");
                    return cars;
                }
            }
    }
    
    public class Car 
    {
        public string Model {get;set;}
        public string Make { get; set; }
        public int Year { get; set; }
    }

你的Xaml:

<ComboBox SelectedItem="{Binding SelectedCar}", ItemsSource="{Binding Cars}" 
        UpdateSourceTrigger=PropertyChanged}"/>

谢谢。你会如何重写这行代码呢?我不明白它的作用,因为 "car" 没有被定义:"SelectedCar = cars.FirstOrDefault(car=>car.Model == "VW");"?谢谢。 - pixel
1
@dbnex14,变量car代表汽车集合中的每个项,无需预先定义。请查看语法car=>。这是lambda表达式。 - E-Bat
1
@dbnex14请查看有关Lambda表达式的此问题:https://dev59.com/ilHTa4cB1Zd3GeqPP0B8 - E-Bat
我添加了更新。它在那行上出错了。谢谢。 - pixel
1
@dbnex14:您正在定义一个字符串数组,它应该是一个Car数组;)变量cars = new Car[] { new Car { Model = "Mazda" }, new Car { Model ="VW" }, new Car {Model = "Audi" }, new Car { Model = "BMW" }, new Car { Model = "Honda" } }; - E-Bat
是的,我是新手,觉得语法很难理解。我已经更新了我的问题并反映了这一点,但现在根据更新中的评论,我又遇到了另一个错误。非常感谢 E-Bat。 - pixel

5
  1. 默认值: 如果你设置 viewModel.Car = "VW",那么它应该自动选择下拉框中的那个项目。 为了使其正常工作,您需要实现INotifyPropertyChanged或在设置DataContext之前先设置Car

INotifyPropertyChanged实现可能如下:

class ViewModel : INotifyPropertyChanged
{
    private string car;

    public ViewModel()
    {
        this.Car = "VW";
        this.Cars = new ObservableCollection<string>() { "Mazda", "VW", "Audi" };
    }

    public string Car
    {
        get
        {
            return this.car;
        }
        set
        {
            if (this.car != value)
            {
                this.car = value;
                OnPropertyChanged();
            }
        }
    }

    public ObservableCollection<string> Cars { get; }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 绑定 ItemsSourceSelectedItem

<ComboBox ItemsSource="{Binding Cars}"
          SelectedItem="{Binding Car, Mode=TwoWay}">
</ComboBox>

如果您的数据源或视图不仅仅是显示字符串,您也可以设置ComboBox.ItemTemplate

    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>

在视图模型中,只需添加一个列表属性:
public ObservableCollection<string> Cars { get; set; }

它不一定要是ObservableCollection,但这种类型会在您更改集合时自动更新UI。


你在哪里设置汽车的值为“Mazda”,“VW”和“Audi”?谢谢。 - pixel
我正在使用我的模型在表单上设置一些默认值。很抱歉,我是WPF的新手,只是想澄清为什么我需要这个。所以,当表单打开时,这些值应该默认显示在我的控件中(在此示例中为汽车组合框,但我已经发布了更新,涉及到其他几个控件)。 - pixel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接