【问题标题】:How to preprocess/convert data of a databinding?如何预处理/转换数据绑定的数据?
【发布时间】:2013-05-18 15:28:58
【问题描述】:

我正在创建一个DataGrid,它将定义(通过用户输入)将在同一窗口上显示的曲线的 X 和 Y 值。该曲线将由包含一系列QuadraticBezierCurve 对象的Path 定义。

然而,虽然用户将输入路径将通过的点的坐标,但QuadraticBezierCurve 的数据包含有关其端点和控制点的信息,即 不是它会经过的点(直线除外)。我已经计算出如何计算曲线必须经过的三个坐标所定义的控制点,但现在我需要创建这条曲线。

是否可以将DataGrid 绑定到正在处理的“缓冲区”数据集(虽然是代码隐藏)并且此结果绑定到QuadraticBezierCurve

或者我是否需要根据用户输入删除现有路径并构建一个新路径?

【问题讨论】:

  • 问题解决了吗?
  • 很遗憾,这几天我没能解决这个问题。而且我是 MVVM 和 WPF 的初学者,所以我必须学习一点才能理解你的答案。 >.> 在我以前的项目中,我总是回避元数据和反射,但我认为这对于 MVVM 和 WPF 的功能非常重要,所以我最终不得不坐下来弄清楚这些东西。不过谢谢关心。希望在周末我能够实现这一点。

标签: c# wpf data-binding datagrid bezier


【解决方案1】:

通过将ItemsSource 绑定到一个集合,DataGrid 的每一行都将绑定到该集合中的一个项目。假设 MVVM,每个项目都是一个视图模型。该视图模型可以具有用户输入的值的属性,并且任何更改都可能导致在暴露控制点的另一个中重新计算。 UI 的另一部分可以绑定到同一个属性,因此会自动刷新。

【讨论】:

    【解决方案2】:

    正如 Kent 所说,在 MVVM 方法中,您可以定义一个具有两个集合的 ViewModel,一个用于用户输入数据,一个用于公开路径可以绑定到的转换后的项目。然后您需要定义何时需要刷新路径的集合。这意味着使用用户输入坐标手动订阅集合的CollectionChanged 事件(仅当用户可以添加或删除点时)和每个用户坐标的PropertyChanged 事件(在任何情况下)。这是否需要,取决于您是否要允许用户更改列表中已有的坐标。

    坐标在添加到列表后不会改变

    如果没有,还有一种更直接的方法,即使用 ValueConverter。由于您当时想要实现的是直接转换,我建议使用内置的 WPF 机制进行转换。这些适用于 MVVM 和非 MVVM 方法。

    定义一个转换器,将存储在 DataGrid 的 ItemsSource 的每个项目中的坐标转换为路径段。这包含您已经计算出的数学,仅此而已:

    public class CoordinateToPathSegment : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var coordinate = value as CurveViewModel.Coordinate;
            var segment = new QuadraticBezierSegment();
    
            // Set properties of quadratic bezier element
    
            return segment;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    您在应用程序/窗口/控件的资源中公开此转换器的实例。有关转换器的一般信息,请查看此处:WPFTutorial: Converters

    然后用这样的方式将它们连接在一起:

     <Path Data="{Binding ItemsSource, 
                          ElementName=CoordinateGrid, 
                          Mode=OneWay, 
                          Converter={StaticResource CoordinateToSegmentConverter}}" />
    

    通过这种方式,您的路径始终与数据网格同步,并且用户输入的坐标会自动转换为路径段,而无需您自己关心刷新。同样:请注意,现有 UserCoordinate 的更改不会转发到 UI,因为一旦坐标转换为 PathSegment,两者之间就不再有任何引用。只有在 DataGrid 中添加/删除坐标时,路径才会刷新。

    用户可以在添加到列表后更改坐标

    如果您想允许用户更改已定义坐标的单个值,还有更多内容。然后你需要得到肯特提议的方式。原理大概是这样的:

    public class CurveViewModel : ViewModelBase
    {
        private Collection<PathSegment> _segments;
        private readonly ObservableCollection<Coordinate> _userInputCoordinates;
    
        public ObservableCollection<Coordinate> UserInputCoordinates
        {
            get { return _userInputCoordinates; }
        }
    
        public Collection<PathSegment> Segments
        {
            get { return _segments; }
            private set
            {
                _segments = value;
                OnPropertyChanged(() => Segments);
            }
        }
    
        public CurveViewModel()
        {
            _userInputCoordinates = new ObservableCollection<Coordinate>();
    
            // Subscribe to refresh the path on adding/deleting ne coordinates
            UserInputCoordinates.CollectionChanged += UserInputCoordinates_CollectionChanged;
        }
    
        private void UserInputCoordinates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            switch (args.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    var newItems = args.NewItems.OfType<INotifyPropertyChanged>();
                    foreach (var coordinate in newItems)
                    {
                        // Subscribe to property change of a particular coordinate to refresh the 
                        // curve when user changes the values of an already existing coordinate data set
                        coordinate.PropertyChanged += Coordinate_PropertyChanged;
                    }
                    break;
    
                case NotifyCollectionChangedAction.Remove:
                    var oldItems = args.OldItems.OfType<INotifyPropertyChanged>();
                    foreach (var coordinate in oldItems)
                    {
                        // Unsubscribe to avoid memory leaks
                        coordinate.PropertyChanged -= Coordinate_PropertyChanged;
                    }
                    break;
            }
    
            // This refreshes the path when a coordinate has been added/removed
            RefreshPath();
        }
    
        private void Coordinate_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RefreshPath();
        }
    
        private void RefreshPath()
        {
            var segments = new Collection<PathSegment>();
    
            foreach (var userInputCoordinate in UserInputCoordinates)
            {
                var segment = new QuadraticBezierSegment();
                // Set properties here
                segments.Add(segment);
            }
    
            Segments = segments;
        }
    
        public class Coordinate : ViewModelBase
        {
            private double _xStart;
    
            public double XStart
            {
                get { return _xStart; }
                set
                {
                    _xStart = value;
                    OnPropertyChanged("XStart");
                }
            }
    
            // Analogous properties for YStart, XEnd, YEnd, XControl, YControl
        }
    }
    

    然后你将路径的Data绑定到Segments。我希望 cmets 或多或少地解释一下它是如何工作的。

    如果这回答了您的问题并解决了问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2021-12-16
      • 2017-08-23
      • 1970-01-01
      • 2011-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多