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

카테고리

List All (553)
Xamarin Forms (4)
Bot Framework (19)
Azure (9)
Windows 10 (35)
WPF (3)
Facebook News & Tips (158)
Windows App(Universa.. (83)
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)
Total527,527
Today0
Yesterday86

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


'WPF' 카테고리의 다른 글

FlexGrid MergerManager 성능 개선 셈플 및 그래프  (0) 2018.12.20
GrapeCity WPF Edition (C1) tip  (0) 2018.12.19
.NetFrame 4.5 working tip  (0) 2013.06.12
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();
            }

        }


'WPF' 카테고리의 다른 글

FlexGrid MergerManager 성능 개선 셈플 및 그래프  (0) 2018.12.20
GrapeCity WPF Edition (C1) tip  (0) 2018.12.19
.NetFrame 4.5 working tip  (0) 2013.06.12
Posted by MVP kaki104

.NetFrame 4.5 working tip

WPF / 2013.06.12 13:30

4.5에서만 되는 부분은 아니라도 참고 사항 정리

(Silverlight, Store app xaml과의 다른 점)

 

1. ResourceDictionary에 있는 ItemTemplate에 Command binding 방법

실버라이트와 Store app의 xaml과는 다른 방법으로 바인딩을 하는군요..

찾는데 약 1시간 정고 걸린듯..

 

    <DataTemplate x:Key="TGroupItemTemplate">
        <Grid d:DesignWidth="180" d:DesignHeight="50" Margin="5">
            <telerik:RadRadioButton Content="{Binding NM_TGROUP}" Padding="0,5,0,5" Height="50"
                               Command="{Binding DataContext.TGroupClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
                               CommandParameter="{Binding CD_TGROUP}" GroupName="{Binding CD_IDNUM}" IsChecked="{Binding IsChecked, Mode=TwoWay}"
                               IsEnabled="{Binding FG_USE, ConverterParameter=0|nor, Converter={StaticResource AnyDataToBoolConverter}}" />
        </Grid>
    </DataTemplate>

 

2. DynamicResource

스타일을 잡아서 사용하는데 StaticResource가 아닌 DynamicResource로 표현이 되는..

이것도 역시 다른 넘들이랑 다르군요

 

            <Border Grid.Column="3" Style="{DynamicResource GrayBorderStyle}">
                <TextBlock Text="*사용자" VerticalAlignment="Center" />
            </Border>

 

3. EventToCommandBehavior

커맨드를 사용할 때 커맨드 파라메터로 args를 넘겨주는 Behavior

잘 동작함, 커맨드는 자신이 사용하는 커맨드를 그냥 사용해도 됨

 

원본링크

http://stackoverflow.com/questions/6205472/mvvm-passing-eventargs-as-command-parameter

 

참고소스(적용)

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

xmlns:b="clr-namespace:Sample.Behaviors"

 

            <Frame x:Name="frameMain" NavigationUIVisibility="Hidden" >
                <i:Interaction.Behaviors>
                    <b:EventToCommandBehavior Event="Navigated" Command="{Binding NavigatedCommand}" PassArguments="True"/>
                    <b:EventToCommandBehavior Event="NavigationFailed" Command="{Binding NavigationFailedCommand}" PassArguments="True"/>
                    <b:EventToCommandBehavior Event="Navigating" Command="{Binding NavigatingCommand}" PassArguments="True"/>
                </i:Interaction.Behaviors>
            </Frame>

 

4. Error: "The underlying provider failed on Open" in Entity Framework application

http://blogs.msdn.com/b/dataaccesstechnologies/archive/2012/08/09/error-quot-the-underlying-provider-failed-on-open-quot-in-entity-framework-application.aspx

User Instance=True;

 

The connection was not closed. The connection's current state is connecting

 

5. The context cannot be used while the model is being created

 

6. Non-static method requires a target.

Table에 데이터가 없을때 Getting을 이용해서 조회를 하면 나는 오류

 

7. EF error

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

 

'WPF' 카테고리의 다른 글

FlexGrid MergerManager 성능 개선 셈플 및 그래프  (0) 2018.12.20
GrapeCity WPF Edition (C1) tip  (0) 2018.12.19
.NetFrame 4.5 working tip  (0) 2013.06.12
Posted by MVP kaki104

티스토리 툴바