ItemsControl ItemSource绑定不更新

时间:2016-11-23 16:43:32

标签: c# wpf

我过去只是通过将字符串列表转换为带有换行符的一个字符串来创建一个文本块。这个Binding有效;应该和所有人一起更新,但是我试图将文本列表移动到ItemsControl中,因为它们将来需要在某些时候成为超链接。问题:触发PropertyChangeEvent时,ItemsControl不会更改。相关守则如下:

的Xaml

<local:BaseUserControl x:Class="BAC.Windows.UI.Views.ErrorsView"
             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:local="clr-namespace:BAC.Windows.UI.Views"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

             ...

            <ItemsControl ItemsSource="{Binding Path=ErrorMessages}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"></TextBlock>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

            <!--<TextBlock VerticalAlignment="Center" Visibility="{Binding ErrorMessages, Converter={StaticResource VisibleWhenNotEmptyConverter}}" Text="{Binding ErrorMessages, Converter={StaticResource ErrorMessagesToTextConverter}}">

            (What I used to use)

            </TextBlock>-->


 ...

</local:BaseUserControl>

视图模型

using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using ASI.Core.Core;
using ASI.Core.DTO;
using ASI.Core.Extensions;
using ASI.Core.Mappers;
using BAC.Core.Resources;
using BAC.Core.Services;
using BAC.Core.ViewModels.Views; 

namespace BAC.Core.ViewModels
{
    public interface IErrorsViewModel : IViewModel<IErrorsView>
    {
    }

    public class ErrorsViewModel : BaseViewModel<IErrorsView>, IErrorsViewModel
    {
        ...

        private readonly ErrorDTO _errorDTO;
        private readonly ErrorDTO _warningDTO;

        public ErrorsViewModel(...) : base(view)
        {
            ...

            //Just added this string to know that it's at least binding. This Message displays, and never changes.
            ErrorMessages = new List<string>() {"Simple Message"};

            //Tells the View to bind dataContext to Viewmodel
            Edit();
        }

        private void errorDTOOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
        {
            ErrorDTO dto;
            if (!string.Equals(propertyChangedEventArgs.PropertyName, nameof(dto.HasError))) return;

            ErrorMessages.Clear();
            _errorDTO.ErrorMessages.Each(x => ErrorMessages.Add(Constants.Captions.Errors + ": " + x));
            _warningDTO.ErrorMessages.Each(x => ErrorMessages.Add(Constants.Captions.Warnings + ": " + x));

            OnPropertyChanged(() => ErrorMessages);
            OnPropertyChanged(() => HasError);
            OnPropertyChanged(() => HasWarning);
        }

        ...

        public bool HasError => _errorDTO.HasError;

        public bool HasWarning => _warningDTO.HasError;

        public IList<string> ErrorMessages { get; set; }

        ...
}

只是因为我知道人们可能会要求看到它......

   public class BaseNotifyPropertyChanged : INotifyPropertyChanged
   {
      public event PropertyChangedEventHandler PropertyChanged;
      [NotifyPropertyChangedInvocator]
      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }

      public void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
      {
         var body = propertyExpression.Body as MemberExpression;
         if (body != null)
            OnPropertyChanged(body.Member.Name);
      }

       protected void OnEvent(Action action)
       {
           try
           {
               action();
           }
           catch
           { }
       }
   }

我确信这是一件简单易事的事情,但是我看起来越难,我就越能被简单的东西所愚弄。除了ItemSource之外,为什么绑定适用于所有其他conrols?它有什么特别之处?

3 个答案:

答案 0 :(得分:2)

我还会添加anotehr解释(即使我知道这是旧的)。

这不会更新属性的原因是List对象实际上没有更改,因此ListView不会更新列表。在不使用“ObservableCollection”的情况下执行此操作的唯一方法是在每个属性更改上创建一个全新的列表,如下所示:

    private void errorDTOOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
    {
        if (!string.Equals(propertyChangedEventArgs.PropertyName, nameof(dto.HasError))) return;
            OnPropertyChanged(() => ErrorMessages);
    }

    public List<string> ErrorMessages => getErrorMessages();

    private List<string> getErrorMessages() {
        //create list in a manner of your choosing
    }

希望这可以帮助人们遇到这种情况。

答案 1 :(得分:1)

因此,我可以使用ObservableCollection而不是List来使代码工作。 ObservableCollection在更改其集合时自动生成列表更改通知。以下是我的示例代码。我使用计时器每秒更新错误列表。

<Window x:Class="TestEer.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:TestEer"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
    <ItemsControl ItemsSource="{Binding Path=ErrorMessages}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

using System.Collections.ObjectModel;
using System.Timers;
using System.Windows;
using System.Windows.Data;

namespace TestEer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private Timer _timer;
    private readonly object _sync = new object( );
    public MainWindow( )
    {
        InitializeComponent( );
        BindingOperations.EnableCollectionSynchronization( ErrorMessages, _sync );
        _timer = new Timer
        {
            AutoReset = true,
            Interval = 1000
        };

        _timer.Elapsed += _timer_Elapsed;
        _timer.Enabled = true;
        _timer.Start( );
    }

    private void _timer_Elapsed( object sender, ElapsedEventArgs e )
    {
        ErrorMessages.Add( $"Error @ {e.SignalTime}" );
    }

    public ObservableCollection<string> ErrorMessages { get; } = new ObservableCollection<string>( );
}
}

答案 2 :(得分:-1)

我们在构造函数之前在get set方法中设置OnPropertyChanged()方法,这似乎有效!

private bool _theString;
public bool TheString
{
    get { return _theString; }
    set { _theString = value; OnPropertyChanged(); }
}

在.xaml中使用{Binding TheString}

希望这有帮助!