2011/12/03

.NET Framework - ListView

สวัสดีท่านผู้อ่านทุกท่านครับ

วันนี้ผมจะมาแนะนำวิธีใช้ ListView ซึ่งเป็น Control ตัวหนึ่งใน WPF ครับ

ก่อนอื่นเลยก็ต้องสร้าง Project ใหม่ เป็น WPF



แล้วก็มาเขียนโค้ด XAML กันเลย~



<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListView x:Name="listView">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="No." Width="Auto" DisplayMemberBinding="{Binding No}" />
                    <GridViewColumn Header="Name" Width="180" DisplayMemberBinding="{Binding Name}"/>
                    <GridViewColumn Header="Uri" Width="250" DisplayMemberBinding="{Binding Uri}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>


ผมได้สร้าง ListView ขึ้นมาหนึ่งตัว แล้วตั้งชื่อว่า listView แล้วกำหนด Column ไว้ 3 ตัว คือ No., Name และ Uri และกำหนดค่าที่อ่านใน Binding



เสร็จแล้วก็มาเขียนโค้ดคำสั่งกันครับ

ก่อนอื่นเลยก็เพิ่ม namespace System.Collections.ObjectModel ด้วยนะครับ


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ObservableCollection<WebListItem> WebCollection = new ObservableCollection<WebListItem>();
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            listView.ItemsSource = WebCollection;
            WebCollection.Add(new WebListItem { No = 1, Name = "Google", Uri = new Uri("http://www.google.com") } );
            WebCollection.Add(new WebListItem { No = 2, Name = "Google+", Uri = new Uri("http://plus.google.com") });
            WebCollection.Add(new WebListItem { No = 3, Name = "Gmail", Uri = new Uri("http://mail.google.com") });
            WebCollection.Add(new WebListItem { No = 4, Name = "Facebook", Uri = new Uri("http://www.facebook.com") });
            WebCollection.Add(new WebListItem { No = 5, Name = "MSDN", Uri = new Uri("http://msdn.microsoft.com") });
        }
    }

    class WebListItem
    {
        public int No { get; set; }
        public string Name { get; set; }
        public Uri Uri { get; set; }
    }
}


WebListItem เป็น class ที่สร้างขึ้นมาเพื่อเก็บข้อมูล แล้วก็ต้องตั้งชื่อ property ให้ตรงกับที่เขียนใน Binding ด้วยนะครับ
หลังจากนั้นก็สร้าง ObservableCollection<WebListItem> สำหรับเก็บค่าขึ้นมา แล้วกำหนดให้ listView ของเราดึงข้อมูลจาก collection ที่เราสร้าง

เป็นยังไงบ้างครับ ใช้ไม่ยากเลยใช่ไหมหล่ะครับ




2011/12/02

.NET Framework - Memory Leak เวลาใช้ System.Windows.Controls.Image

สวัสดีครับ เมื่อไม่กี่วันมานี้ผมเจอปัญหา Memory Leak ตอนใช้ System.Windows.Controls.Image ใน WPF มาครับ มาดูกันว่าผมทำยังไงมันถึงเกิดปัญหาขึ้น

ปัญหามีอยู่ว่า ผมโหลดภาพด้วย BitmapImage จำนวน 10 ภาพแล้วแสดงผลด้วย Image และผมต้องการที่จะลบภาพออก ปรากฏว่าเกิด Memory Leak ขึ้น โปรแกรมผมกิน Ram ไป เท่ากับตอนที่โหลดภาพ 10 ภาพ

เพื่อความสะดวกผมจะขอโหลดภาพขึ้นมาแค่ภาพเดียว เป็นไฟล์ png ขนาด 5.27MB ความละเอียด 1810x4034


public partial class MainWindow : Window
{
    Image img;

    public MainWindow()
    {
        InitializeComponent();
        img = new Image();
        stackPanel1.Children.Add(img);
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var bi = new BitmapImage();
        bi.BeginInit();
        bi.UriSource = new Uri(@"H:\img.png");
        bi.EndInit();
        if (bi.CanFreeze)
            bi.Freeze();
        img.Width = bi.PixelWidth;
        img.Height = bi.PixelHeight;
        img.Source = bi;
        bi = null;
        img.Visibility = System.Windows.Visibility.Visible;
    }

    private void button2_Click(object sender, RoutedEventArgs e)
    {
        img.Source = null;
        img.Visibility = System.Windows.Visibility.Collapsed;
    }
}

Code ข้างบนนี้เกิด Memory Leak


ผมลองเอา

img.Visibility = System.Windows.Visibility.Collapsed;

ออกปรากฏว่าปัญหา Memory Leak หายไป แต่ตัว Image ยังจองพื้นที่ใน Window อยู่จึงต้องกำหนดความกว้างกับความสูงเป็น 0 แทน

