블로그 이미지
* Microsoft MVP - Windows Development 2014 ~ 2019 5ring https://www.facebook.com/groups/w10app/ kaki104

카테고리

List All (587)N
Blazor (1)N
Windows App(Universa.. (97)
Xamarin Forms (4)
Bot Framework (19)
Azure (9)
Windows 10 (52)
WPF (4)
Facebook News & Tips (158)
Windows 8&8.1 (113)
Windows Phone 8 (42)
Silverlight (37)
HTML5 & MVC4 (16)
Portable Class Library (2)
Uncategorised Tips a.. (3)
Kinect for Windows (2)
ETC (12)
kaki104 Scrap (4)
App News (11)
Total533,854
Today0
Yesterday21

'2018/12'에 해당되는 글 2건

  1. 2018.12.20 FlexGrid MergerManager 성능 개선 셈플 및 그래프
  2. 2018.12.19 GrapeCity WPF Edition (C1) tip

C1FlexGrid에 Custom MergeManager를 만들어서 사용하는 방법에 대한 예제가 모두 grid[r,c]에서 값을 가지고 와서 비교하도록 되어 있는데, 이 부분이 성능에 많은 영향을 줍니다. 


그래서, CollectionView에서 데이터를 직접 찾아서 비교하는 방법으로 성능을 50% 이상 올릴 수 있는 방법을 셈플로 만들어 보았습니다.


위의 성능 프로파일러만 보더라도 확연히 차이가 나는 것을 알 수 있습니다.


MainWindow.xaml


<Window x:Class="FlexGridMergeManagerSampleForWPF.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:FlexGridMergeManagerSampleForWPF"
        xmlns:c1="http://schemas.componentone.com/winfx/2006/xaml"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button Content="Using grid[r,c]" Click="ButtonBase_OnClick" />
            <Button Content="Using CollectionView" Click="ButtonBase_OnClick" />
        </StackPanel>
        <c1:C1FlexGrid x:Name="FlexGrid" Grid.Row="1"
                       HeadersVisibility="All"
                       AutoGenerateColumns="False"
                       AllowMerging="All"
                       GridLinesVisibility="All"
                       GridLinesBrush="LightGray"
                       FrozenLinesBrush="#FF2A4C80"
                       ColumnHeaderBackground="#FFDBE1E9"
                       RowHeaderBackground="#FFDBE1E9"
                       TopLeftCellBackground="#FFBAD2F5"
                       IsReadOnly="True"
                       AllowResizing="Both"
                       AllowSorting="True"
                       ShowMarquee="True"
                       ClipboardCopyMode="ExcludeHeader">
            <c1:C1FlexGrid.Columns>
                <c1:Column Binding="{Binding OrderID}" AllowMerging="False" />
                <c1:Column Binding="{Binding ProductID}" AllowMerging="True" />
                <c1:Column Binding="{Binding ProductName}" AllowMerging="True"/>
                <c1:Column Binding="{Binding UnitPrice}" AllowMerging="True" />
                <c1:Column Binding="{Binding Quantity}" AllowMerging="True" />
                <c1:Column Binding="{Binding Discount}" AllowMerging="True" />
                <c1:Column Binding="{Binding ExtendedPrice}" AllowMerging="True" />
            </c1:C1FlexGrid.Columns>
        </c1:C1FlexGrid>
    </Grid>
</Window>



MainWindow.xaml.cs


using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using C1.WPF.FlexGrid;

namespace FlexGridMergeManagerSampleForWPF
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private const string CASE1 = "Using grid[r,c]";
        private const string CASE2 = "Using CollectionView";

        public MainWindow()
        {
            InitializeComponent();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            var button = (Button) sender;
            switch (button.Content.ToString())
            {
                case CASE1:
                    BindGrid(CASE1);
                    break;
                case CASE2:
                    BindGrid(CASE2);
                    break;
            }
        }

        private void BindGrid(string type)
        {
            FlexGrid.MergeManager = new SampleMergeManager(type);

            var allText = File.ReadAllText("sample.csv");
            if (string.IsNullOrEmpty(allText)) return;
            var lines = allText.Split('\n');

            var items = (from line in lines.Skip(1)
                where string.IsNullOrEmpty(line) == false
                let columns = line.Split(',')
                select new SampleModel
                {
                    OrderID = Convert.ToInt32(columns[0]),
                    ProductID = Convert.ToInt32(columns[1]),
                    ProductName = columns[2],
                    UnitPrice = Convert.ToDouble(columns[3]),
                    Quantity = Convert.ToInt32(columns[4]),
                    Discount = Convert.ToDouble(columns[5]),
                    ExtendedPrice = Convert.ToDouble(columns[6].Replace("\r", ""))
                }).ToList();

            // bind grids to ListCollectionView
            FlexGrid.ItemsSource = items;
        }

        private class SampleMergeManager : IMergeManager
        {
            private static long _count;
            private readonly string _getDataType;

            public SampleMergeManager(string type)
            {
                _getDataType = type;
            }

            public CellRange GetMergedRange(C1FlexGrid grid, CellType cellType, CellRange rng)
            {
                if (cellType != CellType.Cell) return rng;
                var col = grid.Columns[rng.Column];
                if (col.AllowMerging == false) return rng;

                for (var i = rng.Row; i < grid.Rows.Count - 1; i++)
                {
                    if (CompareNext(grid, i, rng.Column))
                        break;
                    rng.Row2 = i + 1;
                }

                for (var i = rng.Row; i > 0; i--)
                {
                    if (ComparePrev(grid, i, rng.Column))
                        break;
                    rng.Row = i - 1;
                }

                _count++;
                Debug.WriteLine($"count : {_count}");
                return rng;
            }

            private bool CompareNext(C1FlexGrid grid, int r, int c)
            {
                if (_getDataType == CASE1)
                    return grid[r, c]?.ToString() != grid[r + 1, c]?.ToString();

                var col = grid.Columns[c];
                if (!(grid.CollectionView is ListCollectionView collection)) return false;
                var item1 = collection.GetItemAt(r);
                var item2 = collection.GetItemAt(r + 1);

                var property = item1.GetType().GetProperty(col.Binding.Path.Path);
                if (property == null) return false;
                var val1 = property.GetValue(item1);
                var val2 = property.GetValue(item2);

                return val1?.ToString() != val2?.ToString();
            }

            private bool ComparePrev(C1FlexGrid grid, int r, int c)
            {
                if (_getDataType == CASE1)
                    return grid[r, c]?.ToString() != grid[r - 1, c]?.ToString();

                var col = grid.Columns[c];
                if (!(grid.CollectionView is ListCollectionView collection)) return false;
                var item1 = collection.GetItemAt(r);
                var item2 = collection.GetItemAt(r - 1);

                var property = item1.GetType().GetProperty(col.Binding.Path.Path);
                if (property == null) return false;
                var val1 = property.GetValue(item1);
                var val2 = property.GetValue(item2);

                return val1?.ToString() != val2?.ToString();
            }

        }
    }
}


소스

https://github.com/kaki104/BasicSamples/tree/master/FlexGridMergeManagerSampleForWPF


Posted by MVP kaki104

GrapeCity WPF Edition (C1) tip

WPF / 2018.12.19 13:45


WPF 프로젝트를 하다보면, 기존에 WinForm에서 사용하던 형태의 그리드를 필요로 하는 경우가 많습니다.

특히 그리드에 머지 기능이 필요한 경우에는 Telerik RadGridView 보다 C1의 FlexGrid를 이용하는 것이 더 좋을 때도 있습니다.


FlexGrid를 이용해서 프로젝트를 할 때 버그나 Tip을 정리하도록 하겠습니다.

사용한 버전은 C1.WPF.FlexGrid version 4.0.20173.580입니다.



1. 무한 Custom MergeManager 호출


- FlexGrid에 AllowMerging이 활성화 되어 있고, MergeManager에 Custom MergeManager를 만들어서 연결한 경우

- ShowMarquee="True"로 설정하고

- 어플리케이션을 실행하고, FlexGrid를 클릭하면


Custom MergeManager를 무한 호출을 합니다. 이런 내용을 의도했다고 보기는 어려우니 버그이지 않을까 생각됩니다.

최신 버전에서는 이런 현상이 발생하지 않습니다.



2. WPF FlexGrid에 AllowMerging에 RestrictXXX 기능을 사용할 수 없습니다.


WinForm용 FlexGrid에는 RestrictAll, RestrictCols, RestrictRows 기능을 사용할 수 있어서, 머지를 할 때 왼쪽이나 위에 있는 데이터의 머지 상태에 따라서 머지를 해주는데, WPF용에는 해당 기능이 빠져있습니다. 그래서, Custom MergeManager를 이용해서 구현해야 하는 불편함이 있습니다.



3. Custom MergeManager 성능 향상


MergeManager를 만들어서 사용할려고, 검색을 해보면, 대부분의 자료는 아래 메소드를 호출해서 그리드의 데이터를 가지고 온 후에 비교를 하는 방식을 사용하라고 되어 있습니다.


    string GetDataDisplay(C1FlexGrid grid, int r, int c
   

       
return grid[r, c].ToString(); 
    } 


그런데, 여기서 문제는 grid[r, c].ToString() 컨트롤의 특정 셀을 찾고, 그 셀에서 값을 가지고 오는 과정에서 엄청난 성능 저하가 발생한다는 것입니다.


그래서, 컨트롤에 연결된 데이터에서 값을 찾아서 처리하는 방법을 만들었습니다.

        public class SampleMergeManager : IMergeManager
        {
            private static long Count;

            public CellRange GetMergedRange(C1FlexGrid grid, CellType cellType, CellRange rng)
            {
                if (cellType != CellType.Cell)
                    return rng;

                //if (rng.Column == 0)
                //    return rng;
                var vRange = grid.ViewRange;

                var itemsSource = grid.ItemsSource;

                for (int i = rng.Row; i < grid.Rows.Count - 1; i++)
                {
                    if (CompareNext(grid, i, rng.Column))
                        break;
                    rng.Row2 = i + 1;
                }

                for (int i = rng.Row; i > 0; i--)
                {
                    if (ComparePrev(grid, i, rng.Column))
                        break;
                    rng.Row = i - 1;
                }

                Count++;
                //Debug.WriteLine($"count : {Count} row1 : {rng.Row}, row2 : {rng.Row2}, col1 : {rng.Column}, col2 : {rng.Column2}");
                return rng;
            }

            private bool CompareNext(C1FlexGrid grid, int r, int c)
            {
                var col = grid.Columns[c];
                var collection = grid.CollectionView as ListCollectionView;

                var item1 = collection.GetItemAt(r);
                var item2 = collection.GetItemAt(r + 1);

                var property = item1.GetType().GetProperty(col.Binding.Path.Path);
                var val1 = property.GetValue(item1);
                var val2 = property.GetValue(item2);

                return val1?.ToString() != val2?.ToString();
            }

            private bool ComparePrev(C1FlexGrid grid, int r, int c)
            {
                //return grid[r, c]?.ToString() != grid[r - 1, c]?.ToString();
                var col = grid.Columns[c];
                var collection = grid.CollectionView as ListCollectionView;

                var item1 = collection.GetItemAt(r);
                var item2 = collection.GetItemAt(r - 1);
                var property = item1.GetType().GetProperty(col.Binding.Path.Path);
                var val1 = property.GetValue(item1);
                var val2 = property.GetValue(item2);

                return val1?.ToString() != val2?.ToString();
            }

        }


Posted by MVP kaki104