Giới thiệu về UserControl

UserControl là một loại control do người dùng định nghĩa, thiết kế giao diện, chức năng cho nó. Cách sử dụng của UserControl cũng tương tự như các loại control thông thường khác. Sau khi xây dựng hoàn chỉnh, lập trình viên có thể sử dụng lại UserControl đó cho nhiều ứng dụng, nhiều cửa sổ khác nhau rất đơn giản. Các loại UserControl đều kế thừa từ class System.Windows.Controls.UserControl.


Xây dựng UserControl

Trong phần này, tôi sẽ demo cách tạo và sử dụng một loại usercontrol mà tôi đã làm cho đồ án cuối kì của mình: CarRacer. Một số tính năng chính của control này bao gồm: cho phép bạn load hình ảnh của thân xe vào, property quy định khoảng cách tối đa xe sẽ chạy (chiều dài của đường đua) và vận tốc của xe, các animation mô phỏng chuyển động của xe, một event cho phép bạn đăng kí phương thức sẽ xử lí khi xe cán đích (vượt qua chiều dài đường đua).

Dưới đây là hình ảnh của một control CarRacer. Nó bao gồm 3 phần: thân xe, bánh trước, bánh sau. Ba phần này đều được cắt ra từ một hình ảnh duy nhất (size 100 x 55 px), do đó để đảm bảo về kích thước, ta sẽ sử dụng size mặc định của hình ảnh làm kích thước của CarRacer.

Để tạo ra sự khác biệt của các CarRacer khác nhau, chúng ta sẽ sử dụng phần thân xe có màu sắc khác nhau.

Đầu tiên, bạn cần tạo ra một project trong Visual Studio, loại project mà chúng ta tạo là WPF User Control Library như hình bên dưới. Ở đây tôi đặt tên project là CarRacer.

Vào phần Solution Explorer của project, bạn có thể xóa file UserControl1.xaml và UserControl1.xaml.cs mà VS tạo sẵn. Click chuột phải vào project, chọn Add -> New Item. Chọn đến mục WPF trong Installed Template, chọn UserControl(WPF), đặt tên là CarRacer.

Xây dựng giao diện

Trước tiên, bạn add tất cả các hình ảnh có trong folder CarImage vào project. Các hình ảnh này bao gồm bánh xe trước, sau và 2 thân xe khác màu nhau. Trong file CarRacer.xaml, sửa lại DesignHeight và DesignWidth được khai báo ở đầu như sau:

d:DesignHeight="55" d:DesignWidth="100"

Tiếp theo, chúng ta sử dụng Canvas để làm panel thay cho Grid. Lần lượt add image của bánh trước, bánh sau và thân xe vào canvas, tiến hành đặt tên và định vị lại vị trí của các images cho chính xác. Dưới đây là đoạn mã xaml:

<UserControl x:Class="CarRacer.CarRacer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="55" d:DesignWidth="100"
             >
    <Canvas>
        <Image x:Name="Than_xe" Height="54" Canvas.Left="0" Source="Than_xe.png" Canvas.Top="0" Width="100"/>
        <Image x:Name="Banh_Xe_2" Height="28" Canvas.Left="69" Source="Banh_truoc.png" Canvas.Top="27" Width="27" />
        <Image x:Name="Banh_Xe_1" Height="27" Canvas.Left="8" Source="Banh_sau.png" Canvas.Top="27" Width="28" />
    </Canvas>
</UserControl>

Define DependencyProperty và RoutedEvent

Như đã nói ở trên, chúng ta sẽ phân biệt các CarRacer khác nhau dựa vào màu sắc của thân xe, ở đây thân xe lại là một image, do đó chúng ta sẽ tạo ra một dependency property để lấy đường dẫn của image chứa phần thân xe và gán vào property Source của đối tượng có tên x:Name="Than_xe" mà chúng ta đã khai báo trong file xaml. Một dependency property nữa sẽ cho phép chúng ta khai báo khoảng cách đường đua và một để xác định vận tốc của xe. Phần khai báo các property này như sau:

public static DependencyProperty SourceProperty;

public static DependencyProperty LengthProperty;

