INotifyPropertyChanged问题,属性没有从Dispatcher更新



当我从一个单独的线程使用Dispatcher时,我有一个问题,我的一些属性没有更新。当我检查数组中的值时,它们都是0。

属性:

private string[] _years;
public string[] Years
{
    get { return _years; }
    private set
    {
        if (_years == value)
    {
        return;
    }
    _years = value;
    OnPropertyChanged("Years");
    }
}
private int[] _yearCounts;
public int[] YearCounts
{
    get { return _yearCounts; }
    private set
    {
        if (_yearCounts == value)
        {
            return;
        }
        _yearCounts = value;
        OnPropertyChanged("YearCounts");
    }
}
private ObservableCollection<RecordModel> _missingCollection;
public ObservableCollection<RecordModel> MissingCollection
{
    get { return _missingCollection; }
    private set
    {
        if (_missingCollection == value)
        {
            return;
        }
        _missingCollection = value;
        OnPropertyChanged("MissingCollection");
    }
}
构造函数:

public MissingReportsViewModel()
{
    YearCounts = new int[4];
    Years = new string[4];
    Initialize();
}

方法:

private void Initialize()
{
    SetYears();
    Task task = new Task(() => 
    { 
        MissingCollection = new AccessWorker().GetMissingReports(); 
    });
    task.ContinueWith((result) => 
    { 
        SetYearCounts(); 
    });
    task.Start();
}
private void SetYears()
{
    for (int i = 0; i < 4; i++)
    {
        Years[i] = DateTime.Now.AddYears(-i).Year.ToString();
    }
}
private void SetYearCounts()
{
    for (int i = 0; i < 4; i++)
    {
        int n = i;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
            new Action(() => YearCounts[n] = MissingCollection.Where(item =>
                item.RepNum.Substring(0, 4).Equals(Years[n])).ToList().Count()));
    }
}

INotifyPropertyChanged:

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

问题在于运行后,YearsCount的各指标设为0。如果我去掉Task.Run()和Dispatcher,程序在漫长的操作过程中会冻结一点,但一切都显示正常。所以,我搞砸了某个地方,未能正确更新YearsCount属性。

编辑(解决方案?):

非常感谢Garry Vass,他通过我的两个冗长的问题挂在那里,Jon Skeet(从第一篇文章开始)和Shoe。我能够使它启动并运行,没有任何问题,像这样:

XAML

四年:

<TextBlock TextWrapping="Wrap" 
           Style="{DynamicResource SectionBodyTextStyle}">
    <Run Text="{Binding YearlyStats[0].Year}"/>
    <Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="1" 
           Style="{DynamicResource SectionBodyTextStyle}">
    <Run Text="{Binding YearlyStats[1].Year}"/>
    <Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="2" 
           Style="{DynamicResource SectionBodyTextStyle}">
    <Run Text="{Binding YearlyStats[2].Year}"/>
    <Run Text=":"/>
</TextBlock>
<TextBlock TextWrapping="Wrap" Grid.Row="3" 
           Style="{DynamicResource SectionBodyTextStyle}">
    <Run Text="{Binding YearlyStats[3].Year}"/>
    <Run Text=":"/>
</TextBlock>

四项统计(每年一次):

<TextBlock Text="{Binding YearlyStats[0].Count}" Grid.Column="1" 
           Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[1].Count}" Grid.Column="1" Grid.Row="1" 
           Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[2].Count}" Grid.Column="1" Grid.Row="2" 
           Margin="10,0,0,0"/>
<TextBlock Text="{Binding YearlyStats[3].Count}" Grid.Column="1" Grid.Row="3" 
           Margin="10,0,0,0"/>

c#代码

数据对象:

