2016-04-01 39 views
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。
希望您的幫助和支持。

+1

什麼問題?此外,它看起來像Class_Wall和Class_Brick本質上是viewmodel類。實現INotifyPropertyChanged而不是使用DependencyProperties會更傳統。 –

回答

0

那麼,INotifyPropertyChanged有助於解決我的問題。
這裏更新類一些代碼:

class Class_Brick_NPC : INotifyPropertyChanged 
{ 

    public Class_Brick_NPC() 
    { 

    } 

    #region Event Functions 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 

     if(handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

    #endregion 

    #region Inner Parametres 

    private int numberOfHoles = 0; 

    private int numberOfCracks = 0; 

    #endregion 

    #region Parametres 

    public int NumberOfHoles 
    { 
     get { return numberOfHoles; } 
     set 
     { 
      numberOfHoles = value; 

      OnPropertyChanged("NumberOfHoles"); 
     } 
    } 

    public int NumberOfCracks 
    { 
     get { return numberOfCracks; } 
     set 
     { 
      numberOfCracks = value; 

      OnPropertyChanged("NumberOfCracks"); 
     } 
    } 

    #endregion 

} 

class Class_Wall_NPC : INotifyPropertyChanged 
{ 
    public Class_Wall_NPC() 
    { 
     standard_Brick.PropertyChanged += InnerUpdate; 
    } 

    #region Event Methods 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 

     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

    public void InnerUpdate(object sender, PropertyChangedEventArgs args) 
    { 
     if (args.PropertyName == "NumberOfHoles") 
     { 
      OnPropertyChanged("NumberOfHoles"); 
     } 
     else if (args.PropertyName == "NumberOfCracks") 
     { 
      OnPropertyChanged("NumberOfCracks"); 
     } 
    } 
    #endregion 

    #region Inner Properties 
    private ObservableCollection<Class_Brick_NPC> bricks = new ObservableCollection<Class_Brick_NPC>(); 

    private Class_Brick_NPC standard_Brick = new Class_Brick_NPC(); 

    private int numberOfHoles = 0; 

    private int numberOfCracks = 0; 
    #endregion 

    #region Properties 

    public ObservableCollection<Class_Brick_NPC> Bricks 
    { 
     get { return bricks; } 
     set 
     { 
      bricks = value; 

      OnPropertyChanged("Bricks"); 
     } 
    } 

    public Class_Brick_NPC Standard_Brick 
    { 
     get { return standard_Brick; } 
     set 
     { 
      standard_Brick = value; 
     } 
    } 



    public int NumberOfHoles 
    { 
     get 
     { 
      int wallHoles = standard_Brick.NumberOfHoles; 

      foreach (Class_Brick_NPC brick in bricks) 
      { 
       wallHoles += brick.NumberOfHoles; 
      } 

      return wallHoles; 
     }    
    } 

    public int NumberOfCracks 
    { 
     get 
     { 
      int wallCracks = standard_Brick.NumberOfCracks; 

      foreach (Class_Brick_NPC brick in bricks) 
      { 
       wallCracks += brick.NumberOfCracks;      
      } 

      return wallCracks; 
     }    
    } 

    #endregion 

    #region Methods 

    public void Add_Brick(Class_Brick_NPC addedItem) 
    { 
     bricks.Add(addedItem); 

     addedItem.PropertyChanged += InnerUpdate; 

     OnPropertyChanged("Bricks"); 
    } 

    public void Remove_Brick(Class_Brick_NPC removedItem) 
    { 
     bricks.Remove(removedItem); 
     removedItem.PropertyChanged -= InnerUpdate; 

     OnPropertyChanged("Bricks"); 
     OnPropertyChanged("NumberOfHoles"); 
     OnPropertyChanged("NumberOfCracks"); 
    } 

    #endregion 
} 

但是,有沒有辦法使用的DependencyProperty達到同樣的效果?