public static DependencyProperty SpeedProperty;

Bây giờ ta tiến hành đăng kí các DependencyProperty này trong một static constructor:

static CarRacer()
{
    SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(CarRacer));
    LengthProperty = DependencyProperty.Register("Length", typeof(double), typeof(CarRacer));
    SpeedProperty = DependencyProperty.Register("Speed", typeof(double), typeof(CarRacer));            
}

Các tham số trong hàm Register bao gồm: tên của property, kiểu dữ liệu của property, và class sở hữu property đó. Ngoài ra còn có các tham số về PropertyMetadata mà bạn có thể tìm hiểu thêm.

Công việc cuối cùng là tạo cho các DependencyProperty này có dạng như một property thông thường:

public string Source
{
    get { return (string)GetValue(SourceProperty); }
    set { SetValue(SourceProperty, value); }            
}

public double Length
{
    get { return (double)GetValue(LengthProperty); }
    set { SetValue(LengthProperty, value); }
}

public double Speed
{
    get { return (double)GetValue(SpeedProperty); }
    set { SetValue(SpeedProperty, value); }
}

Bây giờ, chúng ta sẽ tạo ra một routed event có tên là Finished để xử lí sự kiện khi xe chạm đích. Các công đoạn khai báo và đưa Routed Event về dạng như các event thông thường như sau:

public static readonly RoutedEvent FinishedEvent;

public event RoutedEventHandler Finished
{
    add { AddHandler(FinishedEvent, value); }
    remove { RemoveHandler(FinishedEvent, value); }
}

Tương tự như DependencyProperty, chúng ta cũng phải đăng kí RoutedEvent trong static constructor. Các tham số truyền vào hàm RegisterRoutedEvent bao gồm: tên event, kiểu routing, class handler, class sở hữu event đó:

FinishedEvent = EventManager.RegisterRoutedEvent("Finished", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(CarRacer));

Tới đây chúng ta đã có thể sử dụng được các property cũng như event trong file xaml như các loại control khác. Bây giờ, chúng ta sẽ sử dụng DataBinding trên Image có x:Name="Than_xe" để lấy đường dẫn của phần thân xe:

<Image Height="54" Canvas.Left="0" 
      Source="{Binding Path=Source, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" 
      Canvas.Top="0" Width="100"
      />

Sử dụng UserControl

Để kiểm thử chúng ta sẽ add thêm một project WPF Application. Trong project mới tạo, click chuột phải, chọn Set as StartUp Project. Tiếp theo chúng ta add reference cho project này bằng cách bấm chuột phải vào tên project, chọn Add Reference… Trong thẻ Browse, tìm tới thư mục bin của folder chứa project CarRacer mà chúng ta tạo đầu tiên ở trên, chọn file CarRacer.dll và nhấn OK.

Trong file xaml, ta tiến hành khai báo namespace của CarRacer như sau bằng cách thêm phần khai báo của file MainWindow.xaml nội dung như sau:

xmlns:car="clr-namespace:CarRacer;assembly=CarRacer"

Add 2 image thân xe vào project mới tạo. Bây giờ chúng ta sẽ tạo ra 2 đối tượng CarRacer, một màu xanh và một màu đỏ tương ứng với 2 hình thân xe mà chúng ta đã add từ trước. Ở đây vì trong cùng 1 solution có 2 project nên chúng ta cần xác định rõ file image nằm trong assembly nào. Để hiểu rõ hơn về pack URIs trong WPF, các bạn có thể xem tại đây.

<Grid>
        <car:CarRacer Source="pack://application:,,,/CarRacerDemo;component/Than_xe.png" x:Name="xe_xanh"/>
        <car:CarRacer Source="pack://application:,,,/CarRacerDemo;component/Xe_Do.png" x:Name="xe_do" Margin="0 150 0 0"/>
    </Grid>

Kết quả tương ứng mà chúng ta thu được:

Tới đây là xong phần 1. Trong phần 2 tôi sẽ trình bày vấn đề về animation cũng như cài đặt một số chức năng khác cho control.

Source: http://www.mediafire.com/?u86gd18qia2bibc