WPF – Style và Trigger

Style là một kĩ thuật để định dạng cách hiển thị, xử lý cho một/nhiều control bằng cách gán giá trị cho các property, event. Sử dụng Style giúp cho việc thiết kế giao diện trở nên dễ dàng và linh hoạt hơn nhiều so với Windows Forms.

Setter và EventSetter

Lớp Style chứa một collection các đối tượng SetterBase và được truy xuất thông qua property Style.Setters. Có hai lớp được thừa kế từ SetterBase là Setter dùng để gán giá trị cho property và EventSetter để gán giá trị cho các event.

Một Style đơn giản viết theo kiểu in-line:

<Button Content="Hello">
    <Button.Style>
        <Style>
            <Setter Property="Button.FontSize" Value="20"/>
            <Setter Property="Control.Foreground" Value="Blue"/>
            <EventSetter Event="Button.Click" Handler="Button_Click" />
        </Style>
    </Button.Style>
</Button>

Thay vì dùng cách trên, thông thường ta sẽ tách riêng style bằng cách tạo trong phần resource,  bằng cách này ta có thể sử dụng style nhiều lần:

<Window.Resources>
    <Style x:Key="style1">
        <Setter Property="Button.FontSize" Value="20"/>
        <Setter Property="Control.Foreground" Value="Blue"/>
        <EventSetter Event="Button.Click" Handler="Button_Click" />
    </Style>
</Window.Resources>
…
<Button Content="Hello" Style="{StaticResource style1}"/>

Extending và Overriding

Các style có thể thừa kế nhau thông qua việc gán giá trị cho thuộc tính BaseOn (kiểu Style). Ví dụ style 2 sau sẽ có đầy đủ các Setter mà bạn thêm vào style1:

<Window.Resources>
    <Style x:Key="style1">
        <Setter Property="Button.FontSize" Value="20"/>
    </Style>
    <Style x:Key="style2" BasedOn="{StaticResource style1}">
        <Setter Property="Control.Foreground" Value="Blue"/>
    </Style>
</Window.Resources>
...
<Button Content="Hello" Style="{StaticResource style2}"/>

Khi muốn ghi đè giá trị của một property thuộc control đã được áp dụng style, bạn chỉ việc gán lại giá trị mới cho property đó.

Target Type

Style được tạo ra có thể được áp dụng cho một kiểu control nhất định thông qua thuộc tính TargetType (kiểu System.Type). Lưu ý là khi sử dụng cách này, bạn không cần đặt key cho style, bất kì control nào có kiểu được xác định bởi TargetType sẽ tự động được áp dụng style này.

<Style TargetType="Button" >
    <Setter Property="FontSize" Value="20"/>
</Style>

Trigger

Trigger là phương pháp giúp bạn đặt giá trị cho các property dựa trên một điều kiện cụ thể. Các Trigger được lưu trong collection Style.Triggers. Một Trigger bao gồm hai phần chính là:

–       Điều kiện kích hoạt Trigger được tạo bằng cách gán hai giá trị Trigger.Property và Trigger.Value.

–       Một collection SetterBase để thay đổi giá trị của các property khi điều kiện của Trigger được đáp ứng.

Ví dụ sau tôi sử dụng các Trigger để tự động thay đổi màu chữ của Button khi property Button.Content chỉ ra tên của màu đó:

 <Style TargetType="Button">
    <Style.Triggers>
        <Trigger Property="Content" Value="Blue" >
            <Setter Property="Foreground" Value="Blue" />
        </Trigger>
        ...
        <Trigger Property="Content" Value="Purple" >
            <Setter Property="Foreground" Value="Purple" />
        </Trigger>
    </Style.Triggers>
</Style>
…
<Button Content="Purple" />

MultiTrigger

