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

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