代码之家  ›  专栏  ›  技术社区  ›  Bandook

WPF DataGrid-通知另一类用户选择的值

  •  0
  • Bandook  · 技术社区  · 7 年前

    我已经试了几天了。我是新的编码和相当新的WPF和数据网格。非常感谢您的帮助。

    See Image 数据网格的

    基本上,左边的datagrid与右边的ItemSource不同。

    在右边的datagrid中,用户选择他想要计算总量的品牌。我捕获所选的行并得到如下所示的品牌率-

            private void BrandGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            List<DataGridCellInfo> info = BrandGrid.SelectedCells.ToList();
            rate = 0;
            if (info.Count > 0)
            {
                Brand i = (Brand)info[1].Item;
                rate = i.Rate;
            }
        }
    

    现在我如何告诉其他类使用所选的比率来计算总金额。就像我计算总面积一样

    TotalArea=长度*数量*someConstantWidth(使用INotifyPropertyChanged很容易)。当用户在datagrid中编辑长度和数量时,TotalArea将同时更新。

    这里的TotalAmount=总面积* 对于如何从主窗口发送信息,我感到非常困惑。BrandGrid\u SelectedCells更改为另一个类。

    我尝试过处理委托和事件,但没有成功,因为在我的示例应用程序中,我发现很难实现它。

    最好的方法是什么?我该怎么办?

    更新
    非常感谢您如此详细的回复!我对执行情况没有太多疑问,

    (1) 在步骤1中,代码行:public void SelectBrand()=>OnBrandSelect?.Invoke(这个,速率);

    (2) 请注意Result类:长度和数量是用户输入-使用它们计算总面积。这里长度、数量、面积等都在同一个类结果中,我使用INotifyPropertyChanged来更新TotalArea列。

    我希望输出类似于INotifyPropertyChanged处理更改的方式。它实时地改变细胞,就像我改变其他细胞的输入一样——如果可能的话,类似的行为会很棒。


    通过了解您的解决方案,以下是正确的要求-
    (1) 没有向OrderGrid添加项目的按钮。新行自动出现。

    (2) 可以在输入项目之前或输入项目之后先选择品牌。

    选择

    (4) 如果用户在选择品牌后在OrderGrid中添加新项目,则新项目的总金额将基于当前已选择的品牌进行计算。

    你说的“SelectedCellsChanged”是对的,我把它改成了鼠标双击事件。它现在非常可靠,用户感觉也更好。非常感谢。

    2 回复  |  直到 7 年前
        1
  •  0
  •   B.Spangenberg    7 年前

    因此,我假设您正试图调用所选品牌中的一个事件来向列出的任何人传达品牌率为(),然后用它来计算总数。

    第一步: 您需要向“Brand”类添加一个事件,并创建一个公共方法在内部调用它。

    class Brand
    {
        public string Name { get; set; }
        public Rate Rate { get; set; }
    
        public void SelectBrand() => OnBrandSelect?.Invoke(this, Rate);
    
        public event EventHandler<Rate> OnBrandSelect;
    }
    

    第二步: 当您将品牌事件加载到包含它们的集合(用作网格源)时,将它们挂接起来。基本示例如下。

    // load brands from db or elswhere.
    List<Brand> brandsSelectedToLoad = new List<Brand>()
    
     ObservableCollection<Brand> BrandDisplayCollectionToBeUsedAsGridSource = new ObservableCollection<Brand>();
     foreach (Brand brand in brandsSelectedToLoad)
     {
          brand.OnBrandSelect += (s, args) => { /* Call calculation method (TotalAmount = TotalArea * args -- args is rate) and set the result */}
          BrandDisplayCollectionToBeUsedAsGridSource.Add(brand);
     }
    

    第三步:

    private void BrandGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
    {
         List<DataGridCellInfo> info = BrandGrid.SelectedCells.ToList();
         if (info.Count > 0)
         {
             Brand selectedBrand = info[1].Item as Brand;
             if (selectedBrand == null)
                 return;
    
             selectedBrand.SelectBrand();
         }
    }
    

    注: 我将称之为“结果网格”的另一个网格很可能需要在totalamount列中根据事件调用的速率反映这些计算。 但我看不出结果记录和 精选品牌(除非它隐藏在结果对象中)。哪个 让我相信你只是将记录添加到brand select的结果网格中,然后忘记它。

    您需要将加载到Brands网格中的同一Brand对象引用嵌入到结果对象中,并将其事件挂接到结果对象中(几乎与第二步相同)。你可以忽略第二步,然后使用下面的。

    例子:

    class Result : INotifyPropertyChanged
    {
        private double total;
    
        public Result(Brand brandRef)
        {         
            this.Brand = brandRef;
            brand.OnBrandSelect += (s, args) => 
                { 
                   /* Call calculation method (TotalAmount = TotalArea * 
                      args -- args is rate) and set the result */
    
                   Total = /*(TotalAmount = TotalArea * 
                      args -- args is rate)*/;
                }
        }
    
        public double Total   
        {
            get { return total; }
            set
            {
                total = value;
                NotifyPropertyChanged();
            }
        }
    
        public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    更新响应

    我懂了。因此,只有在用户配置了长度和数量输入之后,品牌选择才可用。因此,在构造中使用brand参数是不可能的,因为您需要预先实例化result对象。

    执行以下操作:

    1) 创建品牌对象,因为我在第一步。就用双倍的价格代替吧。

    public void Caculate(double rate)
    {
        TotalAmount = TotalArea * rate;
    }
    

    所以我假设在某个地方有一个按钮,它将一个新记录添加到结果网格中,由用户配置,然后他选择品牌。就在将对象添加到结果对象集合的点之前。用下面的钩子钩住它。

    ResultObject resultObj = new ResultObject()
    BrandCollection.All(brand => 
    { 
       brand.OnBrandSelect += (s, rate) => 
       { 
          resultObj.Calculate(rate);
       }; return true; 
    });
    resultGridCollection.Add(resultObj);
    

    所以现在每个brand对象都有一个钩子函数,用于计算和设置select上的现有结果对象。您仍然使用步骤3来调用选择事件。

    请记住,每次添加新对象时,您都需要取消订阅所有品牌活动,否则品牌选择将不断更改您的所有结果。如果您想支持对选定的GridResult进行更改,只需将订阅代码从add方法移动到result的选定网格单元方法。

    更新2

    namespace GridSelection
    {
       /// <summary>
       /// Interaction logic for MainWindow.xaml
       /// </summary>
        public partial class MainWindow : Window
        {
            MainWindowViewModel model = new MainWindowViewModel();
            public MainWindow()
            {
               InitializeComponent();
               OrdersGrid.SelectedCellsChanged += OrdersGrid_SelectedCellsChanged;
               BrandGrid.SelectedCellsChanged += BrandGrid_SelectedCellsChanged;
               this.Loaded += MainWindow_Loaded;
         }
    
    
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            model.Load();
            this.DataContext = model;
        }
    
        private void OrdersGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            DataGrid grid = sender as DataGrid;
            Order selectedOrder = grid.SelectedItem as Order;
    
            if (selectedOrder == null)
                return;
    
            model.BrandCollection.All(b => 
            {
                b.UnsubscribeAll();
                b.OnBrandSelect += (s, rate) => 
                {
                    selectedOrder.CalculateTotal(rate);
                }; return true;
            });
        }
    
        private void BrandGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            DataGrid grid = sender as DataGrid;
            Brand selectedbrand = grid.SelectedItem as Brand;
    
            if (selectedbrand == null)
                return;
    
            selectedbrand.InvokeBrandSelected();
        }
    }
    
    internal class MainWindowViewModel
    {
        public ObservableCollection<Brand> BrandCollection { get; private set; }
        public ObservableCollection<Order> OrderCollection { get; private set; }
        public ICommand AddNewOrderCommand { get; private set; }
    
        public void Load()
        {
            BrandCollection = new ObservableCollection<Brand>
            {
                new Brand() { Name = "Brand One", Rate = 20 },
                new Brand() { Name = "Brand Two", Rate = 30 },
                new Brand() { Name = "Brand Three", Rate = 50 }
            };
    
              OrderCollection = new ObservableCollection<Order>();
    
              AddNewOrderCommand = new CustomCommand(p => 
              {
                  OrderCollection.Add(new Order());
              });
          }
      }
    
      public class Order : INotifyPropertyChanged
      {
        #region Private Variables
          private int length;
          private int quantity;
          private int totalArea;
          private double totalAmount;
         #endregion
    
          #region Public Properties
          public int Length
          {
               get { return length; }
               set
               {
                   length = value;
                   NotifyPropertyChanged();
    
                   CalculateArea();
               }
           }
    
            public int Quantity
            {
                get { return quantity; }
                set
                {
                    quantity = value;
                    NotifyPropertyChanged();
    
                    CalculateArea();
                }
            }
    
            public int TotalArea
            {
                get { return totalArea; }
                set
                {
                    totalArea = value;
                    NotifyPropertyChanged();
                }
            }
    
            public double TotalAmount
            {
                get { return totalAmount; }
                set
               {
                   totalAmount = value;
                   NotifyPropertyChanged();
               }
            }
    
            #endregion
    
            private void CalculateArea()
            {
                TotalArea = this.Length * this.Quantity;
            }
            public void CalculateTotal(double rate)
            {
                TotalAmount = this.TotalArea * rate;
            }
    
            #region Public Methods
            public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
    
            #region Events
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
        }
    
        public class Brand
        {
            public string Name { get; set; }
            public double Rate { get; set; }
    
            public void InvokeBrandSelected() => OnBrandSelect?.Invoke(this, Rate);
            public void UnsubscribeAll() => OnBrandSelect = null;
    
            public event EventHandler<double> OnBrandSelect;
        }
    
        // Interface 
        public interface ICustomCommand : ICommand
        {
            event EventHandler<object> Executed;
        }
    
        // Command Class
        public class CustomCommand : ICustomCommand
        {
            #region Private Fields
            private readonly Action<object> _execute;
            private readonly Func<object, bool> _canExecute;
            #endregion
    
            #region Constructor
            public CustomCommand(Action<object> execute) : this(execute, null)
            {
            }
    
            public CustomCommand(Action<object> execute, Func<object, bool> canExecute)
            {
                _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute ?? (x => true);
            }
            #endregion
    
            #region Public Methods
            public bool CanExecute(object parameter)
            {
                return _canExecute(parameter);
            }
    
            public void Execute(object parameter = null)
            {
                Refresh();
                _execute(parameter);
                Executed?.Invoke(this, parameter);
                Refresh();
            }
    
            public void Refresh()
            {
                CommandManager.InvalidateRequerySuggested();
            }
            #endregion
    
            #region Events
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                }
                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
            public event EventHandler<object> Executed;
            #endregion
        }
    }
    

    我注意到,ongrid select不是很可靠。你只需要研究一下,当网格获得焦点并第一次被选中时,似乎不会触发被选中的单元格。

    以下是基于您发布的更新的代码。

    namespace GridSelection
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
       public partial class MainWindow : Window
       {
           MainWindowViewModel model = new MainWindowViewModel();
           public MainWindow()
           {
               InitializeComponent();
    
               BrandGrid.MouseDoubleClick += BrandGrid_MouseDown;
               OrdersGrid.InitializingNewItem += OrdersGrid_InitializingNewItem; ;
    
               this.Loaded += MainWindow_Loaded;
           }
    
        private void OrdersGrid_InitializingNewItem(object sender, InitializingNewItemEventArgs e)
        {
            Order newOrder = e.NewItem as Order;
            if (newOrder == null)
                return;
    
            if(model.CurrentBrand != null)
            {
                newOrder.UpdateRate(model.CurrentBrand.Rate);
            }
    
            model.BrandCollection.All(b =>
            {
                b.OnBrandSelect += (s, rate) =>
                {
                    newOrder.UpdateRate(model.CurrentBrand.Rate);
                    newOrder.CalculateTotal();
                }; return true;
            });
        }
    
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            model.Load();
            this.DataContext = model;
        }
    
        private void BrandGrid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            DataGrid grid = sender as DataGrid;
            Brand selectedbrand = grid.SelectedItem as Brand;
    
            if (selectedbrand == null)
                return;
    
            selectedbrand.InvokeBrandSelected();
        }
    }
    
       internal class MainWindowViewModel : INotifyPropertyChanged
       {
           private Brand currentBrand;
    
           public ObservableCollection<Brand> BrandCollection { get; private set; }
           public ObservableCollection<Order> OrderCollection { get; private set; }
    
           public Brand CurrentBrand
           {
               get { return currentBrand; }
               set
               {
                   currentBrand = value;
                   NotifyPropertyChanged();
               }
           }
    
           public void Load()
           {
               BrandCollection = new ObservableCollection<Brand>
               {
                  new Brand() { Name = "Brand One", Rate = 20 },
                  new Brand() { Name = "Brand Two", Rate = 30 },
                  new Brand() { Name = "Brand Three", Rate = 50 }
               };
    
               OrderCollection = new ObservableCollection<Order>();
           }
    
           public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
           {
               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
           }
    
           public event PropertyChangedEventHandler PropertyChanged;
       }
    
       public class Order : INotifyPropertyChanged
       {
          #region Private Variables
          private int length;
          private int quantity;
          private int totalArea;
          private double totalAmount;
          #endregion
    
          public Order()
          {
    
          }
    
           #region Properties
           private double Rate { get; private set; }
           public int Length
           {
             get { return length; }
             set
              {
                   length = value;
                   NotifyPropertyChanged();
    
                   CalculateArea();
                   CalculateTotal();
               }
           }
           public int Quantity
           {
               get { return quantity; }
               set
               {
                   quantity = value;
                   NotifyPropertyChanged();
    
                   CalculateArea();
                   CalculateTotal();
               }
           }
           public int TotalArea
           {
               get { return totalArea; }
               set
               {
                   totalArea = value;
                   NotifyPropertyChanged();
               }
           }
           public double TotalAmount
           {
               get { return totalAmount; }
               set
               {
                   totalAmount = value;
                   NotifyPropertyChanged();
               }
           }
           #endregion
    
           #region Methods
           private void CalculateArea()
           {
               TotalArea = this.Length * this.Quantity;
           }
    
           public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
           {
               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
           }
           public void CalculateTotal()
           {
               TotalAmount = this.TotalArea * this.Rate;
           }
           public void UpdateRate(double rate)
           {
               Rate = rate;
           }
           #endregion
    
           #region Events
           public event PropertyChangedEventHandler PropertyChanged;
           #endregion
       }
    
       public class Brand
       {
           public string Name { get; set; }
           public double Rate { get; set; }
    
           public void InvokeBrandSelected() => OnBrandSelect?.Invoke(this, Rate);
           public void UnsubscribeAll() => OnBrandSelect = null;
    
           public event EventHandler<double> OnBrandSelect;
       }
    }
    
        2
  •  0
  •   Bandook    7 年前

    为那些可能期望INotifyPropertyChanged有不同行为的人写这篇文章。

    我注意到实时值的变化不能用-

    public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    通过实时 我的意思是-当您在datagrid中更改一个值时,另一个值在不同的列中,同一行同时更改。而在上面的代码中,需要单击单元格以更新值。

    这种整洁的行为最好的方法是-

    public void NotifyPropertyChanged(string property)
    {
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    并将此方法称为NotifyPropertyChanged(yourVariableName);