【问题标题】:Callback of DependencyProperty when changed subproperties更改子属性时回调 DependencyProperty
【发布时间】: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


【解决方案1】:

嗯,INotifyPropertyChanged 有助于解决我的问题。
这里有一些更新类的代码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

}

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 实现相同的结果?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-16
    • 2013-11-02
    • 1970-01-01
    • 2021-07-31
    • 2019-07-08
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多