Khi cần đặt nhiều điều kiện cho Trigger, bạn cần sử dụng lớp MultiTrigger. Lớp này chứa một collection các đối tượng Condition tương ứng với mỗi điều kiện mà bạn đặt. Với Condition, bạn có thể đặt điều kiện bằng cách xác định property hoặc sử dụng binding. Tuy nhiên bạn chỉ được phép dùng binding khi làm việc với lớp MultiDataTrigger (giới thiệu ở phần dưới).

<Style TargetType="Button">
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="Content" Value="Blue" />
                <Condition Property="FontSize" Value="12" />
            </MultiTrigger.Conditions>
            <Setter Property="Foreground" Value="Blue"/>
        </MultiTrigger>
    </Style.Triggers>
</Style>

DataTrigger và MultiDataTrigger

Đặc điểm chính của DataTrigger là không xét điều kiện dựa trên các property mà dựa trên binding. Như vậy bạn sử dụng DataTrigger tương tự như Trigger chỉ khác ở điểm bạn cần sử dụng thuộc tính Binding thay vì Property.

Ví dụ sau sử dụng binding kết hơp với style để highlight các item có giá trị đặc biệt trong một ListBox.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        listBox1.ItemsSource = new Person[]{
            new Person() { Age = 11, Name = "Alan" },
            new Person() { Age = 11, Name = "Tessa" },
            new Person() { Age = 12, Name = "Lenny" }
        };
    }
}
public class Person
{
    public int Age
    {
        get; set;
    }

    public string Name
    {
        get; set;
    }

    public override string ToString()
    {
        return Name + " - age: " + Age;
    }

}

DataTrigger:

<Window.Resources>
    <Style TargetType="ListBoxItem">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=Age}" Value="11">
                <Setter Property="Background" Value="Yellow" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid Width="300" Height="100">
    <ListBox Name="listBox1"/>
</Grid>

và MultiDataTrigger:

<Style TargetType="{x:Type ListBoxItem}">
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=Age}" Value="11" />
                <Condition Binding="{Binding Path=Name}" Value="Tessa" />
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" Value="Yellow" />
        </MultiDataTrigger>
    </Style.Triggers>
</Style>

Kết quả:

EventTrigger

Thay vì sử dụng điều kiện là property và binding như các loại Trigger trên, EventTrigger được kích hoạt khi một event xảy ra trên control. EventTrigger không cho phép bạn thay đổi các property của control mà chỉ cho phép sử dụng các animation để áp dụng cho control.

Ví dụ sau thực hiện animation đổi màu của các đối tượng Ellipse khi bạn rê chuột vào trong control (Mouse.MouseEnter).

<Style TargetType="Ellipse">
    <Style.Triggers>
        <EventTrigger RoutedEvent="Mouse.MouseEnter">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <ColorAnimation
                            Storyboard.TargetProperty="Fill.Color"
                            To="Orange" Duration="0:0:0.5" AutoReverse="True" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Style.Triggers>
</Style>
...
 <Ellipse Width="100" Height="100" Fill="LightBlue"/>

Kết quả:

https://yinyangit.wordpress.com

Đọc thêm:

