如何设置用户控件的数据上下文

7

我正在使用MVVM工具包编写WPF应用程序,并且在连接视图模型和视图方面遇到了问题。

该模型是使用ado.net实体框架创建的。

视图模型:

public class CustomerViewModel
    {
        private Models.Customer customer;
        //constructor
        private ObservableCollection<Models.Customer> _customer = new ObservableCollection<Models.Customer>();
        public ObservableCollection<Models.Customer> AllCustomers
        {
            get { return _customer; }

        }
        private Models.Customer _selectedItem;
        public Models.Customer SelectedItem
        {
            get { return _selectedItem; }

        }
        public void LoadCustomers()
        {
            List<Models.Customer> list = DataAccessLayer.getcustomers();
            foreach (Models.Customer customer in list)
            {
                this._customer.Add(customer);
            }
            }
        }

视图(目前没有代码):

<UserControl x:Class="Customers.Customer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:vm ="clr-namespace:Customers.ViewModels"
             d:DesignHeight="300" d:DesignWidth="300"
             xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit" >

    <Grid>
        <toolkit:DataGrid ItemsSource="{Binding AllCustomers}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" AutoGenerateColumns="True">

        </toolkit:DataGrid>

        <toolkit:DataGrid ItemsSource="{Binding SelectedItem.Orders}">

        </toolkit:DataGrid>



    </Grid>
</UserControl>

数据访问层类:

 class DataAccessLayer
    {
        public List<Customer> customers = new List<Customer>();

        public static List<Customer> getcustomers()
        {
            entities db = new entities();

            var customers = from c in db.Customer.Include("Orders")
                           select c;
            return customers.ToList();
        }


    }

问题在于没有设置数据上下文,因此没有显示任何数据。我尝试在代码后台中解决这个问题,但是没有成功。无论如何,我更喜欢在xaml文件中解决它。另一个问题是SelectedItem绑定的问题 - 代码从未使用。


在你的代码中,你的视图模型有一个AllCustomers属性,但你正在绑定到Customers。 - mak
我正在稍微清理代码,结果打错了一个字。实际上工作代码中的绑定当然是正确的。 - EVA
3个回答

5

因为这里使用了MVVM范例,所以我会在View的构造函数中实例化您的ViewModel。我的View/ViewModel通常遵循以下事件顺序:

  1. 实例化View
  2. View构造函数实例化ViewModel
  3. ViewModel初始化
  4. ViewModel运行数据获取程序(单独线程)
  5. ViewModel调用OnPropertyChanged("")来提醒View有些内容已经改变;检查所有内容

我的ViewModel是从XAML代码后台实例化的(抱歉,这是VB.NET,还没有足够信任自己学习C#):

Public Sub New()
    MyBase.New()
    Me.DataContext = New EditShipmentViewModel(Me) 'pass the view in to set as a View variable
    Me.InitializeComponent()
End Sub

起初我希望有像这样的东西:

<UserControl>
    <UserControl.DataContext>
        <Local:EditShipmentViewModel>
    </UserControl.DataContext>
</UserControl>

但事情并没有按照我想象的那样进行。
希望这能有所帮助。

1
这是MVVM的相反。 - 15ee8f99-57ff-4f92-890c-b56153
@EdPlunkett,欢迎您发表回答。 - CodeWarrior
1
这是WPF中最常见的反模式之一。你不应该鼓励初学者使用会给他们带来麻烦和挫败感的反模式。 - 15ee8f99-57ff-4f92-890c-b56153

0

我按照观察Blend4的方式设置了我的视图模型数据上下文。也就是说,如果我的视图模型叫做 MainViewModel,那么在视图中引用它的方式如下:

 <UserControl.Resources>
    <local:MainViewModel x:Key="MainViewModelDataSource" />
 </UserControl.Resources>

 <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}">
    ...view xaml stuff

 </Grid>

此外,如果您正在构造函数中从数据库加载数据到您的视图模型中,请不要忘记添加一个辅助方法,例如:
  if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
        {
             myCollection = new ObservableCollection<Blah>(something);
        }

为了避免Visual Studio/Blend4在设计器中尝试从数据库连接检索数据时崩溃。我个人经常在构造函数中加载数据,只是因为我需要它,并且希望从启动时缓存在内存中。 :)

0

这是您要找的this吗?


是的和不是。我需要以某种方式调用getcustomers()方法。我应该在viewmodel构造函数中这样做吗?似乎不太好。 - EVA
就我个人而言,我会让ViewModel在构造函数中调用getcustomers()方法。您认为这种方式有什么不好的地方吗?当视图渲染时,它将创建ViewModel的新实例,并且此时希望检索数据,因此构造函数执行此操作是有意义的。 - Chris Nicol
在资源中的XAML构造<UserControl.DataContext>是什么意思? - EVA
2
在XAML中声明对象的唯一主要问题是,如果VM构建期间抛出任何错误,都将被XAML解析错误吞噬。我们已经转而使用像MEF这样的DI,在Load时将VM注入到View的DataContext中。OnLoad可能会多次触发,因此请确保您使用_isLoaded字段或类似内容来短路它。 - Agies
是的,使用依赖注入(DI)肯定是更好的解决方案。很好的评论! - Chris Nicol

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