private void button2_Click(object sender, RoutedEventArgs e)
{
    img.Source = null;
    img.Width = img.Height = 0.0;
}

เท่านี้ก็ช่วยลด Memory ที่ใช้แล้ว ถึงจะลดได้ไม่หมด แม้ว่าจะลบ Image ออก แล้วใช้ GC.Collect() หน่วยความจำก็ไม่กลับคืนมาทั้งหมด
จะเห็นว่าตอนเปิดโปรแกรมใหม่ ๆ ใช้ Ram 14,596K
พอโหลดภาพมาแสดงใช้ Ram 72,984K
พอลบภาพออกจะใช้ Ram ลดลงเหลือ 45,032K

ตอนนี้ผมก็ยังไม่รู้จะแก้ได้ยังไง

2011/11/16

.NET Framework - #2 Windows Application


สวัสดีครับ เป็นยังไงกันบ้างครับ
วันนี้เราจะมาสร้าง Form ใน Windows กันครับ

เหมือนเดิมนะครับ ให้สร้าง Project เปล่า ๆ ขึ้นมา แล้วก็เพิ่ม References 3 ตัวคือ System, System.Drawing และ System.Windows.Forms

สำหรับ C++/CLI ให้เพิ่มตรง Project Properties นะครับ


F#, C#, VB.NET ก็เพิ่มตรง References ได้เลยครับ



แล้วก็ไปตั้งค่าอีกนิดนึงครับ
C#, VB.NET, F# ให้เข้าไปที่ Project Properties แล้วตั้ง Output type เป็น Windows Application



ส่วน C++/CLI ให้เข้าไปที่ Project Properties, ในส่วนของ Linker ตรง System เปลี่ยน SubSystem เป็น Windows และตรง Advanced เปลี่ยน Entry Point เป็น main




ตั้งค่าเสร็จแล้ว ก็มาเขียนโค้ดกันเลยครับ~~~~!!!


C++/CLI

using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;

[STAThread]
int main()
{
    Form ^f = gcnew Form();
    f->Text = "C++/CLI Form";
    Application::Run(f);
    return 0;
}


C#

using System;
using System.Drawing;
using System.Windows.Forms;

class Program
{
    [STAThread]
    static void Main()
    {
        Form f = new Form();
        f.Text = "C# Form";
        Application.Run(f);
    }
}


VB.NET

Option Strict On
Option Explicit On

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Module Program
    <STAThread>
    Sub Main()
        Dim f As New Form()
        f.Text = "VB.NET Form"
        Application.Run(f)
    End Sub
End Module


F#

open System
open System.Drawing
open System.Windows.Forms

[<STAThread>]
let f = new Form(Text = "F# Form")
Application.Run(f)



เป็นยังไงกันบ้างครับ เห็นไหมครับว่าภาษา .NET เหมือนกันโค้ดแทบไม่ต่างกันเลย ต่างกันแค่ไวยากรณ์ของภาษานั้น ๆ

วันนี้ก็เอาไว้เท่านี้นะครับ ขอให้สนุกกับการเขียนโปรแกรมครับผม ^w^

(โปรแกรมที่ผมใช้วันนี้คือ Visual Studio 11 Developer Preview นะครับ)

2011/11/11

F# - #3

สวัสดีครับ วันนี้เราก็มาฝึกเขียนภาษา F# กันเหมือนเดิมนะครับ
ก่อนอื่นผมจะขอพูดเกี่ยวกับฟังก์ชันอีกหน่อยนะครับ
คราวนี้ให้เปิดตัว F# Interactive (Console) นะครับ



แล้วลองพิมพ์ลงไปว่า

let f a b = a + b;;
let g(a, b) = a + b;;

จะเห็นว่า f กับ g จะไม่เหมือนกัน
ลองมาดูที่ฟังก์ชัน f กันก่อน

val f : int -> int -> int

หมายถึง f จะรับค่า int 2 ตัว แล้วส่งค่า int กลับมา
ส่วน g

val g : int * int -> int

หมายถึง g จะรับค่า int * int ที่เป็น tuple แล้วจะส่งค่า int กลับออกมา



คราวนี้เราลองมากำหนดชนิดของค่าที่รับเข้าไปกัน

let f (a : float) (b : float) = a + b;;

val f : float -> float -> float

จะเห็นว่าผลลัพท์ (ค่าที่ฟังก์ชันส่งกลับมา) จะเปลี่ยนด้วย

คราวนี้เรากลับมาที่ Visual Studio กันนะครับ
วันนี้เราจะมาดูวิธีเขียนเงื่อนไขกัน

let a = 10
if a < 20 then
    printfn "a < 20"
else
    printfn "a >= 20"

วิธีเขียนจะคล้าย ๆ Object Pascal กับ VB.NET