35 thoughts on “WPF – Style và Trigger

  1. Mình tạo context menu cho button, và mỗi Menu Item, mình đính kèm 1 hình. Nhưng vì hình ảnh quá lớn, nên mình sẽ resize nó lại. Nhưng khi áp dụng Style để resize hình ảnh trong context menu thì không thành công. Đoạn code minh họa

    <Style x:Key="context" TargetType="Image">
                <Setter Property="Width" Value="8"></Setter>
                <Setter Property="Height" Value="8"></Setter>
            </Style>
    //....
     <Button Content="Test button" HorizontalAlignment="Left" Margin="10,25,0,0" Name="button1" VerticalAlignment="Top" Template="{StaticResource buttonStyleBlue}">
                <Button.ContextMenu>
                    <ContextMenu>                    
                        <MenuItem Command="Cut">
                            <MenuItem.Icon>
                                <Image Source="Images/cut.png" />
                            </MenuItem.Icon>
                        </MenuItem>
                        <MenuItem Command="Copy">
                            <MenuItem.Icon>
                                <Image Source="Images/copy.png" />
                            </MenuItem.Icon>
                        </MenuItem>
                        <MenuItem Command="Paste">
                            <MenuItem.Icon>
                                <Image Source="Images/paste.png" />
                            </MenuItem.Icon>
                        </MenuItem>
                    </ContextMenu>                
                </Button.ContextMenu>            
            </Button>
    

    Trường hợp mình chỉ muốn áp dụng style Context cho những hình ảnh nằm trong button thì phải làm thế nào? (Tức là những hình ảnh không nằm trong context menu button thì không bị resize ảnh)
    Mình xin cảm ơn bạn trước.

    Trả lời
  2. Mình sử dụng theme ở trang này: http://wpf.codeplex.com/wikipage?title=WPF%20Themes
    Nhưng có 1 số thứ muốn custom lại.
    Ví dụ ở theme ShinyBlue, nó hiển thị các phần tử trong Listbox dạng stack.
    Mình muốn giữ nguyên các định dạng trong Listbox như border, color, background, và chỉ muốn thay đổi cách hiển thị, từ dạng stack sang wrapPannel.
    Mình có thể tạo Style riêng và dùng viết đè lại những property có sẵn trong file theme đó?
    Mình xin cám ơn bạn trước.

    Trả lời
  3. Mình thấy bạn có vẻ đang làm một dự án về WPF thì phải. Thú thật là mình chưa thấy ai ứng dụng WPF ở VN và không biết công nghệ này sẽ phát triển thế nào trong tương lại.
    Về vấn đề của bạn thì bạn có thể xem mục “Extending và Overriding” trong bài viết trên.

    Trả lời
  4. Hiii, mình đang làm 1 cái giả windows explorer. Nếu đi theo tutorial của YinYang thì mình học, nhưng không biết ứng dụng kiến thức đã học thế nào. (làm tới đâu học tới đó).
    Trong bài Data Binding, có thể YinYang chưa nhấn mạnh namespace nên mình mất tới hơn 1 tháng mới hiểu cách dùng.
    Gần đây khi binding dữ liệu lên, đụng tới style, mình xem lại lần nữa mới hiểu IValueConverter.
    Rồi cũng thật thú vị, khi áp dụng Theme có sẵn trên codeplex, mới hiểu được nhiều thứ như control template, style và dataTemplate, và bị override các style khi mình áp dụng ExpressDark trong file App.xaml.

    Nói chung blog của bạn thú vị lắm. Để mình tự tìm hiểu tiếp.
    Sắp tới mình cũng định nghiên cứu về BackgroundWorker trên web và thử ứng dụng nó kiểm tra dữ liệu ở client và server khi submit form ($(#targetname).submit() bị điểm dở là khi dùng ajax kiểm tra, nó trả dữ liệu bị sai).
    Mình cũng nghe nói ExpressionTree dùng để biên dịch code khi chạy, mình cũng định nghiên cứu vấn đề này…
    Nên dùng static class hay instance class để viết các hàm thư viện?
    Sử dụng Entity Framework, nên dùng CodeFirst hay Model First? (vẫn chưa có câu trả lời hoàn chỉnh)
    —-
    Blog bạn viết hay lắm, mình muốn thực hành nhiều thứ mà vẫn chưa có thời gian. Mình cũng định nhiều bài để chia sẽ lập trình nhưng vì viết 1 thứ gì đó, tự viết rất khó nên vẫn còn bỏ ngõ rất nhiều…
    Cảm ơn YinYang rất nhiều, vì trong thời gian qua, mình hay hỏi và làm bạn tốn nhiều thời gian.
    Thân.

    Trả lời
  5. Cảm ơn bạn đã chia sẻ, thực ra thì các kiến thức mình viết vừa để học, chia sẻ, lưu trữ nên khó mà bao quát hết. Hơn nữa kinh nghiệm thực tế hầu như không có, chỉ biết lý thuyết nên nhiều vấn đề khó mà giải quyết được.

    Trả lời
  6. Tiện ghé qua blog Yin Yang, mình sẽ trả lời giúp cho Án Bình Trọng:
    – Static class / non – static class thì nó phụ thuộc vào việc thư viện của bạn có cần tạo ra instance không, nếu không cần tạo ra instance mỗi lần sử dụng thì xài static, còn nếu cần thì khác. Vậy thì bạn cần tạo ra instance cho một lớp lúc nào? Ví dụ bạn có lớp Canvas, lớp này chuyên dùng để vẽ các hình 2D, bạn cần phải lưu lại thông tin về Brush sau mỗi lượt vẽ, như vậy bạn cần tạo ra các instance tại mỗi nơi bạn vẽ thì thông tin về Brush nó khác nhau. Nói chung thì mình thiên về tạo non-static class hơn.

    – Code First / Model First? Mình luôn chọn Code First cho dù mình có DB sẵn, tại vì sao? Tại vì Code First hỗ trợ các tập tin POCO, nó không sử dụng VIsual Designer, nó không sinh ra các thứ code lằng nhằng mà dù mình là chủ nhân, mình lại không thể làm chủ cách code mình được viết. Nếu bạn viết một entity class, bạn muốn bỏ vào đó một số hàm để validate, hoặc các attribute mới, bạn dùng Model First thì sẽ khá là pain full, dù rằng C# hỗ trợ viết các partial class. Một thông tin mới hơn đó là Entity Framework 4.3 hỗ trợ Code First Migration, nghĩa là, bạn dùng Model First, bạn có thể migrate qua Code First. Điều đó cho thấy Microsoft hướng developer sang dùng Code First nhiều hơn, hay nói một cách khác, cộng đồng devs đã khiến Microsoft phải tập trung nhiều hơn cho Code First. Tham khảo http://d.jou.vn/Article/Entity-Framework-43-da-duoc-hoan-thien–phien-ban-50-hua-hen-se-ho-tro-kieu-enum/27

    Riêng đối với Yin Yang, mình không biết bạn đã đi làm chưa, nhưng nội lực của bạn thật là đáng khen ngợi, việc viết bài không bao giờ là dễ dàng cho dù chỉ dịch bài viết nước ngoài sang tiếng Việt.

    Trả lời
    • Trước hết xin cám ơn bạn rất nhiều.
      Ý mình hỏi có lẽ chưa rõ. Ví dụ bạn viết hàm A (giả sử hàm trung bình cộng), bạn viết dạng hàm thư viện (sử dụng static class, static function). Trong 1 thời điểm, giả sử có 30 luồng yêu cầu tính trung bình cộng, thì việc sử dụng static function có đảm bảo quá trình tính toán có đúng không?

      Trả lời
      • Mình nghĩ việc phương thức static của bạn có tính đúng hay không phụ thuộc vào cách bạn sử dụng dữ liệu, tham số sao cho đảm bảo tính thread-safe. Chúng được lưu cùng trong một bảng gọi là MethodTable nên cách mà .Net đối xử với static và instance là giống nhau. Quan trọng là các dữ liệu mà chúng có liên hệ tới.

        Ngoài ra, để đảm bảo rằng các phương thức static của bạn được thực hiện một cách đồng bộ (chỉ có duy nhất một thread được thực thi phương thức static của bạn tại một thời điểm) là dùng attribute MethodImpl với tham số MethodImplOptions.Synchronized:

        [MethodImpl(MethodImplOptions.Synchronized)]

        Tham khảo: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimplattribute.aspx

      • Chào các anh. Trước hết em cảm ơn anh Yin Yang vì blog của anh quả thật rất hay (không biết có lớn tuổi hơn hay không nhưng thật sự nể phục kiến thức của anh). Em cũng mới chỉ học 1 năm CNTT, chưa dám múa rìu qua mắt thợ. Em đang nghiên cứu về WPF để viết 1 bài toán quản lý đơn giản thôi, cho bài tập lớn ở trường. Tình cờ thấy câu hỏi của anh Án Trọng Bình về vấn đề static/non-static. Em nghĩ vấn đề anh muốn đề cập ở đây nó được gọi là hiện tượng thắt cổ chai, tức là hiện tượng bị quá tải khi dùng static. Để giải quyết vấn đề này thì có 1 cách mà em tìm được và em nghĩ là nó có hiệu quả. Đó là chúng ta không viết static cho class hay phương thức tính toán, mà trong class, chúng ta chỉ tạo ra 1 method static duy nhất. Phương thức này có dạng :

        public class MyConnection
            {
                private static SqlConnection _conn = null;
        
                public static SqlConnection GetInstance()
                {
                    return _conn ?? (_conn = new SqlConnection("server=localhost;database=DemoWFC;uid=sa;pwd=abc"));
                }
            }
        

        Giả sử với hàm để trả về với kết nối CSDL. Như vậy em nghĩ là nó vẫn sẽ đảm bảo được tính tối ưu khi chỉ tạo ra 1 thể hiện duy nhất khi ứng dụng chạy, đồng thời có thể tạo ra instance mới nếu có bị quá tải hay hủy vì nguyên nhân nào đó. Mong được các anh cho ý kiến.

      • Cảm ơn bạn, vấn đề mà bạn nêu ra cũng rất hay gặp phải trong lập trình. Vì thế cách giải quyết cho nó là sử dụng singleon pattern (là cách bạn làm bên trên). Thực tế thì không cần biết gì về pattern này thì các lập trình viên cũng vẫn sử dụng thường xuyên.

      • Mình dùng static class, static function nhưng không dùng biến cục bộ (trong class) để hạn chế sự dùng chung.
        Ví dụ:

        public static class StringEnumConversion
        {
            public static T Convert<T>(string str)
            {
                return (T)Enum.Parse(typeof(T), str);
            }
        }
        

        Mình hạn chế dùng static variable để hạn chế khả năng xảy ra lỗi khi viết chương trình đa luồng. Các biến được truyền vào trong hàm con và các biến được khai báo cục bộ bên trong hàm.

      • Khi kết nối xuống CSDL, bạn không nên dùng biến static.
        Tùy theo cách viết của bạn, mình không rõ. Nhưng mình đoán nếu bạn chỉ tạo 1 kết nối, trong trường hợp viết web, csdl chỉ thực hiện 1 yêu cầu trong 1 thời điểm (thay vì nhiều yêu cầu). Nó sẽ làm lãng phí tài nguyên máy server.
        1 kết nối được duy trì trong khi bật web-server, có thể bị các vấn đề về bảo mật. Bạn nên tạo instance mỗi khi có yêu cầu về dữ liệu.

      • Về bản chất em nghĩ là em chưa hề khởi tạo nó. Kết nối chỉ thật sự được khởi tạo khi có yêu cầu (khi đó em mới gọi GetInstance trong các thao tác như hiển thị, update, xóa), ban đầu kết nối chỉ có giá trị là null, em chưa hề đụng chạm tới, nên chuyện tốn tài nguyên em nghĩ là không có. Thứ 2 là dù là web hay phần mềm thì đều có các thao tác chính như hiển thị, add, update, delete. Nếu không dùng static thì sau mỗi thao tác này, mình lại phải khởi tạo lại kết nối, em nghĩ vậy tốn tài nguyên hơn.

      • Mình nghĩ lãng phí là thế này: Ví dụ: Bạn tạo 1 website, vào 1 thời điểm, có tới 20 yêu cầu cần truy xuất csdl. Nếu bạn tạo 1 kết nối thì mỗi lần truy xuất chỉ thực hiện được 1 yêu cầu, trong khi máy chủ sql server có thể đáp ứng tối đa 50 kết nối cùng 1 lúc. Người dùng click chuột và ngồi đợi 🙂
        Thời gian sống của 1 biến cũng rất quan trọng. Giả sử có những lúc không có ai truy cập, nó vẫn chiếm 1 vùng nhớ nhất định.
        Bạn có tìm hiểu LINQ to SQL chưa?

      • Em không nghĩ như thế. Việc sử dụng biến static không có nghĩa là có 10 người cùng truy cập đến server thì cả 10 người này chia sẻ chung 1 kết nối static đó. Static ở đây là phạm vi global, nhưng tầm nhìn thấy của nó vẫn chỉ giới hạn trong phiên làm việc của 1 client thôi. Có 10 client kết nối đến thì nó vẫn sẽ tạo ra 10 kết nối. Vì thế không có chuyện client này phải chờ cho kết nối làm việc xong với client kia mới đến lượt.

      • Ví dụ bạn có 2 server dùng làm host
        Server A: SQL Server
        Server B: ASP.NET MVC
        Trong bài ví dụ của bạn, hình như class của bạn dựa sử dụng Singleton pattern.
        Thực ra có những điểm gần giống nhau giữa Static method và Method của bạn viết (mình đặt là static A)
        Thứ 1: Static method và static A được cấp phát 1 vùng nhớ cụ thể, khi gọi hàm, con trỏ trỏ đến địa chỉ hàm và gọi lời thực thi hàm.
        Thứ 2: Hàm sẽ được gọi tuần tự chứ không thể gọi song song.
        Còn đối với instance class, tuy tên hàm giống nhau nhưng nó chiếm nhiều vùng nhớ trên máy tính, và khi gọi song song, nó thực thi song song.

        Ý mình nói trường hợp bạn viết web, chuỗi kết nối của bạn đại diện cho cầu nối giữa Server A và B. Nên nếu có 20 lời gọi từ các client khác nhau, server B chỉ có thể trả lời từng yêu cầu 1 chứ không thể tạo ra 20 instance object kết nối xuống CSDL và trả lời cùng 1 lúc (dù đường truyền khá mạnh và server A cho phép thực hiện nhiều kết nối cùng 1 lúc). Như vậy làm server B bị thắc cổ chai.

        Ở đây mình suy luận dựa trên kiến thức đã học (có thể còn lầm lẫn), còn thực tế thì mình chưa test được.
        Bạn thử search trên mạng, các bài hướng dẫn của ScottGu về Linq to sql, ngta thường dùng tạo biến trong hàm và không bao giờ sử dụng static variable cho việc kết nối xuống CSDL.

      • Điểm hại của biến static là nó tồn tại cho đến khi chương trình kết thúc. Bạn có thể tiết kiệm được được, ví dụ 1kb vùng nhớ, như vậy là quá nhỏ so với dung lượng RAM hiện nay. Nhưng chương trình của bạn thường xuyên chiếm +1kb, mình nghĩ nó lại nặng.
        Ngoài ra, việc dùng static cũng khá nguy hiểm. Ví dụ bạn cho class A với các giá trị x, y, z. Thời điểm hiện tại, bạn có thể kiểm soát nó. Nhưng giả sử chương trình của bạn lên tới hơn 50 class, tới lúc đó, ngẫu nhiên giá trị bị sai, bạn khó lần theo logic để xem trong đoạn code logic nào làm biến static bị sai giá trị.
        (Ở đây mình nói vụ static class- vì class của bạn tạo cũng chỉ tạo 1 lần và sống suốt thời gian chạy chương trình, không dùng instance class vì sợ hiểu nhầm sang ý khác)

      • ồ cảm ơn anh đã có lời khuyên. Nói chung em cũng chưa tìm hiểu được nhiều lắm. Hiện tại em vẫn đang tập trung vào winform thôi. Hi vọng sau này sẽ có dịp trao đổi với anh nhiều hơn.

      • OK cảm ơn các bạn đã chia sẻ. Về mặt này có lẽ bạn Án Bình Trọng rất có kinh nghiệm. À mình nghĩ CodeBlue cũng nên tìm hiểu về web vì winform hiện tại và sau này sẽ không phổ biến bằng.

    • Lúc đọc comment của bạn Án Bình Trọng mình lại ko nghĩ đó là các câu hỏi mà chỉ coi đó là các vấn đề bạn đang nghiên cứu nên không trả lời. Mình cũng xin phép trả lời một vài ý sau:

      – Mình ko nghĩ là phương thức submit() của jQuery có thể bị sai ở điểm nào đó. Công dụng của nó chỉ đơn giản là gửi dữ liệu của form về server và cũng ko cung cấp một callback để thực thi khi server nhận được dữ liệu. Có thể phương pháp của bạn áp dụng thiếu một điểm nào đó.

      – Về expression tree bạn có thể tham khảo một số bài viết trong blog của mình: Expression Tree. Có hai loại bài viết về chủ đề này: một các thuật toán xây dựng expression tree và hai là khái niệm expression tree trong .Net.

      – Tất nhiên là bạn nên dùng các instance vì đây là bản chất của ngôn ngữ OOP (trừ phi bạn ko dùng ngôn ngữ hỗ trợ OOP). Việc xây dựng thư viện hay ứng dụng cũng ko khác biệt nhiều khi lựa chọn sử dụng instance hay static. Thường thì các thư viện kết hợp cả hai kĩ thuật này. Và cuối cùng là chúng phụ thuộc nhiều vào design pattern. Không phải cái nào tốt hơn mà là dùng trong trường hợp nào sẽ tốt hơn.

      – Mình cũng đồng ý với anh Lê Hoàng Dũng, tuy nhiên theo mình nghĩ thì còn tuỳ trường hợp nào bạn nên sử dụng cách này hay cách kia. Sử dụng một công cụ designer tốt sẽ là lựa chọn hợp lý hơn cho những database phức tạp và giúp giảm được nhiều thời gian. Tương tự như việc bạn chọn lựa giữa code một ứng dụng bằng notepad hay một IDE như NetBean. Mặc dù dùng NetBean có thể ko được tối ưu mã và rõ ràng như cách viết = notepad nhưng nó vẫn là lựa chọn tốt để phát triển những ứng dụng phức tạp.

      @Lê Hoàng Dũng: Thật vinh hạnh được một MVP như anh góp ý. Hiện tại e mới đi làm được khoảng 2 tháng và công việc cũng khá thuận lợi. Hy vọng học được nhiều điều từ blog của anh.

      Trả lời
      • $().submit() là câu hỏi của bạn mình.
        Thật ra hàm submit() của jquery chỉ dùng để kiểm tra điều kiện bên client, nếu áp dụng kiểm tra điều kiện trên server trước khi trả về true để dữ liệu được submit đi thì không đúng logic. (mình dùng $.ajax(..) để kiểm tra dữ liệu trên server)
        Mình giải quyết bằng cách dùng 1 hàm javascript khác, kiểm tra điều kiện rồi mới gọi submit (Nút Gởi dữ liệu làm bằng thẻ div). Cách đó tuy giải quyết vấn đề nhưng có vẻ không được hay.

        Mình hỏi về instance class hay static class là vì mình muốn bắt chước Microsoft, viết hàm cho người lập trình xài, làm cho họ có cảm giác như xài hàm của Microsoft (vd 1 số hàm của Microsoft như return Json(listSinhvien)-trong MVC). Nhưng cái mình sợ nhất là dính tới đa luồng, nó có thể sai.

        @YinYang: bạn rất pro đấy. Từ lúc thực sự bắt đầu học .NET, mình bị rối lúc tạo captcha để ngăn spam, mình đã xem blog của bạn (bài đầu tiên là tạo captcha trong asp.net). Nhiều lúc rối rắm, mình đều luôn tìm đến blog của bạn để tìm 1 ý tưởng nào đó. Chúc bạn luôn thành công trong công việc

        @Lê Hoàng Dũng: blog anh có nhiều thông tin thú vị thật, và nhất là vụ hỗ trợ enum trong Entity Framework. Tuy chưa biết nó là gì nhưng để em ngồi ngâm cứu thử xem.

      • Cảm ơn bạn! Đối với vấn đề submit() mình có thể hình dung được rắc rối của bạn có lẽ là do bạn chưa đặt tham số async = false của $.ajax(). Nếu ko có tham số này thì các đoạn mã phía sau $.ajax() sẽ được thực thi trước khi kết quả trả về.

      • enum thực chất là kiểu liệt kê. EF hỗ trợ enum, nghĩa là khi em có một entity class, nó có 1 field có kiểu enum, và nó được ánh xạ dễ dàng vào table trong SQL Database. Như vậy thì EF sẽ là một ORM (Object Relational Mapping) tiện dụng hơn. Ánh xạ được kiểu enum thì NHibernate đã làm được rồi.

        Cheers!

      • Hi Yin Yang! Nói thực là anh ngưỡng mộ em đấy chứ. Nội lực rất đáng nể, dù hiện tại anh là một professional trainer, nhưng anh cũng chưa thể viết và chia sẻ được tất cả mọi thứ bằng các bài viết một cách nhuần nhuyễn như em. Anh thấy Yin Yang tập trung vào HTML5, CSS3 & JavaScript là hoàn toàn hợp lý vì nó là cánh cửa cho mọi platform trong tương lai. Ví dụ như phát triển app cho Windows 8 (WinJS), hoặc cho các smartphone OS (sử dụng PhoneGap) chẳng hạn. Tuy nhiên, với một người có kỹ năng về thuật toán & tư duy tốt như em thì đừng nên bỏ lỡ cơ hội được viết ứng dụng cho enterprise. Còn như thế nào là app cho enterprise thì em cứ tìm hiểu sẽ ngộ ra ^^. Rất vui được làm quen với Yin Yang!

      • Cám ơn những lời khuyên của anh. Em sẽ nghiên cứu thử nhưng có lẽ công việc sẽ hạn chế nhiều. Rất vui được làm quen và hy vọng được trao đổi nhiều vấn đề với anh hơn.

  7. Khi mình sử dụng Toolbar(width = 400) để chứa 3 control: nút Back(width = 36), Forward(width = 36), textbox.
    Nút textbox với thuộc tính Width=”Auto” hay HorizontalAlignment=”Stretch” đều không chiếm hết không gian còn lại trong Toolbar.
    Vậy trong trường hợp đó, mình phải giải quyết thế nào?

    Trả lời
  8. Mình đang làm dự án cho sở giao dịch CK HN và sử dụng WPF rất nhiều. Về mặt giao diện thì cơ bản là khá ok rồi, mình có thể viết style, temp cho tất cả các control. Tuy nhiên mình muốn tìm hiểu thêm về kỹ thuật binding khi viết temp cho các control để cho việc xây dựng các control trở nên linh hoạt hơn. Mong Yin Yang nói chi tiết hơn về kỹ thuật binding khi xây dựng temp. Thanks.

    Trả lời
  9. Yin Yang có thể giúp mình cách tạo hiệu ứng nhấp nháy cho cửa sổ ứng dụng ( khi nó không được active – nằm dưới thanh start ). trong wpf, sử dụng Binding ( DataBinding, DataTrigger v.v… ) ?

    Trả lời

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s