【问题标题】:ItemsControl ItemSource Binding not updatingItemsControl ItemSsource 绑定未更新
【发布时间】:2016-11-23 16:43:31
【问题描述】:

我以前只是通过将字符串列表转换为带有换行符的字符串来创建一个文本块。此绑定有效;应该在应该更新的时候更新,但我正在尝试将文本列表移动到 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 之外的所有其他控件都有效?它有什么特别之处?

【问题讨论】:

    标签: c# wpf


    【解决方案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>( );
    }
    }
    

    【讨论】:

    • 非常感谢,这个解决方案奏效了。我会记住这一点,ItemSource 控件将来更喜欢 Observable Collection。
    【解决方案2】:

    我还会添加一个注释(尽管我知道这是旧的)。

    这不会更新属性的原因是 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
        }
    

    希望在人们遇到这种情况时能有所帮助。

    【讨论】:

      【解决方案3】:

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

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

      在您的 .xaml 中使用 {Binding TheString}

      希望这会有所帮助!

      【讨论】:

      • 我确实调用了 OnPropertyChanged,它在“errorDTOOnPropertyChanged”方法中。我确实尝试了你的建议,但没有改变:(。
      • 该死的为我们解决了这个问题,不知道是什么问题:(
      猜你喜欢
      • 1970-01-01
      • 2017-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-09
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多