簡體   English   中英

WPF用戶控件-雙向綁定不起作用

[英]WPF User Control - Two way binding doesn't work

我已經用DependencyProperty制作了一個UserControl,我想綁定2種方式。 但是,這不起作用。 屬性更改時,永遠不會在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);
            }
        }
    }
}

調試時,可以看到在用戶界面中更改屬性時可以到達以下行:

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

從你的代碼

public CityViewModel SelectedCity
    {
        get
        {
            return (CityViewModel)GetValue(SelectedCityProperty);
        }
        set
        {
            SetCurrentValue(SelectedCityProperty, value);
        }
    }

改變這個

SetCurrentValue(SelectedCityProperty, value);

對此

SetValue(SelectedCityProperty, value);

問題在於您的約束力。 您已將UserControl DataContext設置為CityListViewModel因此綁定失敗,因為綁定引擎正在CityListViewModel中而不是AddressViewModel搜索屬性Address.City

您必須使用RelativeSourceElementName顯式解析該綁定。

SelectedCity="{Binding DataContext.Address.City,RelativeSource={RelativeSource 
               Mode=FindAncestor, AncestorType=UserControl}, Mode=TwoWay}"

要么

x:Name給UserControl說NewCustomer並使用ElementName綁定。

SelectedCity="{Binding DataContext.Address.City, ElementName=NewCustomer}"

您也可以避免將Mode to TwoWay設置Mode to TwoWay因為在DP的注冊時已經指定了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM