更改子属性时回调DependencyProperty

时间:2016-04-01 18:09:40

标签: c# wpf callback observablecollection dependency-properties

在开发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 具有 Bricks 标准块的集合(在DependencyProperty中放置两者不是强制性的 - 仅用于演示所需的回调)。< / p>

Wall Brick 具有相同的属性,用于计算砖块集合标准砖中的裂缝和孔洞数。每当更改标准砖中的参数或更改集合时,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 个答案:

答案 0 :(得分:0)

Well, INotifyPropertyChanged helps solve my problem.
Here some code for updated Classes Brick:

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

}

and Wall:

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
}

But is there any way to achieve same result using DependencyProperty?