2014-04-11 57 views
3

我有2个对象,一个Customer和一个Store。有多个商店位置,每个客户都有一个名为PreferredStoreId(int?)的属性,它与Store的Id(int)相关。发生WPF数据绑定问题

在一个WPF应用程序中,我试图构建一个允许编辑客户的表单。此窗体上存在一个组合框,其中充满了商店以充当显示当前设置的PreferredStore的方式以及更改首选商店的方式。我的问题是,虽然我可以填充组合框,但我无法获得Customer.PreferredId(设置为UserControl的datacontext的对象)和组合框的SelectedItem(Store对象)的.Id属性之间的双向绑定。

这里是我的XAML来帮助意义:

<UserControl x:Class="ucCustomerEditor" 
     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" 
     xmlns:localViewModels="clr-namespace:ViewModels" 
     xmlns:qc="clr-namespace:QuickConverter;assembly=QuickConverter" 
     mc:Ignorable="d" d:DesignWidth="750" Height="334"> 
<UserControl.DataContext> 
    <localViewModels:CustomerViewModel x:Name="customerViewModel" /> 
</UserControl.DataContext> 
<StackPanel> 
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> 
     <Button Height="26" Width="50" Content="Save" Margin="5,10" Click="UserAction_Save" /> 
     <Button Height="26" Width="50" Content="Cancel" Margin="10,10" Click="UserAction_Cancel" /> 
    </StackPanel> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBox Text="{Binding FirstName}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/> 
     <Label Content="First Name:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/> 
    </Grid> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBox Text="{Binding LastName}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/> 
     <Label Content="Last Name:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/> 
    </Grid> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBox Text="{Binding EmailAddress}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/> 
     <Label Content="Email Address:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/> 
    </Grid> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBox Text="{Binding PhoneNumber}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/> 
     <Label Content="Phone Number:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/> 
    </Grid> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <ComboBox Name="cbPreferredStore" 
        ItemsSource="{Binding Stores}" DisplayMemberPath="DisplayName" Height="23" Margin="10,0,0,0" VerticalAlignment="Top" 
        HorizontalAlignment="Stretch" Grid.Column="1" SelectedValue="{Binding ElementName=customerViewModel, Path=PreferredStoreId}"> 
      <ComboBox.DataContext> 
       <localViewModels:StoreListViewModel /> 
      </ComboBox.DataContext> 
     </ComboBox> 
     <Label Content="Preferred Store:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/> 
    </Grid> 
    <Grid Height="26" Margin="10" VerticalAlignment="Top"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="209"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBox Text="{Binding Password}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/> 
     <Label Content="Password:" Margin="10,0" VerticalAlignment="Top" Height="26" FontWeight="Bold"/> 
    </Grid> 
</StackPanel> 

StoreViewModel代码:

