Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Trang 1 của 2 12 CuốiCuối
Kết quả 1 đến 10 của 11
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Tạo một Rating Control trong lập trình C#

    Chào các bạn,

    Hôm nay, Fox xin phép được trình bày cách xây dựng một Rating Windows Control nền dotNET 2.0 .



    Đã có code và demo cho bản RC1 (theo bản quyền GPL v3) các bạn tải về xem nhé [IMG]images/smilies/smile.png[/IMG]

    I. GIỚI THIỆU
    Trong quá trình xây dựng phần mềm quản lý cho nhóm, đến module có tên "Chưng cầu dân ý", Fox muốn mọi thành viên trong nhóm cho điểm ý kiến đang xem xét. Nếu thể hiện bằng cách hiển thí String lên Label thì xoàng quá [IMG]images/smilies/biggrin.png[/IMG] (Bọn trong nhóm dám nói ý kiến cũa Fox là "xoàng"). Lòng tự trọng khiến Fox lao vào Google, CodeProject nghiên cứu và tạo control này cho bọn chúng tâm phục khẩu phục [IMG]images/smilies/smile.png[/IMG])

    II. Ý TƯỞNG
    Qua nhiều lần lênh đênh trên mạng, Fox đã có được mục tiêu thực hiện. Có một số ý kiến dùng đồ họa trong C# vẽ hình, số khác thì dùng PictureBox... Fox sẽ gộp cả 2 ý kiến trên để thực hiện.
    1. Công cụ
    a. IDE dành cho C#: VS 2005, SharpDevelop 2.2 (nếu dùng SharpDevelop 3.0 thì phải cài dotNET 3.5).
    b. Phần mềm thiết kế ảnh (Ở đây Fox dùng PhotoShop CS2 để vẽ, XnView để chỉnh sửa ảnh).
    2. Lên kế hoạch
    a. Kiểu dáng: hình sao và hình trái tim (do đây là 2 hình phổ biến).
    b. Mức điểm tối thiểu là 0, tối đa là 5; cho phép hiển thị điểm + 0.5.
    c. Mỗi kiểu dáng cần tối thiểu 3 hình (hình nền chỉ có viền tim hoặc viền sao, hình một nửa quả tim hoặc một nửa ông sao, hình full tim hoặc full sao).
    d. Rating Control của Fox cho phép người dùng rê chuột trên nó, rê đến đâu thì hiển thị hình đến đó. Ví dụ rê 2 ông sao thì vẽ 2 ông sao full và 3 sao rỗng; rê đến 2 ông sao và một nửa ông sao thứ 3 thì vẽ 2 ông sao full, 1 nửa ông sao và 2 ông sao rỗng...
    e. Muốn chọn số điểm thích hợp thì chỉ việc click chuột ngay hình sao tại điểm rê cuối cùng. Ví dụ: theo ví dụ trên thì click chuột ở 2 ông sao diểm là 2.0; click chuột ngay một nữa ông sao thứ 3 thì số điểm là 2.5 ...
    f. Cho phép chỉ một lần hoặc nhiều lần rate.
    g. Khi một user khác rate tiếp nhưng chưa click chọn điểm thì khi rê chuột khỏi control thì vẫn hiện thị số sao và số điểm đã được rate lần trước; nếu chọn điểm luôn thì hiển thị số điểm hiện tại và số điểm trước đó.

    III. CODING
    1. Vẽ
    Fox sẽ up mẫu sao và tim minh họa cho các bạn, nếu các bạn không thích có thể tự thiết kế mẫu riêng cho mình [IMG]images/smilies/smile.png[/IMG] . Ở đây fox dùng size 32x32 cho mục đích hướng dẫn, Fox khuyên nếu các bạn muốn áp dụng thực tế nên dùng cỡ 18x18 hoặc 24x24 là tốt nhất.
    2. Gõ
    a. Vào IDE new một project Class Library; trong project này new một UserControl (đặt tên tùy thích, ở đây Fox dùng tên RatingBar), sau đó switch UserControl này sang màn hình Coding (vì bạn không cần làm bất cứ thứ gì trên màn hình Designer cả).
    b. Khai báo một enum (bên ngoài Class) định dạng cho kiểu dáng của Control (theo mục a phần II.2 Lên kế hoạch)


    Mã:
    public enum IconStyle{    Star, // hình sao    Heart // hình trái tim}
    c. Trong class RatingBar fox khai báo 2 biến hằng (theo mục b phần II.2 Lên kế hoạch)


    Mã:
    public class RatingBar : UserControl{...     const byte ICON_DISTANCE = 1; // Khoãng cách giữa các hình    const byte MAX_ICONS = 5; // số điểm tối đa là 5. ...}
    d. Khai báo các biến toàn cục, delegate, event


    Để thực hiện việc ghi nhận điểm, ta viết một Class sau


    Mã:
     //Thực hiện mục g phần II.2 Lên kế hoạchpublic class RatingEventArgs : EventArgs    {        #region - field(s) -         private float mCurrentRate;        private float mLastRate;         #endregion         public RatingEventArgs(float current, float last)        {            mCurrentRate = current;            mLastRate = last;        }         public float CurrentRate        {            get { return mCurrentRate; }            set { mCurrentRate = value; }        }         public float LastRate        {            get { return mLastRate; }            set { mLastRate = value; }        }    } // Đăng ký sự kiện khi user bình chọn trên control. public delegate void OnRatingChangedEventHandler(object sender, RatingEventArgs e); [DefaultEvent("RatingValueChanged")]public class RatingBar : UserControl{...    // Sự kiện khi user bình chọn trên control    public event OnRatingChangedEventHandler RatingValueChanged;     float mRating = 0; // biến quản lý số điểm dự tính bình chọn.    float mRatingHolder = 0; // biến quản lý số điểm đã bình chọn.            bool mReadOnly = false; // cho phép xem không cho phép rate.    bool mRatingOnce = false; // Chỉ cho phép rate 1 lần.    bool mIsRated = false; // Kiểm tra đã rate hay chưa (dùng kèm với mRatingOnce).     Image mEmptyIcon; // Quản lý hình viền tim hoặc viền sao.    Image mHalfIcon; // Quản lý hình nửa tim hoặc nửa sao.     Image mFullIcon; // Quản lý hình full tim hoặc full sao.     IconStyle mIconStyle; // Kiểu dáng của control.     PictureBox mContainerBar; // Canvas để vẽ ...}
    e. Contructor


    Mã:
    public RatingBar(){      SetStyle(ControlStyles.SupportsTransparentBackColor, true); // cho phép có nền trong suốt.                  mContainerBar = new PictureBox();      mContainerBar.BackgroundImageLayout = ImageLayout.Center;      mContainerBar.MouseMove += new MouseEventHandler(ContainerBarOnMouseMoving); // Đăng kí sự kiện MouseMove (mục d phần II.2 Lên kế hoạch)      mContainerBar.MouseLeave += new EventHandler(ContainerBarOnMouseLeaving); // Đăng kí sự kiện MouseLeave (mục g phần II.2 Lên kế hoạch)      mContainerBar.MouseClick += new MouseEventHandler(ContainerBarOnMouseClicking); // Đăng kí sự kiện MouseClick (mục e phần II.2 Lên kế hoạch)      mContainerBar.Cursor = Cursors.Hand;      mContainerBar.Dock = DockStyle.Fill;       this.BackColor = Color.Transparent;      this.Controls.Add(mContainerBar);      this.UpdateIcons();      this.UpdateControlSize();       // draws empty icons first...      this.ReDraw();}
    f. Bỏ hình vào file Resource
    Các bạn mở file Resource của Rating Control (.resx) chọn Add Resource -> Add Exist File... và chọn các hình mẫu Fox gửi lên.

    g. Coding những events đã đăng kí cho PictureBox


    Mã:
    void ContainerBarOnMouseMoving(object sender, MouseEventArgs e)        {            if (mReadOnly || (mRatingOnce && mIsRated))                return;                        Bitmap bitmap = new Bitmap(mContainerBar.Width, mContainerBar.Height);                        Graphics graphic = Graphics.FromImage(bitmap);                        int i = 0; // mốc hoành độ bắt đầu vẽ.                        float tempRating = 0;                        byte tempIconsCounter = MAX_ICONS; // temporary variable to hold the iconsCount value, because we're decreasing it on each loop                        for (int a = 0; a < MAX_ICONS; a++) // lần lượt vẽ 5 hình sao rỗng hoặc tim rỗng.            {                if (e.X > i && e.X <= i + mEmptyIcon.Width / 2) // nếu rê đến những điểm một nửa sao hoặc nửa tim                {                    graphic.DrawImage(mHalfIcon, i, 0, mEmptyIcon.Width, mEmptyIcon.Height);                     i += ICON_DISTANCE + mEmptyIcon.Width;                     tempRating += 0.5f;                }                else if (e.X > i) // rê trọn hình                {                    graphic.DrawImage(mFullIcon, i, 0, mEmptyIcon.Width, mEmptyIcon.Height);                     i += ICON_DISTANCE + mEmptyIcon.Width;                    tempRating += 1.0f;                }                else // rê qua 5 hình                    break;                                tempIconsCounter--; //giảm số hình rỗng            }                        mRatingHolder = tempRating;                        this.DrawEmptyIcons(graphic, i, tempIconsCounter); // vẽ hình rỗng còn lại.                        graphic.Dispose();                        mContainerBar.BackgroundImage = bitmap; // hiển thị hình kết quả lên PictureBox        }                void ContainerBarOnMouseLeaving(object sender, EventArgs e)        {            if (mReadOnly || (mRatingOnce && mIsRated))                return;                        Bitmap bitmap = new Bitmap(mContainerBar.Width, mContainerBar.Height);                        Graphics graphic = Graphics.FromImage(bitmap);                        int i = 0;                        byte tempIconsCounter = MAX_ICONS;                        float tempRating = mRating;            while (tempRating > 0)            {                if (tempRating > 0.5f)                {                    graphic.DrawImage(mFullIcon, i, 0, mEmptyIcon.Width, mEmptyIcon.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd                    i += ICON_DISTANCE + mEmptyIcon.Width;                }                else if (tempRating == 0.5f)                {                    graphic.DrawImage(mHalfIcon, i, 0, mEmptyIcon.Width, mEmptyIcon.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd                    i += ICON_DISTANCE + mEmptyIcon.Width;                }                else                    break;                                tempIconsCounter--;                                tempRating--;            }                        this.DrawEmptyIcons(graphic, i, tempIconsCounter);             graphic.Dispose();                        mContainerBar.BackgroundImage = bitmap;        }                void ContainerBarOnMouseClicking(object sender, MouseEventArgs e)        {            float tempOldRate = mRating;             mRating = mRatingHolder;             mIsRated = true;             if (mRatingOnce)                mContainerBar.Cursor = Cursors.Default;            if (RatingValueChanged != null && tempOldRate != mRating)                RatingValueChanged(this, new RatingEventArgs(mRating, tempOldRate));        }
    h. Những method hỗ trợ


    Mã:
    void DrawEmptyIcons(Graphics g, int x, int count){       for (byte a = 0; a < count; a++)       {             g.DrawImage(mEmptyIcon, x, 0, mEmptyIcon.Width, mEmptyIcon.Height);              x += ICON_DISTANCE + mEmptyIcon.Width;       }} // Lấy kích cở cho controlvoid UpdateControlSize(){       int width = mEmptyIcon.Width * MAX_ICONS + (ICON_DISTANCE * (MAX_ICONS - 1));       int height = mEmptyIcon.Height;        this.Size = new Size(width, height);       this.MaximumSize = new Size(width, height); // Không cho mở rộng control khi thiết kế *một bug chưa fix  hoàn chỉnh được } // Khi chọn kiểu cho controlvoid UpdateIcons(){      if (mIconStyle == IconStyle.Star)      {            mEmptyIcon = RatingBar_Resources.EmptyStar; //            mFullIcon = RatingBar_Resources.FullStar;        // lấy trong những file ảnh trong resource.            mHalfIcon = RatingBar_Resources.HalfStar;       //      }      else      {            mEmptyIcon = RatingBar_Resources.EmptyHeart;            mFullIcon = RatingBar_Resources.FullHeart;            mHalfIcon = RatingBar_Resources.HalfHeart;      }       this.ReDraw(); // Vẽ lại nền} /// <summary>/// Redraws empty-icons when choose another icon-style./// </summary>private void ReDraw(){      Bitmap bitmap = new Bitmap(mContainerBar.Width, mContainerBar.Height);       Graphics graphic = Graphics.FromImage(bitmap);       this.DrawEmptyIcons(graphic, 0, MAX_ICONS);       graphic.Dispose();       mContainerBar.BackgroundImage = bitmap;}
    i. Properties dùng cho thiết kế


    Mã:
             public Image EmptyIcon        {            get { return mEmptyIcon; }        }                public Image HalfIcon        {            get { return mHalfIcon; }        }                public Image FullIcon        {            get { return mFullIcon; }        }         [DefaultValue(false)]        public bool ReadOnly        {            get { return mReadOnly; }            set            {                mReadOnly = value;                 if (value)                    mContainerBar.Cursor = Cursors.Default;                else                    mContainerBar.Cursor = Cursors.Hand;            }        }                [DefaultValue(false)]        public bool RatingOnce        {            get { return mRatingOnce; }            set            {                mRatingOnce = value;                                if (!value) mContainerBar.Cursor = Cursors.Hand; /* Set hand cursor, if false is set from true*/ }        }                [Browsable(false)]        public float Rate        {            get { return mRating; }        }         public Color BarBackColor        {            get { return mContainerBar.BackColor; }            set { mContainerBar.BackColor = value; }        }         [DefaultValue(typeof(IconStyle), "star")]        public IconStyle IconStyle // Chọn kiểu dáng cho control Tim hoặc Sao        {            get { return mIconStyle; }            set            {                mIconStyle = value;                UpdateIcons();            }        }
    Nhấn build project thôi [IMG]images/smilies/biggrin.png[/IMG]

    IV. ÁP DỤNG
    Tạo một project Windows Form khác trong cùng Solution và Add Reference đến Project chứa Control của chúng ta. Tạo một Form sau đó vào màn hình thiết kế sẽ có một control tên RatingBar cho các bạn kéo thả vào form [IMG]images/smilies/biggrin.png[/IMG]

    [old] Đáng tiết là Fox chưa thể up code cho các bạn (vài lý do tế nhị), chỉ có hướng dẫn và file demo và code cho Form thôi

    Đã up code và demo cho RC1 (theo bản quyền GPL v3).


    Nếu có gì thắc mắc hoặc phát hiện bug các bạn cứ trao đổi với Fox nhé.

    V. PHIÊN BẢN
    1. Ngày 20/03/2009:
    a. Fixing:
    _ Khi thay đổi property IconStyle từ Heart->Star và ngược lại sẽ vẽ lại lên PictureBox.
    _ Up code Beta 2
    b. Known bug:
    _ Chưa fix cho property AutoSize giống kiểu Label - dùng tạm property MaximumSize.
    _ Some unfix minor bugs...
    c. Things to do:
    _ Mặc định kiểu sao và kiểu tim (không cho user thay đổi resource)
    _ Sự kiện mặc định cho Control là RatingValueChanged (double-click vào Control sẽ tự động đăng ký)
    _ Fix minor bugs...

    2. Ngày 22/03/2009:
    a. Fixing:
    _ Mặc định kiểu sao và kiểu tim (không cho user thay đổi resource)
    _ Sự kiện mặc định cho Control là RatingValueChanged (double-click vào Control sẽ tự động đăng ký)
    _ Hỗ trợ màu nền trong suốt (Transparent).
    _ Fix minor bugs...
    _ Up to RC1 version (theo bản quyền GPL v3)

    c. Things to do:
    _ Thay đổi size của IconStyle cho control (16x16, 18x18, 24x24)
    _ Cho phép add control trên ToolStrip, MenuStrip hoặc StatusStrip trong quá trình design form.
    _ Fix minor bugs...
    _ Up to version 1.0.0 final.

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    fox viết rất hay , cám ơn bạn nhiêu nha. mong bạn sẽ có những bài viết đóng góp hón nữa.

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    gần 700 người vào đọc, có lẽ số người sử dụng qua Control này cũng nhiều, mà chả ai phát hiện bug hết sao 8-|? Nếu Control này Fox làm riêng không gộp vào frameworck chung thì giờ phiên bản sửa lỗi chắc lên v3.x rồi :-s

    Nếu các bạn hài lòng với bản này, chắc Fox hướng dẫn tạo lại Control trên bằng WPF i-) , cheer [IMG]images/smilies/biggrin.png[/IMG]

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    như tên gọi của nó, đây là một dạng thuộc tính bổ sung thông tin cho method, class, property,... hiểu như bạn cũng được. Trong forum cũng có 1 số bài hướng dẫn, bạn tìm đọc để biết thêm chi tiết.

  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    [DefaultEvent("RatingValueChanged")]
    [DefaultValue(false)]
    [Browsable(false)]
    Bạn có thể cho mình hỏi cách ý nghĩa của những dòng này ko? khi nào sử dụng? tại sao fai sử dụng nó? tra cứu nó ở đâu? Mong bạn giải thích giúp mình nhé. Mình còn kém trong lập trình lắm.

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bạn search trên MSDN các class trên, thêm đuôi Attribute vào là ra:
    DefaultEvent: ví dụ khi double click vào một Button thì nó sẽ nhảy vào event gì, đó là công dụng của attribute này
    DefaultValue: dịch sang tiếng việt
    Browsable: cho phép property có hiển thị lên cửa sổ Properties ko

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    vậy mấy cái dòng dạng [...] đặt ở đầu phương thức nào đó là giống như 1 cái công tắc để cho phép phương thức đó được làm gì. không biết mình hiểu nôm na như j có đúng không

    cái phần [..("tên")] là mình tự đặt hay sao?

  8. #8
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Trích dẫn Gửi bởi OWickedFox
    gần 700 người vào đọc, có lẽ số người sử dụng qua Control này cũng nhiều, mà chả ai phát hiện bug hết sao 8-|? Nếu Control này Fox làm riêng không gộp vào frameworck chung thì giờ phiên bản sửa lỗi chắc lên v3.x rồi :-s

    Nếu các bạn hài lòng với bản này, chắc Fox hướng dẫn tạo lại Control trên bằng WPF i-) , cheer [IMG]images/smilies/biggrin.png[/IMG]
    ok bạn làm hướng dẫn bên WPF luôn nha

  9. #9
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Code hơi dài. Không cần sử dụng PictureBox và vẽ đi vẽ lại trong mousemove và mouseleave.Chỉ cần viết hàm vẽ, bắt sự kiện paint của control và vẽ ở trong đó. còn các hàm liên quan đến mouse chỉ tính toán tọa độ thôi,sau khi tính toán xong gọi Invalidate() để vẽ lại control.
    Mình chưa thấy bạn xử lý sự kiện Resize control. Nếu không xử lý sự kiện này, khi control resize (lúc đưa lên form rồi) thì mình nghĩ vẽ sẽ không chính xác nữa. (Chưa thử cái code của bạn: nếu có gì không đúng thì bỏ qua nhé.)

  10. #10
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    @Y2: Cám ơn bạn đã giúp đỡ giải thích các câu hỏi tại topic này, dạo này Fox bận quá ít vào CViet

    @itkttn: Cám ơn bạn đã góp ý, những ý kiến của bạn mình đã sửa và cập nhật từ lâu rồi. Nhưng nhưng bản cập nhật mình bỏ trên SVN server của công ty cũ rồi, nó là một trong những Control thuộc bộ lib mình phát triển cho họ... để bữa nào mình liên lạc họ để xin làm hướng dẫn trên CViet. Hy vọng họ đồng ý [IMG]images/smilies/laughing.gif[/IMG] vì bữa có to tiếng với cha manager mới dẫn đến bức xúc nghĩ việc [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

 

 
Trang 1 của 2 12 CuốiCuối

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •