【问题标题】:Data changes reflected wrong in parent window数据更改在父窗口中反映错误
【发布时间】:2012-04-25 05:27:37
【问题描述】:

我有以下要求:

  1. 显示TreeView 的项目
  2. TreeView中显示所选项目的详细信息。
  3. 用于编辑所选项目的对话框。

我已经实现了这些要求,但是第三个没有做它应该做的事情,所以我被卡住了。

我想要它做什么: 编辑对话框应该能够编辑项目。这不是TreeViewItem,而是我的一个类的一个实例。

  1. 保存编辑 - 一个用于关闭对话框的按钮。
  2. 放弃编辑 - 用于重置项目中更改的字段并关闭对话框的按钮。

第二个要求不起作用。如果我编辑一个字段并点击取消,项目详细信息面板仍会显示编辑。我已经调试过,但我发现底层项目没有改变 - 但是项目显示的值已更改。

代码:

  1. 物品类别(类别)

    public class Category
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
        public virtual Unit Unit { get; set; }
        public virtual List<Category> ChildCategories { get; set; }
        public virtual Category ParentCategory { get; set; }
        public virtual bool IsMainCategory { get; set; }
    
        public Category()
        {
            ChildCategories = new List<Category>();
        }
    
        public virtual void AddChild(Category child)
        {
            ChildCategories.Add(child);
            child.ParentCategory = this;
        }
    }
    
  2. 项目(类别)详细信息显示在标签中:

    <DataTemplate DataType="{x:Type local:Category}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="4*" SharedSizeGroup="a" />
                <ColumnDefinition Width="6*" SharedSizeGroup="b" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="1*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>
    
            <TextBlock Text="Name" Grid.Column="0" Grid.Row="0" Padding="5"/>
            <TextBlock Text="{Binding Path=Name}" Grid.Column="1" Grid.Row="0" Padding="5"/>
            <TextBlock Text="Description" Grid.Column="0" Grid.Row="1" Padding="5"/>
            <TextBlock Text="{Binding Path=Description}" Grid.Column="1" Grid.Row="1" Padding="5"/>
        </Grid>
    </DataTemplate>
    
  3. 主窗口中编辑项目的事件处理程序:

    private void EditCategory(object sender, RoutedEventArgs e)
    {
        Category ctg = _tree.SelectedItem as Category;
        if (ctg != null)
        {
            CategoryDefineWindow cdw = new CategoryDefineWindow();
            cdw.CategoryObject = ctg;
            cdw.ShowDialog();
        }
    }
    
  4. 项目编辑器窗口 xaml:

    <Window x:Class="BSRCat.View.CategoryDefineWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="150" Width="500">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*" SharedSizeGroup="a" />
            <ColumnDefinition Width="7*" SharedSizeGroup="b" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
    
        <TextBlock Text="Name" Grid.Column="0" Grid.Row="0" Padding="5"/>
        <TextBox Text="{Binding Path=CategoryObject.Name, RelativeSource={RelativeSource AncestorType=Window}}" Grid.Column="1" Grid.Row="0" Padding="5"/>
        <TextBlock Text="Description" Grid.Column="0" Grid.Row="1" Padding="5"/>
        <TextBox Text="{Binding Path=CategoryObject.Description, RelativeSource={RelativeSource AncestorType=Window}}" Grid.Column="1" Grid.Row="1" Padding="5"/>
    
        <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Right">
            <Button Content="Ok" Margin="5" Height="20" Width="30" Click="Confirmed"/>
            <Button Content="Cancel" Margin="5" Height="20" Width="50" Click="Cancelled"/>
        </StackPanel>
    </Grid>
    </Window>
    
  5. 项目编辑器窗口后面的代码:

    public partial class CategoryDefineWindow : Window
    {
        public Category CategoryObject 
        {
            get
            {
            return _category;
            }
            set
            {
                _category = value;
                _initial = new Category() { Name = value.Name, Description = value.Description     };
            }
        }
    
        private Category _category;
        private Category _initial;
    
        public CategoryDefineWindow()
        {
            InitializeComponent();
        }
    
        private void Confirmed(object sender, RoutedEventArgs e)
        {
            Close();
        }
    
        private void Cancelled(object sender, RoutedEventArgs e)
        {
            _category.Name = _initial.Name;
            _category.Description = _initial.Description;
            Close();
        }
    }
    

我已经调试了CategoryDefineWindow.Cancelled 方法并且_category 对象被正确重置。我找不到哪里出错了。

【问题讨论】:

  • 详细信息面板首先如何获取更新的值? Category 类是否实现了 INotifyPropertyChanged?
  • 附带说明:我建议实现接口 ICloneable 并在其克隆方法中使用方法 object.MemberwiseClone()。这样你就不需要所有这些属性复制的东西,当你需要复制的属性数量增加时,实现起来很麻烦..
  • @Jefim :我将TreeView.SelectedItem 绑定到标签的Content 属性。定义了DataTemplate 后,我以这种方式获得了详细信息面板。

标签: c# wpf data-binding


【解决方案1】:

Category 类应该实现 INotifyPropertyChanged 接口。一旦属性值发生更改,WPF 将收到通知。

   public class Category : INotifyPropertyChanged
{
    private string _Name;
    private string _Description;

    public virtual int Id { get; set; }
    public virtual string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            if (_Name == value)
                return;

            _Name = value;
            NotifyPropertyChanged("Name");
        }
    }

    public virtual string Description
    {
        get
        {
            return _Description;
        }
        set
        {
            if (_Description == value)
                return;

            _Description = value;
            NotifyPropertyChanged("Description");
        }
    }
    public virtual Unit Unit { get; set; }
    public virtual List<Category> ChildCategories { get; set; }
    public virtual Category ParentCategory { get; set; }
    public virtual bool IsMainCategory { get; set; }

    public Category()
    {
        ChildCategories = new List<Category>();
    }

    public virtual void AddChild(Category child)
    {
        ChildCategories.Add(child);
        child.ParentCategory = this;
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    相关资源
    最近更新 更多