我已经使用DependencyProperty创建了一个UserControl,我想以2方式绑定。但不知何故,这不起作用。当属性发生变化时,“City”属性永远不会在AddressViewModel中设置。
这是我的UserControl: XAML:
<UserControl x:Class="EasyInvoice.UI.CityPicker"
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"
d:DesignHeight="30" d:DesignWidth="300"
DataContext="{Binding CityList, Source={StaticResource Locator}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox IsReadOnly="True" Margin="3" Text="{Binding SelectedCity.PostalCode, Mode=OneWay}" Background="#EEE" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
<ComboBox Margin="3" Grid.Column="1" ItemsSource="{Binding Path=Cities}" DisplayMemberPath="CityName" SelectedItem="{Binding SelectedCity}"/>
</Grid>
</UserControl>
代码背后:
using EasyInvoice.UI.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace EasyInvoice.UI
{
/// <summary>
/// Interaction logic for CityPicker.xaml
/// </summary>
public partial class CityPicker : UserControl
{
public CityPicker()
{
InitializeComponent();
((CityListViewModel)this.DataContext).PropertyChanged += CityPicker_PropertyChanged;
}
private void CityPicker_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedCity")
SetCurrentValue(SelectedCityProperty, ((CityListViewModel)this.DataContext).SelectedCity);
}
public static readonly DependencyProperty SelectedCityProperty =
DependencyProperty.Register("SelectedCity", typeof(CityViewModel), typeof(CityPicker),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public CityViewModel SelectedCity
{
get
{
return (CityViewModel)GetValue(SelectedCityProperty);
}
set
{
SetCurrentValue(SelectedCityProperty, value);
}
}
}
}
这是我使用此控件的地方:
<UserControl x:Class="EasyInvoice.UI.NewCustomerView"
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"
Height="135" Width="450"
xmlns:xctk="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
xmlns:einvoice="clr-namespace:EasyInvoice.UI">
<UserControl.Resources>
<Style TargetType="xctk:WatermarkTextBox">
<Setter Property="Margin" Value="3"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<xctk:WatermarkTextBox Grid.ColumnSpan="2" Watermark="Voornaam" Text="{Binding FirstName}"/>
<xctk:WatermarkTextBox Grid.ColumnSpan="2" Watermark="Famillienaam" Grid.Column="2" Text="{Binding LastName}"/>
<xctk:WatermarkTextBox Grid.ColumnSpan="2" Watermark="Straat" Grid.Row="2" Text="{Binding Address.Street}"/>
<xctk:WatermarkTextBox Grid.ColumnSpan="1" Watermark="Huisnummer" Grid.Column="2" Grid.Row="2" Text="{Binding Address.HouseNr}"/>
<xctk:WatermarkTextBox Grid.ColumnSpan="1" Watermark="Busnummer" Grid.Column="3" Grid.Row="2" Text="{Binding Address.BusNr}"/>
<einvoice:CityPicker Grid.Row="3" Grid.ColumnSpan="4" SelectedCity="{Binding Address.City, Mode=TwoWay}"/>
<Button Grid.Row="5" Content="Opslaan en sluiten" Grid.ColumnSpan="4" Style="{StaticResource PopupButton}"/>
</Grid>
</UserControl>
这是“地址”的ViewModel:
using EasyInvoice.Model;
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EasyInvoice.UI.ViewModel
{
public class AddressViewModel : ViewModelBase
{
private string _street;
private string _houseNr;
private string _busNr;
private CityViewModel _city;
public AddressViewModel(Address address)
{
LoadAddress(address);
}
public AddressViewModel() : this(new Address()) { }
private Address Address { get; set; }
public string Street
{
get
{
return _street;
}
set
{
if (_street == value)
return;
_street = value;
RaisePropertyChanged("Street");
}
}
public string HouseNr
{
get
{
return _houseNr;
}
set
{
if (_houseNr == value)
return;
_houseNr = value;
RaisePropertyChanged("HouseNr");
}
}
public string BusNr
{
get
{
return _busNr;
}
set
{
if (_busNr == value)
return;
_busNr = value;
RaisePropertyChanged("BusNr");
}
}
public string BusNrParen
{
get
{
return string.Concat("(", BusNr, ")");
}
}
public bool HasBusNr
{
get
{
return !string.IsNullOrWhiteSpace(_busNr);
}
}
public CityViewModel City
{
get
{
return _city;
}
set
{
if (_city == value)
return;
_city = value;
RaisePropertyChanged("City");
}
}
public void LoadAddress(Address address)
{
this.Address = address;
if(address == null)
{
_street = "";
_houseNr = "";
_busNr = "";
_city = new CityViewModel(null);
}
else
{
_street = address.StreetName;
_houseNr = address.HouseNr;
_busNr = address.BusNr;
_city = new CityViewModel(address.City);
}
}
}
}
当我调试时,我可以看到当我在UI中更改属性时达到了这一行:
SetCurrentValue(SelectedCityProperty, ((CityListViewModel)this.DataContext).SelectedCity);
但不知何故,这永远不会被设定:
public CityViewModel City
{
get
{
return _city;
}
set
{
if (_city == value)
return;
_city = value;
RaisePropertyChanged("City");
}
}
另外,我确定viewmodel连接正确,因为我在“HouseNr”设置断点,这可以正常工作。
如果没有提供足够的内容,可以在此处找到项目:https://github.com/SanderDeclerck/EasyInvoice
答案 0 :(得分:3)
从您的代码中
public CityViewModel SelectedCity
{
get
{
return (CityViewModel)GetValue(SelectedCityProperty);
}
set
{
SetCurrentValue(SelectedCityProperty, value);
}
}
更改此
SetCurrentValue(SelectedCityProperty, value);
到这个
SetValue(SelectedCityProperty, value);
答案 1 :(得分:2)
问题出在您的约束中。您已将DataContext
UserControl
设置为CityListViewModel
,因此绑定失败,因为绑定引擎正在CityListViewModel而不是Address.City
中搜索属性AddressViewModel
。
您必须使用RelativeSource
或ElementName
明确解决该绑定。
SelectedCity="{Binding DataContext.Address.City,RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=UserControl}, Mode=TwoWay}"
OR
将x:Name
提供给UserControl说NewCustomer并使用ElementName进行绑定。
SelectedCity="{Binding DataContext.Address.City, ElementName=NewCustomer}"
此外,您可以避免设置Mode to TwoWay
,因为您在注册DP时已经指定了此项。