ส่วนเครื่องหมายจะมี
= เท่ากับ
< น้อยกว่า
> มากกว่า
<> ไม่เท่ากับ

แต่ถ้ามีหลายเงื่อนไข จะใช้ && (and) กับ || (or) เป็นตัวเชื่อม และ elif จะหมายถึง else if

let a = 10
if a < 20 && a >= 0 then
    printfn "0 <= a < 20"
elif a >= 20 then
    printfn "a >= 20"
else
    printfn "a < 0"

คราวนี้มาดูวิธีตรวจสอบค่ากันบ้าง ที่ในภาษา C/C++, C# เป็น switch case แต่ใน F# จะใช้ match with

let a = 4
match a with
| 0 -> printfn "Zero"
| 1 -> printfn "One"
| 2 | 3 -> printfn "2 or 3"
| x when a > 3 -> printfn "%i" x
| _ when a < -4 -> printfn "< -4"
| _ -> printfn "Other"

เครื่องหมาย | (pipe) จะเหมือนกับ case ในภาษา C#
| x when a > 3 หมายถึง ให้ค่าเป็น x ถ้า a > 3 หรือจะเขียนเป็น | x when x > 3 ก็ได้
| _ when a < -4 ไม่สามารถเขียนเป็น _ when _ < -4 ได้
นอกจากนี้ยังเขียนเป็น | x when x > 3 && x < 6 ได้อีกด้วย

ลองเขียนเคสอื่น ๆ ดูนะครับ

คราวนี้ลองมาดู resursive function (ฟังก์ชันเรียกตัวเอง) กันบ้าง

let rec fac n =
    match n with
    | _ when n < 0 -> failwith "n must >= 0"
    | 0 | 1 -> 1
    | _ -> n * fac(n - 1)

เราจะใช้ rec ต่อหลัง let เพื่อบอกว่า ฟังก์ชันที่จะสร้างเป็นฟังก์ชันแบบ recursive ครับ
ส่วน failwith หมายถึง ให้แสดง error ครับ ลองใส่ค่าติดลบดูก็ได้ครับ
ส่วนที่ต้องใส่วงเล็บ ตรง fac(n - 1) ก็เพราะว่า ถ้าเราเขียนเป็น n * fac n - 1 มันจะกลายเป็น n * fac(n) - 1

คราวนี้เราลองมารับค่ากันบ้างครับ
ก่อนอื่นเราต้องเรียก namespace System ขึ้นมาก่อน ด้วยคำสั่ง open เหมือนกับ using ใน C# และ Import ใน VB.NET

open System

printf "Enter a number: "
let a = int(Console.ReadLine())
printfn "%i" a

คราวนี้ถ้าจะรับข้อความเข้ามาทั้งบรรทัด

open System

printf "Enter a string: "
let a = Console.ReadLine()
printfn "%s" a

และเราจะแยกข้อความออกจากกันถ้าเจอเว้นวรรค

open System

printf "Enter a string: "
let a = Console.ReadLine().Split()
printfn "%A" a

ตอนนี้เราจะได้ a เป็น array นะครับ จึงไม่สามารถใช้ %s กับ a ได้
จากตรงนี้ลองพิมพ์ข้อความลงไปว่า Hello World
เราจะได้ [|"Hello"; "World"|]
ซึ่งเราจะเขียนแบบนี้ตอนสร้าง array ครับ

let a = [|1; 2; 3; 4|]
printfn "%A" a

สมาชิกแต่ละตัวจะใช้ ; (semi-colon) เป็นคั่น และใช้ [|   |] ครอบ

แล้วถ้าต้องการรับค่าเข้ามาแล้วแปลงเป็น int หล่ะจะทำยังไง
เช่น ใส่ค่าเข้ามาว่า 1 6 12 จะให้รับเข้ามาเป็น array [|1; 6; 12|]

open System

printf "Enter a string: "
let a = Array.map(fun x -> int x) (Console.ReadLine().Split())
printfn "%A" a

เราจะใช้คำสั่ง Array.map เข้ามาช่วย แล้วเขียนฟังก์ชันข้างใน
หรือจะเขียนว่า

let a = Console.ReadLine().Split() |> Array.map(fun x -> int x)

ก็ได้

แล้วเจ้า |> (pipe + >) มันคืออะไร ?

เครื่องหมาย |> นิยามโดย

let (|>) x f = f x

ลองดูกับฟังก์ชันง่าย ๆ

open System
let a = Math.Cos 5.0
printfn "%f" a

เราจะเขียนเป็น

let a = 5.0 |> Math.Cos

ก็ได้ มีความหมายเหมือนกัน

เริ่มงงกันแล้วใช่ไหมครับ งั้นวันนี้พอแค่นี้ก่อนละกันครับ เดี๋ยวผมจะมาพูดเรื่อง Array อีกทีนะครับ