public class MissingReportInfoModel : INotifyPropertyChanged
{
    private string _year;
    public string Year
    {
        get { return _year; }
        set
        {
            if (_year == value)
            {
                return;
            }
            _year = value;
            OnPropertyChanged("Year");
        }
    }
    private int _count;
    public int Count
    {
        get { return _count; }
        set
        {
            if (_count == value)
            {
                return;
            }
            _count = value;
            OnPropertyChanged("Count");
        }
    }
    public MissingReportInfoModel()
    {
        Year = "Not Set";
        Count = 0;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

ViewModel属性:

private ObservableCollection<MissingReportInfoModel> _yearlyStats;
public ObservableCollection<MissingReportInfoModel> YearlyStats
{
    get { return _yearlyStats; }
    private set
    {
        if (_yearlyStats == value)
        {
            return;
        }
        _yearlyStats = value;
        OnPropertyChanged("YearlyStats");
    }
}
private ObservableCollection<RecordModel> _missingCollection;
public ObservableCollection<RecordModel> MissingCollection
{
    get { return _missingCollection; }
    private set
    {
        if (_missingCollection == value)
        {
            return;
        }
        _missingCollection = value;
        OnPropertyChanged("MissingCollection");
    }
}

视图模型构造函数:

public MissingReportsViewModel()
{
    YearlyStats = new ObservableCollection<MissingReportInfoModel>();
    Initialize();
}

视图模型方法:

private void Initialize()
{
    SetYears();
    Task task = new Task(() => 
    { 
        MissingCollection = new AccessWorker().GetMissingReports(); 
    });
    task.ContinueWith((result) => 
    { 
        SetCounts(); 
    });
    task.Start();
}
private void SetYears()
{
    for (int i = 0; i < 4; i++)
    {
        var info = new MissingReportInfoModel();
        info.Year = DateTime.Now.AddYears(-i).Year.ToString();
        YearlyStats.Add(info);
    }
}
private void SetCounts()
{
    for (int i = 0; i < 4; i++)
    {
        int n = i;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
            new Action(() => 
            {
                YearlyStats[n].Count = MissingCollection.Where(item =>
                    item.RepNum.Substring(0, 4).Equals(YearlyStats[n].Year)).ToList().Count();
            }));
    }
}

当你展示一个项目集合时,有几件事要记住,其中包括…

  • 将关系保持在单个对象下。你原来的设计使用并行数组,这更容易失去同步;
  • 集合本身需要一个公共getter,以便绑定引擎可以找到它,并且它需要给出更改通知。ObservableCollection of T给出了
  • 元素中的每个属性也需要提供更改通知。从INPC继承类可以实现这一点。
  • 对属性的更改由WPF绑定引擎自动编组到正确的线程中,但是对T的ObservableCollection (add, remove)的更改必须由View Model编组。

这是一个典型的类为您的丢失的报告…

  public class MissingReportInfo : INotifyPropertyChanged
    {
        private string _year;
        public string Year
        {
            [DebuggerStepThrough]
            get { return _year; }
            [DebuggerStepThrough]
            set
            {
                if (value != _year)
                {
                    _year = value;
                    OnPropertyChanged("Year");
                }
            }
        }
        private int _count;
        public int Count
        {
            [DebuggerStepThrough]
            get { return _count; }
            [DebuggerStepThrough]
            set
            {
                if (value != _count)
                {
                    _count = value;
                    OnPropertyChanged("Count");
                }
            }
        }
        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name)
        {
            var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }

请注意,它在Interlocked类上使用了'CompareExchange',以避免潜在的竞争条件。

集合的声明如下所示:

public class ViewModel
{
    public ObservableCollection<MissingReportInfo> MissingReportInfos { get; set; } 
    public void Initialize()
    {
        MissingReportInfos = new ObservableCollection<MissingReportInfo>();
    }
}

最后,Xaml使用预期固定集合的文本块。这是尴尬的。相反,请尝试其中一个收集容器。这里是一个示例项目控件设置来显示丢失的报告…

    <ItemsControl ItemsSource="{Binding MissingReportInfos}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="5">
                    <TextBlock Text="{Binding Year}"/>
                    <TextBlock Text=":"/>
                    <Rectangle Width="10"/>
                    <TextBlock Text="{Binding Count, StringFormat='###,###,##0'}" HorizontalAlignment="Left"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

酌情添加样式和其他内容。现在,Xaml不再关心集合的物理维度,更重要的是,必须维护代码的开发人员将非常感谢一个干净的实现。

相关内容

  • 没有找到相关文章

最新更新