ublic class StoreViewModel : BaseViewModel 
{ 
    private enum Modes { CREATE, UPDATE } 
    private Modes _mode; 
    private Store _store; 

    public string DisplayName 
    { 
     get { return string.Format("{0} ({1})", this._store.LocationName, this._store.Id); } 
    } 

    public int Id 
    { 
     get { return this._store.Id; } 
     set 
     { 
      this._store.Id = value; 
      notifyPropertyChanged("Id"); 
      notifyPropertyChanged("DisplayName"); 
     } 
    } 
    public string LocationName 
    { 
     get { return this._store.LocationName; } 
     set 
     { 
      this._store.LocationName = value; 
      notifyPropertyChanged("LocationName"); 
      notifyPropertyChanged("DisplayName"); 
     } 
    } 
    public string ImageURL 
    { 
     get { return this._store.ImageURL; } 
     set 
     { 
      this._store.ImageURL = value; 
      notifyPropertyChanged("ImageURL"); 
     } 
    } 
    public string AddressLine1 
    { 
     get { return this._store.AddressLine1; } 
     set 
     { 
      this._store.AddressLine1 = value; 
      notifyPropertyChanged("AddressLine1"); 
     } 
    } 
    public string AddressLine2 
    { 
     get { return this._store.AddressLine2; } 
     set 
     { 
      this._store.AddressLine2 = value; 
      notifyPropertyChanged("AddressLine2"); 
     } 
    } 
    public string AddressLine3 
    { 
     get { return this._store.AddressLine3; } 
     set 
     { 
      this._store.AddressLine3 = value; 
      notifyPropertyChanged("AddressLine3"); 
     } 
    } 
    public string Suburb 
    { 
     get { return this._store.Suburb; } 
     set 
     { 
      this._store.Suburb = value; 
      notifyPropertyChanged("Suburb"); 
     } 
    } 
    public string State 
    { 
     get { return this._store.State; } 
     set 
     { 
      this._store.State = value; 
      notifyPropertyChanged("State"); 
     } 
    } 
    public string Postcode 
    { 
     get { return this._store.Postcode; } 
     set 
     { 
      this._store.Postcode = value; 
      notifyPropertyChanged("Postcode"); 
     } 
    } 
    public string Country 
    { 
     get { return this._store.Country; } 
     set 
     { 
      this._store.Country = value; 
      notifyPropertyChanged("Country"); 
     } 
    } 
    public string PhoneNumber 
    { 
     get { return this._store.PhoneNumber; } 
     set 
     { 
      this._store.PhoneNumber = value; 
      notifyPropertyChanged("PhoneNumber"); 
     } 
    } 
    public string EmailAddress 
    { 
     get { return this._store.EmailAddress; } 
     set 
     { 
      this._store.EmailAddress = value; 
      notifyPropertyChanged("EmailAddress"); 
     } 
    } 

    public static explicit operator StoreViewModel(EasyDayTea.Store store) 
    { 
     return new StoreViewModel(store) { _mode = Modes.UPDATE }; 
    } 

    public StoreViewModel() 
    { 
     _store = new Store(); 
     _mode = Modes.CREATE; 
    } 

    public StoreViewModel(Store store) 
    { 
     _store = store; 
     _mode = Modes.UPDATE; 
    } 

    public void Cancel() 
    { 
     if (_mode == Modes.CREATE) 
     { 
      _store = new Store() { }; 
     } 
     else 
     { 
      EasyDayTea.EasyDayTeaClient client = new EasyDayTeaClient(); 
      _store = client.FetchStore(App.AppUserTeaCredental, _store.Id); 
      client.Close(); 
     } 
     notifyAll(); 
    } 

    public void Save() 
    { 
     try 
     { 
      EasyDayTeaClient client = new EasyDayTeaClient(); 
      if (_mode == Modes.CREATE) 
      { 
       client.AddStore(App.AppUserTeaCredental, ImageURL, LocationName, AddressLine1, AddressLine2, AddressLine3, Suburb, State, Postcode, Country, PhoneNumber, EmailAddress); 
      } 
      else 
      { 
       client.SetStore(App.AppUserTeaCredental, Id, ImageURL, LocationName, AddressLine1, AddressLine2, AddressLine3, Suburb, State, Postcode, Country, PhoneNumber, EmailAddress); 
      } 
      client.Close(); 

      MessageBox.Show("Your customer was saved."); 
      if (_mode == Modes.CREATE) 
      { 
       _store = new Store(); 
       notifyAll(); 
      } 
      else 
      { 
       //do nothing. 
      } 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show("There was a problem saving your customer: \r\n" + ex.Message); 
     } 
    } 

    internal void notifyAll() 
    { 
     notifyPropertyChanged("Id"); 
     notifyPropertyChanged("LocationName"); 
     notifyPropertyChanged("ImageURL"); 
     notifyPropertyChanged("AddressLine1"); 
     notifyPropertyChanged("AddressLine2"); 
     notifyPropertyChanged("AddressLine3"); 
     notifyPropertyChanged("Suburb"); 
     notifyPropertyChanged("State"); 
     notifyPropertyChanged("Postcode"); 
     notifyPropertyChanged("Country"); 
     notifyPropertyChanged("PhoneNumber"); 
     notifyPropertyChanged("EmailAddress"); 
     notifyPropertyChanged("DisplayName"); 
    } 
} 

StoreListViewModel代码:

public class StoreListViewModel : BaseViewModel 
{ 
    private List<StoreViewModel> _stores; 

    public List<StoreViewModel> Stores 
    { 
     get { return this._stores; } 
     set 
     { 
      this._stores = value; 
      notifyPropertyChanged("Stores"); 
     } 
    } 

    public StoreListViewModel() 
    { 
     EasyDayTea.EasyDayTeaClient client = new EasyDayTea.EasyDayTeaClient(); 
     _stores = client.GetStores(App.AppUserTeaCredental).Select(s => (StoreViewModel)s).ToList(); 
     client.Close(); 
    } 
} 
+0

你能展示'Stores'属性的实现吗?你是否实现了'INotifyPropertyChanged'? –

+0

您是否尝试从绑定中删除ElementName = customerViewModel? – dnr3

+0

我已将StoreListViewModel和StoreViewModel添加到原始帖子中。 BaseViewModel实现INotifyPropertyChanged –

回答

0

我想这CustomerViewModel中的属性正确实现了INotifyPropertyChanged接口。

如果是这样,那么您需要将ComboBox的SelectedValue更改为SelectedItem,因为SelectedItem属性返回当前选中的整个对象。但是,SelectedValuePath属性和SelectedValue一起用作SelectedItem属性的替代方案,据我了解这不是您的选择。

另外这里:

SelectedValue="{Binding ElementName=customerViewModel, Path=PreferredStoreId}" 

是不是需要的ElementName,因为CustomerViewModel设置默认分配的DataContext