遇到WPF数据绑定问题

时间:2014-04-11 07:23:54

标签: c# wpf xaml data-binding combobox

我有2个对象,一个是客户和一个商店。有多个商店位置,每个客户都有一个名为PreferredStoreId(int?)的属性,它与商店的Id(int)相关。

在WPF应用程序中,我试图构建一个允许客户编辑的表单。此表单上存在一个组合框,其中包含Stores,用作显示当前设置的PreferredStore的方式以及更改首选商店的方式。

我的问题是,虽然我可以填充组合框,但我无法在Customer.PreferredId(设置为UserControl' datacontext的对象)和组合框的SelectedItem(存储对象)之间进行双向绑定。 )' s .Id property。

这是我的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();
    }
}

1 个答案:

答案 0 :(得分:0)

我认为CustomerViewModel中的PreferredStoreId属性正确实现了INotifyPropertyChanged接口。

如果是,那么您需要将SelectedValue更改为ComboBox的SelectedItem,因为SelectedItem属性返回当前选择的整个对象。但是,SelectedValuePath属性和SelectedValue一起使用可以替代SelectedItem属性,因为我知道它不是您的选择。

同样在这里:

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

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