Форум программистов, компьютерный форум, киберфорум
Наши страницы
setood
Войти
Регистрация
Восстановить пароль
Оценить эту запись

WPF. DataGrid итоги выделенных ячеек как в Excel

Запись от setood размещена 08.06.2019 в 15:49

Приветствую!
В Excel есть прикольная штука, выделил ячейки, и по ним сразу считается сумма, среднее, максимум и минимум.
Нажмите на изображение для увеличения
Название: excel.png
Просмотров: 23
Размер:	6.9 Кб
ID:	5384
Давайте получим тоже самое в WPF DataGrid
1. Добавляем класс CellsSelectionBehavior (код в конце).
2. Добавляем reference: System.Windows.Interactivity (через NuGet).
3. Подключаем Behavior к нашему DataGrid:
XML
1
2
3
4
5
<DataGrid Grid.Row="0" AutoGenerateColumns="False" SelectionUnit="Cell" ItemsSource="{Binding Path=Items, ElementName=_this}">
            <i:Interaction.Behaviors>
                <cellsBeh:CellsSelectionBehavior x:Name="dgb1" />
            </i:Interaction.Behaviors>
...
4.. Делаем привязки к объекту Behaviors:
XML
1
2
3
4
5
6
<TextBlock Grid.Column="0">Sum:  <Run Text="{Binding ElementName=dgb1, Path=Sum, Mode=OneWay}"/></TextBlock>
<TextBlock Grid.Column="1">Max:  <Run Text="{Binding ElementName=dgb1, Path=Max, Mode=OneWay}"/></TextBlock>
<TextBlock Grid.Column="2">Min:  <Run Text="{Binding ElementName=dgb1, Path=Min, Mode=OneWay}"/></TextBlock>
<TextBlock Grid.Column="3">Average:  <Run Text="{Binding ElementName=dgb1, Path=Average, Mode=OneWay}"/></TextBlock>
<TextBlock Grid.Column="4">Count:  <Run Text="{Binding ElementName=dgb1, Path=Count, Mode=OneWay}"/></TextBlock>
<TextBlock Grid.Column="5">CountNotNull:  <Run Text="{Binding ElementName=dgb1, Path=CountNotNull, Mode=OneWay}"/></TextBlock>
Наслаждаемся:
Нажмите на изображение для увеличения
Название: result.png
Просмотров: 26
Размер:	13.1 Кб
ID:	5385

Так же хочется отметить, что значения из ячеек берутся из свойства ClipboardContentBinding (То что сохраняется при нажатии Ctrl+C,V). Если у Вас специфическая ячейка, нужно переопределить это свойство, заодно и копирование работать будет

Код CellsSelectionBehavior:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
    public class CellsSelectionBehavior : Behavior<DataGrid>, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ICollection<object> Values
        {
            get { return (ICollection<object>)GetValue(ValuesProperty); }
            set { SetValue(ValuesProperty, value); }
        }
        public static readonly DependencyProperty ValuesProperty =
            DependencyProperty.Register("Values", typeof(ICollection<object>), typeof(CellsSelectionBehavior), new PropertyMetadata(Array.Empty<object>()));
 
        public double? Sum => Values?.Sum(value => ExtractDouble(value));
        public double? Max => Values?.Max(value => ExtractDouble(value));
        public double? Min => Values?.Min(value => ExtractDouble(value));
        public double? Average => Values?.Average(value => ExtractDouble(value));
        public int? Count => Values?.Count();
        public int? CountNotNull => Values?.Count(v=>v != null);
 
 
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.SelectedCellsChanged += this.AssociatedObject_SelectedCellsChanged;
        }
        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.SelectedCellsChanged -= this.AssociatedObject_SelectedCellsChanged;
        }
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }
        private void AssociatedObject_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            DataGrid dg = sender as DataGrid;
            var Values = dg.SelectedCells.Select(cell => GetValueFromClipBoard(cell));
            this.SetValue(ValuesProperty, Values.ToArray());
            OnPropertyChanged("Sum");
            OnPropertyChanged("Max");
            OnPropertyChanged("Min");
            OnPropertyChanged("Average");
            OnPropertyChanged("Count");
            OnPropertyChanged("CountNotNull");
 
        }
        private static object GetValueFromClipBoard(DataGridCellInfo cellInfo)
        {
            string path = (cellInfo.Column.ClipboardContentBinding as Binding)?.Path?.Path;
            object item = cellInfo.Item;
            var pathParts = path?.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            pathParts?.ForEach(p =>
            {
                var itype = item.GetType();
                var property = itype.GetProperty(p);
                item = property.GetValue(item);
 
            });
            return item;
        }
 
        private static double? ExtractDouble(object o)
        {
            if (o is double || o is double? || o == null)
                return (double?)o;
            double temp = 0; 
            bool isSuccess = double.TryParse(o.ToString(), out temp);
            return isSuccess ? temp : (double?)null;
        }
    }

