postsharp observablecollection firing collectionchanged on propertychanged
postsharp observablecollection firing collectionchanged on propertychanged
非 MVVM。
我得到了这个 ObservableCollection machines
,它由 Machine
类型的对象组成:
[Serializable]
[NotifyPropertyChanged]
public abstract class Machine
{
public MachineNames MachineType { get; set; }
public int MachineVersion { get; set; }
public string LatestEditorName { get; set; }
public DateTime LatestSaveTime { get; set; }
public ObservableCollection<Parameter> Parameters { get; set; }
public string Notes { get; set; }
public abstract double CalculateThroughPut();
public Machine()
{
string[] nameParts = this.GetType().Name.Split('_');
Enum.TryParse(nameParts[0], out MachineNames currentMachineType);
MachineType = currentMachineType;
MachineVersion = int.Parse(nameParts[1]);
Parameters = new ObservableCollection<Parameter>();
ICollectionView icv = CollectionViewSource.GetDefaultView(Parameters);
icv.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
LatestEditorName = Environment.UserName;
LatestSaveTime = DateTime.Now;
}
public double getValue(string parameterName)
{
Parameter currentParameter = Parameters.Where(x => x.Name == parameterName).First();
return currentParameter.Value * currentParameter.MetricConversionFactor;
}
这是它的声明:
public partial class MainWindow : MetroWindow
{
IEnumerable<string> namesOfExistingMachines { get; set; }
public ObservableCollection<Machine> machines { get; set; }
及以后:
private void InitializeData()
{
machines = new ObservableCollection<Machine>();
this.DataContext = machines;
tcMainTabControl.ItemsSource = machines;
请注意 [NotifyPropertyChanged]
标记,它是 PostSharp 的一部分,它只是使 Machine 的所有属性更改通知以进行绑定。此外,它使属性的所有属性都可以更改。
这是 XAML 的初始 window 部分:
<Grid>
<Controls:MetroAnimatedTabControl Name="tcMainTabControl">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="ToolTipService.ShowDuration" Value="100000"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="0"/>
<Setter Property="Header" Value="{Binding Converter={StaticResource tabHeaderConverter}}"/>
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Center" Margin="0,10" FontSize="40" FontWeight="Bold" Text="{Binding Path=MachineType}"/>
<Image HorizontalAlignment="Center" Source="{Binding Path=MachineType, Converter={StaticResource imageUriConverter}}"/>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="Throughput model version "/>
<TextBlock FontSize="20" Text="{Binding Path=MachineVersion}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="created by "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestEditorName}"/>
<TextBlock FontSize="20" Text=" on "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestSaveTime, StringFormat=dd/MM/yyyy}"/>
</StackPanel>
<TextBlock Margin="0,10" FontSize="20" Text="{Binding Path=Notes}"/>
</StackPanel>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<Controls:MetroAnimatedTabControl.ContentTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" Margin="10,0">
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" Margin="0,20" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock FontSize="20" Text="Throughput: "/>
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock FontSize="20" Text=" panel sides per hour"/>
</StackPanel>
</Border>
<ListView DockPanel.Dock="Left" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Path=Parameters, UpdateSourceTrigger=PropertyChanged}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="Black" BorderThickness="0,0,0,1">
<Expander.Header>
<TextBlock FontSize="20" FontWeight="Bold">
<Run>Discipline: </Run>
<TextBlock Text="{Binding Path=Name, Converter={StaticResource titleCaseConverter}}"/>
</TextBlock>
</Expander.Header>
<Expander.Content>
<Border Margin="2" CornerRadius="3">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock FontSize="20" Text="{Binding Name}" Margin="0,0,10,0" VerticalAlignment="Center"/>
<TextBox FontSize="20" BorderBrush="Black" BorderThickness="0,0,0,1" Background="Transparent" Controls:TextBoxHelper.Watermark="Enter value" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalContentAlignment="Center"/>
<TextBlock FontSize="20" Text="{Binding Unit}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding Path=Notes}"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</StackPanel>
绑定部门一切正常。我想要的是每次 Machine
成员中的一个 属性 更改 属性 时为 machines
调用 CollectionChanged
事件(或类似事件) 】 内部。换句话说:例如,如果我更改 machine
的 Machine
之一内的 Parameters
内的 Parameter
,我希望它更新计算
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
谢谢!
要从集合中的项目传播 PropertyChanged
通知,您需要订阅其项目更改通知的集合 class。标准 ObservableCollection<T>
class 不会那样做。您可以扩展 ObservableCollection<T>
,如下所示。您还可以在 SO 上找到更多类似的示例(例如 ObservableCollection that also monitors changes on the elements in collection)。
[NotifyPropertyChanged]
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (T item in e.OldItems)
{
((INotifyPropertyChanged) item).PropertyChanged -= OnItemPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (T item in e.NewItems)
{
((INotifyPropertyChanged) item).PropertyChanged += OnItemPropertyChanged;
}
}
base.OnCollectionChanged(e);
}
protected void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChangedServices.SignalPropertyChanged(this, "Item[]");
NotifyCollectionChangedEventArgs collectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
base.OnCollectionChanged(collectionChangedEventArgs);
}
}
当您使用此自定义集合 class 时,该集合将在项目的 属性 更改时引发事件。现在您还可以告诉 PostSharp 将此通知传播为集合 属性 本身的更改,使用应用于集合 属性 的 [AggregateAllChanges]
属性(例如 Parameters
、machines
).
[AggregateAllChanges]
public ObservableCollectionEx<Parameter> Parameters { get; set; }
[AggregateAllChanges]
public ObservableCollectionEx<Machine> machines { get; set; }
非 MVVM。
我得到了这个 ObservableCollection machines
,它由 Machine
类型的对象组成:
[Serializable]
[NotifyPropertyChanged]
public abstract class Machine
{
public MachineNames MachineType { get; set; }
public int MachineVersion { get; set; }
public string LatestEditorName { get; set; }
public DateTime LatestSaveTime { get; set; }
public ObservableCollection<Parameter> Parameters { get; set; }
public string Notes { get; set; }
public abstract double CalculateThroughPut();
public Machine()
{
string[] nameParts = this.GetType().Name.Split('_');
Enum.TryParse(nameParts[0], out MachineNames currentMachineType);
MachineType = currentMachineType;
MachineVersion = int.Parse(nameParts[1]);
Parameters = new ObservableCollection<Parameter>();
ICollectionView icv = CollectionViewSource.GetDefaultView(Parameters);
icv.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
LatestEditorName = Environment.UserName;
LatestSaveTime = DateTime.Now;
}
public double getValue(string parameterName)
{
Parameter currentParameter = Parameters.Where(x => x.Name == parameterName).First();
return currentParameter.Value * currentParameter.MetricConversionFactor;
}
这是它的声明:
public partial class MainWindow : MetroWindow
{
IEnumerable<string> namesOfExistingMachines { get; set; }
public ObservableCollection<Machine> machines { get; set; }
及以后:
private void InitializeData()
{
machines = new ObservableCollection<Machine>();
this.DataContext = machines;
tcMainTabControl.ItemsSource = machines;
请注意 [NotifyPropertyChanged]
标记,它是 PostSharp 的一部分,它只是使 Machine 的所有属性更改通知以进行绑定。此外,它使属性的所有属性都可以更改。
这是 XAML 的初始 window 部分:
<Grid>
<Controls:MetroAnimatedTabControl Name="tcMainTabControl">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="ToolTipService.ShowDuration" Value="100000"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="0"/>
<Setter Property="Header" Value="{Binding Converter={StaticResource tabHeaderConverter}}"/>
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Center" Margin="0,10" FontSize="40" FontWeight="Bold" Text="{Binding Path=MachineType}"/>
<Image HorizontalAlignment="Center" Source="{Binding Path=MachineType, Converter={StaticResource imageUriConverter}}"/>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="Throughput model version "/>
<TextBlock FontSize="20" Text="{Binding Path=MachineVersion}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="created by "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestEditorName}"/>
<TextBlock FontSize="20" Text=" on "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestSaveTime, StringFormat=dd/MM/yyyy}"/>
</StackPanel>
<TextBlock Margin="0,10" FontSize="20" Text="{Binding Path=Notes}"/>
</StackPanel>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<Controls:MetroAnimatedTabControl.ContentTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" Margin="10,0">
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" Margin="0,20" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock FontSize="20" Text="Throughput: "/>
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock FontSize="20" Text=" panel sides per hour"/>
</StackPanel>
</Border>
<ListView DockPanel.Dock="Left" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Path=Parameters, UpdateSourceTrigger=PropertyChanged}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="Black" BorderThickness="0,0,0,1">
<Expander.Header>
<TextBlock FontSize="20" FontWeight="Bold">
<Run>Discipline: </Run>
<TextBlock Text="{Binding Path=Name, Converter={StaticResource titleCaseConverter}}"/>
</TextBlock>
</Expander.Header>
<Expander.Content>
<Border Margin="2" CornerRadius="3">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock FontSize="20" Text="{Binding Name}" Margin="0,0,10,0" VerticalAlignment="Center"/>
<TextBox FontSize="20" BorderBrush="Black" BorderThickness="0,0,0,1" Background="Transparent" Controls:TextBoxHelper.Watermark="Enter value" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalContentAlignment="Center"/>
<TextBlock FontSize="20" Text="{Binding Unit}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding Path=Notes}"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</StackPanel>
绑定部门一切正常。我想要的是每次 Machine
成员中的一个 属性 更改 属性 时为 machines
调用 CollectionChanged
事件(或类似事件) 】 内部。换句话说:例如,如果我更改 machine
的 Machine
之一内的 Parameters
内的 Parameter
,我希望它更新计算
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
谢谢!
要从集合中的项目传播 PropertyChanged
通知,您需要订阅其项目更改通知的集合 class。标准 ObservableCollection<T>
class 不会那样做。您可以扩展 ObservableCollection<T>
,如下所示。您还可以在 SO 上找到更多类似的示例(例如 ObservableCollection that also monitors changes on the elements in collection)。
[NotifyPropertyChanged]
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (T item in e.OldItems)
{
((INotifyPropertyChanged) item).PropertyChanged -= OnItemPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (T item in e.NewItems)
{
((INotifyPropertyChanged) item).PropertyChanged += OnItemPropertyChanged;
}
}
base.OnCollectionChanged(e);
}
protected void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChangedServices.SignalPropertyChanged(this, "Item[]");
NotifyCollectionChangedEventArgs collectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
base.OnCollectionChanged(collectionChangedEventArgs);
}
}
当您使用此自定义集合 class 时,该集合将在项目的 属性 更改时引发事件。现在您还可以告诉 PostSharp 将此通知传播为集合 属性 本身的更改,使用应用于集合 属性 的 [AggregateAllChanges]
属性(例如 Parameters
、machines
).
[AggregateAllChanges]
public ObservableCollectionEx<Parameter> Parameters { get; set; }
[AggregateAllChanges]
public ObservableCollectionEx<Machine> machines { get; set; }