WPF数据绑定出现问题

3

我有两个对象,一个是客户(Customer),另一个是商店(Store)。有多个商店位置,每个客户都有一个属性叫做PreferredStoreId(int?),它与商店的Id(int)相关联。

在一个WPF应用程序中,我试图构建一个表单,允许编辑客户。这个表单上存在一个组合框,其中填充了商店,作为显示当前设置的首选商店和更改首选商店的方式。

我的问题是,虽然我可以填充组合框,但我无法实现Customer.PreferredId(设置为UserControl数据上下文的对象)和combobox的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();
    }
}

你能展示一下 Stores 属性的实现吗?你实现了 INotifyPropertyChanged 吗? - NeddySpaghetti
你尝试过从绑定中删除ElementName=customerViewModel吗? - dnr3
我已经将StoreListViewModel和StoreViewModel添加到原始帖子中。BaseViewModel实现了INotifyPropertyChanged。 - Xavier Hutchinson
1个回答

0

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

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

还有这里:

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

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


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