【发布时间】:2016-07-21 15:46:59
【问题描述】:
在开发 MVVP 程序时,我遇到了一些基于类在属性更改时不敏感的问题。
例如,我们有基类 Brick,它具有指示其中的孔数和裂缝数的属性。
class Class_Brick : DependencyObject
{
public Class_Brick()
{
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
set { SetValue(NumberOfHoles_Property, value); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
set { SetValue(NumberOfCracks_Property, value); }
}
#endregion
}
类 Wall 具有 砖块集合 和 标准砖块(将两者都放入 DependencyProperty 不是强制性的 - 仅用于演示所需的回调)。
此外,墙与砖具有相同的属性,可计算砖集合和标准砖中的裂缝和孔数时间>。当 Standard Brick 中的参数更改或集合更改(添加项目、删除项目或更改项目中的参数)时,必须每次计算。
class Class_Wall : DependencyObject
{
public Class_Wall()
{
SetValue(Standard_Brick_Property, new Class_Brick());
SetValue(Bricks_Property, new ObservableCollection<Class_Brick>());
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty Standard_Brick_Property = DependencyProperty.Register("Standard_Brick", typeof(Class_Brick), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
public static DependencyProperty Bricks_Property = DependencyProperty.Register("Bricks", typeof(ObservableCollection<Class_Brick>), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
#endregion
#region Inner Methods
public static void On_Property_Change(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ObservableCollection<Class_Brick> inWall;
int wallCracks = 0;
int wallHoles = 0;
if (e.Property == Bricks_Property)
{
inWall = e.NewValue as ObservableCollection<Class_Brick>;
Class_Brick mainItem = (Class_Brick)d.GetValue(Standard_Brick_Property);
wallCracks = mainItem.NumberOfCracks;
wallHoles = mainItem.NumberOfHoles;
}
else //if (e.Property == Standard_Brick_Property)
{
inWall = (ObservableCollection<Class_Brick>)d.GetValue(Bricks_Property);
Class_Brick newItem = e.NewValue as Class_Brick;
wallCracks = newItem.NumberOfCracks;
wallHoles = newItem.NumberOfHoles;
}
if (inWall != null)
{
foreach (Class_Brick brick in inWall)
{
wallCracks += brick.NumberOfCracks;
wallHoles += brick.NumberOfHoles;
}
d.SetValue(Home_Quest.Class_Wall.NumberOfCracks_Property, wallCracks);
d.SetValue(Home_Quest.Class_Wall.NumberOfHoles_Property, wallHoles);
}
}
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
}
public Class_Brick Standard_Brick
{
get { return (Class_Brick)GetValue(Standard_Brick_Property); }
set { SetValue(Standard_Brick_Property, value); }
}
public ObservableCollection<Class_Brick> Bricks
{
get { return (ObservableCollection<Class_Brick>)GetValue(Bricks_Property); }
set { SetValue(Bricks_Property, value); }
}
#endregion
}
我将此 XAML 用于测试字段:
<DockPanel LastChildFill="True">
<TextBlock Text="Wall"
DockPanel.Dock="Top"
HorizontalAlignment="Center"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="0"
Grid.Row="0" Grid.RowSpan="2"
LastChildFill="True">
<TextBlock Text="Bricks"
DockPanel.Dock="Top" />
<Button Content="-" DockPanel.Dock="Bottom"
Click="btn_Remove" />
<Button Content="+" DockPanel.Dock="Bottom"
Click="btn_Add" />
<ListBox x:Name="lB_Bricks"
ItemsSource="{Binding Path=Bricks}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="2"
BorderBrush="Azure"
BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="0"
LastChildFill="True"
DataContext="{Binding Path=Standard_Brick}">
<TextBlock Text="Standard Brick"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="1"
LastChildFill="True">
<TextBlock Text="Resulted Cracks and Holes"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles}"
Grid.Column="1" Grid.Row="0"
IsReadOnly="True"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks}"
Grid.Column="1" Grid.Row="1"
IsReadOnly="True"
TextAlignment="Center"/>
</Grid>
</DockPanel>
</Grid>
</DockPanel>
和 MainWindow.cs 的代码:
public MainWindow()
{
InitializeComponent();
this.DataContext = currentWall;
}
Class_Wall currentWall = new Class_Wall();
private void btn_Add(object sender, RoutedEventArgs e)
{
currentWall.Bricks.Add(new Class_Brick());
}
private void btn_Remove(object sender, RoutedEventArgs e)
{
Class_Brick selectedBrick = this.lB_Bricks.SelectedItem as Class_Brick;
if ( selectedBrick != null)
{
currentWall.Bricks.Remove(selectedBrick);
}
}
直到今天,我从未使用 DependencyProperties 找到解决此问题的方法。
希望得到您的帮助和支持。
【问题讨论】:
-
有什么问题?此外,在我看来 Class_Wall 和 Class_Brick 本质上是视图模型类。实现 INotifyPropertyChanged 而不是使用 DependencyProperties 会更传统。
标签: c# wpf callback observablecollection dependency-properties