1
當開發MVVP程序時,我碰到一些基於類的不可感知性的問題。更改子屬性時回調DependencyProperty
例如,我們有基類磚,具有指定孔數和裂縫數的屬性。
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
}
類牆有收集磚和標準磚(以DependencyProperty的把兩者不是強制性的 - 只是爲了演示需要回調)。
而且牆具有相同的屬性,磚是計算收集磚和標準磚的裂縫和孔洞的數量。 Tey每次都必須計算標準磚或更改集合中的參數(添加項目,刪除項目或更改項目中的參數)。
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本質上是viewmodel類。實現INotifyPropertyChanged而不是使用DependencyProperties會更傳統。 –