Код MainWindow.Xaml:
Кликните здесь для просмотра всего текста
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<Window x:Class="WpfApp5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp5"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d"
        Name="_this"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
 
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0">Sum:  <Run Text="{Binding ElementName=dgb1, Path=Sum, Mode=OneWay}"/></TextBlock>
            <TextBlock Grid.Column="1">Max:  <Run Text="{Binding ElementName=dgb1, Path=Max, Mode=OneWay}"/></TextBlock>
            <TextBlock Grid.Column="2">Min:  <Run Text="{Binding ElementName=dgb1, Path=Min, Mode=OneWay}"/></TextBlock>
            <TextBlock Grid.Column="3">Average:  <Run Text="{Binding ElementName=dgb1, Path=Average, Mode=OneWay}"/></TextBlock>
            <TextBlock Grid.Column="4">Count:  <Run Text="{Binding ElementName=dgb1, Path=Count, Mode=OneWay}"/></TextBlock>
            <TextBlock Grid.Column="5">CountNotNull:  <Run Text="{Binding ElementName=dgb1, Path=CountNotNull, Mode=OneWay}"/></TextBlock>
        </Grid>
        <DataGrid Grid.Row="0" AutoGenerateColumns="False" SelectionUnit="Cell" 
                  ItemsSource="{Binding Path=Items, ElementName=_this}">
            <i:Interaction.Behaviors>
                <local:CellsSelectionBehavior x:Name="dgb1" />
            </i:Interaction.Behaviors>
            <DataGrid.Columns>
                <DataGridTextColumn Header="DataGridTextColumn" Binding="{Binding Bproperty.Name, Mode=OneWay}"/>
                <DataGridComboBoxColumn Header="DataGridComboBoxColumn" SelectedItemBinding="{Binding Bproperty.Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                        
                                        >
                    <DataGridComboBoxColumn.ItemsSource>
                        <x:Array Type="sys:Object" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
                            <sys:Int32>0</sys:Int32>
                            <sys:Int32>1</sys:Int32>
                            <sys:Double>2.2</sys:Double>
                            <sys:Int32>3</sys:Int32>
                            <sys:Int32>4</sys:Int32>
                            <sys:Int32>5</sys:Int32>
                        </x:Array>
                    </DataGridComboBoxColumn.ItemsSource>
                </DataGridComboBoxColumn>
                <DataGridTemplateColumn Header="DataGridTemplateColumn" ClipboardContentBinding="{Binding Bproperty.Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock>
                                    kk:
                                </TextBlock>
                                <TextBlock Text="{Binding Bproperty.Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Код MainWindow.xaml.cs:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        public A[] Items { get; set; } = new A[]
       {
            new A(){ Bproperty = new B(){Name= "q", Number =null }},
            new A(){ Bproperty = new B(){Name= "w", Number = 1}},
            new A(){ Bproperty = new B(){Name= "e", Number = 2.2}},
            new A(){ Bproperty = new B(){Name= "r", Number =3 }},
            new A(){ Bproperty = new B(){Name= "t", Number = 4}},
       };
 
 
        public class A
        {
            public B Bproperty { get; set; }
        }
        public class B
        {
            public string Name { get; set; }
            public object Number { get; set; }
        }
    }
Размещено в Без категории
Просмотров 151 Комментарии 2
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для ashsvis
    Небось делаете свой собственный табличный процессор?
    Запись от ashsvis размещена 09.06.2019 в 16:04 ashsvis вне форума
  2. Старый комментарий
    Цитата:
    Сообщение от ashsvis Просмотреть комментарий
    Небось делаете свой собственный табличный процессор?
    Увы) в нашем CRM много, крайне специфичных, отчетов, и в половине из них эта функция весьма удобна.

    Сам удивился, что не смог найти решения этой задачи в интернете
    Запись от setood размещена 09.06.2019 в 16:40 setood вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru