블로그 이미지
* Microsoft MVP - Windows Development 2014 ~ 2020 http://youtube.com/FutureOfDotNet kaki104

카테고리

List All (593)
Visual Studio (2)
Blazor (2)
Windows App(Universa.. (97)
Xamarin Forms (4)
Bot Framework (19)
Azure (9)
Windows 10 (52)
WPF (6)
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)
Total537,180
Today2
Yesterday87

안녕하세요 Microsoft MVP 박문찬입니다.

7월 초에 강의할 때 사용한 교안을 공유합니다.

 

강의할 때 사용했던 것이라 자세한 설명은 없지만, 전체적으로 WPF 응용 프로그램 개발 기술들에 대해서 살펴 보실 수 있으며, 더 자세한 사항은 검색을 통해서 찾아보시면 자세하게 나올 것이라고 생각합니다.

 

Day1
* Visual Studio 2019
    * Tooling Improvements
    * .NET Refactorings and Code fixes
    * Live Share
    * Extensions and NuGet packages


* Reactive Extension(Rx) : 맛보기


* WPF
    * WPF overview
    * WPF = XAML + C#


* Controls
    * Layout
    * Input
    * Output
    * Collections
    * Menu & Container
    * ETC


* Animation


* 3rd party controls
    * WPF Component Vendors
    * ETC Component

 

 

Day2

* MVVM Pattern
    * MVVM Pattern overview
    * MvvmLight(NuGet) 
    * MVVM Pattern 구성
    * DataContext Property 
    * View & ViewModel connection 
   
 * Binding

    IValueConverter

    * Binding to Collections

    * Command


* WPF deep-dive1

    * Item Selection

    * Behavior

    * IMultiValueConverter

    * Style

    * Template

    * Selector

    * VisualState

 

 

Day3

* WPF deep-dive 2

    * SOLID Principle

    * List vs ObservableCollection

    * Synchronous vs Asynchronous

    * Resource

    * UserControl vs CustomControl

 

* MvvmLight

    * Navigation

    * MessengerInstance

    * SimpleIoc

 

* .NET Standard 2.0

    * .NET Standard 2.0 overview

 

* Database

    * SQLite overview

    * Demo

 

 

Day4

Prism Framework

    •Prism overview

    Prism Template Pack

    UnityContainer

    Commanding

    EventAggregator

    ViewModelLocator

    Modules

    User Interaction

    Regions

    UI Layout Concepts

    Navigation

 

Day5

* Reactive Extension(Rx)

    * Rx overview

    * .NET Event의 한계

    * Rx가 해결?

    * .NET Events와 Observable 비교

    * Observable

    * ReactiveUI

    * Observable.Range

    * Observable.Generate

    * Observable.FromEventPattern

    * DICT.org

    * DictService1

    * DictService2

    * DictService3

    * DictService4

    * DictService5

 

WPF Lecture_Day1.pdf
0.56MB
WPF Lecture_Day2.pdf
0.69MB
WPF Lecture_Day3.pdf
0.78MB
WPF Lecture_Day4.pdf
0.94MB
WPF Lecture_Day5.pdf
1.02MB

Posted by MVP kaki104

안녕하세요.

 

제가 강의를 진행하게 되었습니다. 강의에 대한 정보 및 신청은 아래 링크를 이용하시면 됩니다.

 

http://www.learningway.co.kr/training_desc.php?ctype=1&cidx=510&sidx=3106

 

7월 8-12일까지이고 오전 9시30분 부터 5시 30분까지 진행합니다. 즉, 하루종일이라는 뜻이죠;;

 

제목에 있는 ReactiveExtension(Rx)는 강의 첫날 이야기해서 제외 시킬 예정입니다. 처음하는 분들이 이해하고 사용하기에는 좀 문제가 있는 것 같아서.

 

교제로 사용할 녀석은 지금 열심히 만들고 있는 중입니다. 혹시 필요하신분들은 신청하시면 좋을 것 같습니다.

 

감사합니다.

 

Posted by MVP kaki104

WinForms, WPF 프로젝트에서 Windows 10 API를 사용하는 방법과 MSIX 패키징을 만들어서 배포하는 방법등을 설명합니다.

 

* 환경
* Microsoft.Windows.SDK.Contracts
* Microsoft.Toolkit.Wpf.UI.Controls
* XAML Islands Components Future
* Seamless application deployment, updates, and optimized for DevOps
* MSIX Core
* 트러블슈팅

 

https://youtu.be/oVJJMGlc_Ew


* 소스 :

https://github.com/kaki104/WpfTest

Posted by MVP kaki104

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

.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.

 

Posted by MVP kaki104