如何使用转换器获取PropertyChanged事件与绑定?

时间:2016-06-16 19:58:32

标签: c# wpf xaml

我有一个ListBox绑定到一个对象列表,ListBox中的每个项目都有一个TextBlock与转换器绑定但是当对象的属性发生变化时,我如果没有与转换器绑定,我可以更新TextBlock更新<Window x:Class="BindingPropertyChanged.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:BindingPropertyChanged" mc:Ignorable="d" Title="MainWindow" Height="400" Width="600"> <Window.Resources> <local:LocationToTextConverter x:Key="LocationToTextConverter" /> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions> <ListBox Grid.Row="2" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <TextBlock x:Name="NameText" Text="{Binding Converter={StaticResource LocationToTextConverter}, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock x:Name="DescriptionText" Grid.Row="1" Text="{Binding Description, UpdateSourceTrigger=PropertyChanged}" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Grid Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Label HorizontalAlignment="Right" Content="Place" /> <ComboBox Grid.Column="1" ItemsSource="{Binding Places}" SelectedValue="{Binding SelectedItem.PlaceId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsEditable="False"/> <Label Grid.Row="1" HorizontalAlignment="Right" Content="Description" /> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SelectedItem.Description, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" VerticalAlignment="Center"/> </Grid> </Grid> </Window>

窗口的xmal如下:

ListBox

左侧是前面提到的TextBox,右侧是ComboBoxListBox,用于在ListBox.ItemTemplate中创作所选项目。在DescriptionText中,TextBox如果我在NameText更改了public class DomainBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberNameAttribute] string propertyName = "None") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } public class Location : DomainBase { private int _placeId; private string _description; public int PlaceId { get { return _placeId; } set { if (_placeId == value) return; _placeId = value; OnPropertyChanged(); } } public string Description { get { return _description; } set { if (_description == value) return; _description = value; OnPropertyChanged(); } } } public class Place : DomainBase { public int Id { get; set; } public string Name { get; set; } } 的文字而Repository无法更新,因为它在绑定中使用转换器时,public class Repository { public static ICollection<Place> _places; public static ICollection<Place> Places { get { return _places; } } public static ICollection<Location> _locations; public static ICollection<Location> Locations { get { return _locations; } } static Repository() { _places = new List<Place>(); _places.Add(new Place() { Id = 1, Name = "Downtown Center" }); _places.Add(new Place() { Id = 2, Name = "Headquarter" }); _locations = new List<Location>(); _locations.Add(new Location() { PlaceId = 1, Description = "Room 101" }); _locations.Add(new Location() { PlaceId = 2, Description = "B06" }); } } 可以更新。

以下代码是域模型的类:

ViewModel

虚拟数据有一个DataContext类:

public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Location> _items = new ObservableCollection<Location>(Repository.Locations);
    private Location _selectedItem;

    public ObservableCollection<Location> Items
    {
        get
        {
            return _items;
        }
        private set
        {
            if (_items == value) return;
            _items = value;
            OnPropertyChanged();
            OnPropertyChanged("SelectedItem");
        }
    }
    public Location SelectedItem
    {
        get
        {
            return _selectedItem;
        }
        set
        {
            if (_selectedItem == value) return;
            _selectedItem = value;
            OnPropertyChanged(); 
        }
    }
    public ObservableCollection<Place> Places
    {
        get
        {
            return new ObservableCollection<Place>(Repository.Places);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "None")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

并且class LocationToTextConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string result = ""; Location location = value as Location; if (location != null) { Place place = Repository.Places.Single(o => o.Id == location.PlaceId); if (place != null) { string placeName = place.Name; if (!String.IsNullOrWhiteSpace(placeName)) { result = placeName + ", "; } } result += location.Description; } return result; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } 类设置为窗口的Place

Name

最后,转换器:

Location

我想要的是在Description中显示ListBox LocationDescription ListBox的{​​{1}}当我更改选定的{{1}} {{1}}时,{{1}}中的显示名称也应立即更改。有谁知道如何实现这一目标?

3 个答案:

答案 0 :(得分:3)

你的问题在这里

                    <TextBlock x:Name="NameText" 
                        Text="{Binding Converter={StaticResource LocationToTextConverter}, UpdateSourceTrigger=PropertyChanged}" />

在绑定中,您没有指定路径。执行此操作时,绑定的项目将是位置对象。由于您正在更改Location对象的Descrition属性而不是Location对象本身,因此TextBlock未获取属性更改通知。

您只需要更改几行代码,而不是使用MultiBinding。

<TextBlock.Text>
    <MultiBinding>
        <Binding />
        <Binding Path=Description />
        //dont forget to specify the converter
....
class LocationToTextConverter : IMultiValueConverter
{
    //in the Convert method set, ignore value[1] and change location to values[0]
    Location location = values[0] as Location;

现在,只要您更改description属性,就应该调用转换器。

答案 1 :(得分:0)

问题是Location未更改,因此未调用NotifyPropertyChanged。最简单的解决方案是在Location上创建属性。所以有两个选项可以做到:

  • OnPropertyChanged("Location")致电Location以便触发
  • Location上添加自己的属性并绑定到此属性而不是Location

答案 2 :(得分:0)

为什么不只是拥有一个属性并绑定它呢?

public class Location : DomainBase
{
    private int _placeId;
    private string _description;

    public int PlaceId
    {
        get { return _placeId; }
        set
        {
            if (_placeId == value) return;
            _placeId = value;
            OnPropertyChanged();
        }
    }

    public string Description
    {
        get { return _description; }
        set
        {
            if (_description == value) return;
            _description = value;
            OnPropertyChanged();  // make sure  this Notify RepPlusDescription
        }
    }

    public string RepPlusDescription
    {
        get 
        {   
            string result;
            Place place = Repository.Places.FirstOrDefault(o => o.Id == placeId);
            if (place != null)
            {
                string placeName = place.Name;
                if (!String.IsNullOrWhiteSpace(placeName))
                {
                    result = placeName + ", ";
                }
            }
            return result += location.Description; 
        }
     }
}