Blog chia sẽ kinh nghiệm lập trình và Technical VN. Nơi mà các bạn có thể học tập và tham khảo về những công nghệ mới nhất hiện nay về .NET

Hiển thị các bài đăng có nhãn WPF. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn WPF. Hiển thị tất cả bài đăng

Chủ Nhật, 26 tháng 4, 2015

On 00:58 by Tuan Pham in ,    No comments

Hôm nay tôi sẽ giới thiệu đến các bạn một thư viện mở(GalaSoft.Utilities) giúp chúng ta có thể tạo một ứng dụng có thể zoom.


Tôi có thiết kế giao diện như sau:


  1. CheckBox để ẩn/hiện kính Lúp
  2. Slider thay đổi chiều rộng kính
  3. Slider thay đổi chiều cao kính
  4. Slider thay đổi độ thu/phóng của kính

Chú ý: Kính lúp được mặc định là hình TRÒN, điều này có thể được thay đổi.
Zoom behavior
Zoom behavior design

Chúng ta sẽ Attach Zoom Behavior trong thư viện GalaSoft.Utilities  vào Root Grid Control(là vùng chúng ta muốn kính hoạt động)và binding đến các control đã thiết kế ở trên:

















Kết quả
Zoom behavior
Chúng ta có thể thay đổi hình chiếc kính lúp bằng cách thay đổi Control Template của nó:


1-> Đầu tiên tôi định nghĩa 2 Template


















2 -> Sau đó tôi tạo một group RadioButton (Rb1 và Rb2):

Thể hiện lại 2 Template trên, khi người dùng chọn một trong hai RadioButton này thì tôi sẽ gán Template mà nó đang giữ cho ZoomControl(tức x:Name="MyZoom")











Kết quả ta có 2 button để select kiểu kính lúp(góc trên trái 10h)

Zoom behavior design wpf acttach behavior

Code.CS



#region
using System.Windows;
using System.Windows.Controls;
#endregion

namespace ZoomBehavior
{
///
/// Interaction logic for MainWindow.xaml
///

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

///
/// Change the magnifier's template
///

private void TemplateRadioButton_Click(object s, RoutedEventArgs e)
{
var sender = s as RadioButton;

// Thay đổi hình dạng kính lúp
MyZoom.Template = sender.Template;
}
}
}


Đến đây bạn có thể làm Thánh Soi được rồi, có thể thêm tính năng mở file ảnh để show cho tiện soi
tuanpham wpf acttach behavior

Tải về source tại đây
Vậy là chúng ta đã hoàn thiện một chiếc kính lúp cho ứng dụng của mình, chúc các bạn thành công!
Phạm Tuân.
On 00:57 by Tuan Pham in ,    No comments
Về khía cạnh náo đó chúng ta sẽ thấy giữa StaticResource và DynamicResource không có nhiều sự khác biệt và chúng có thể thay thế lẫn nhau mà không có vấn đề gì. Tuy vậy, ở một khía cạnh khác việc dùng chúng không đúng mục đích là hết sức nguy hiểm.
StaticResource và DynamicResource


Giống nhau:

WPF giới thiệu một khái niệm rất tiện dụng: Khả năng lưu trữ dữ liệu/thông tin như một nguồn tài nguyên(Resource) cho một/một số Control hay cho Windows/Page hoặc cho toàn bộ ứng dụng. Các dữ liệu có thể được lưu trữ dưới nhiều dạng(Type) khác nhau và trong WPF chúng ta thường thấy nó khi các Style/Template được định nghĩa. Điều này cho phép bạn đặt dữ liệu ở một nơi và sau đó sử dụng nó từ hoặc một nơi khác một cách đơn giản.
Tôi xét đến một ví dụ như sau:





Hello, world!









Và chúng ta có cùng một kết quả:
StaticResource và DynamicResource tuanphamdg

Khác nhau:

  1. Nếu dùng StaticResource bạn sẽ thấy nó giống với cách dùng Constant trong các ngôn ngữ lập trình cấp cao - có nghĩa là nó sẽ được loading một lần duy nhất để xử lý, việc bạn thay đồi giá trị của "Resource" sau đó đều không có ý nghĩa. Nếu dùng DynamicResource thì ngược lại, bạn có thể thay đổi giá trị của "Resource" để thay đổi thông tin nơi reference nó.
  2. Nếu dùng StaticResource chúng ta sẽ phải gặp rắc rối khi "Resource" đang được reference lại không tồn tại. Với DynamicResource thì nếu đối tượng được reference không tồn tại nó sẽ có giá trị default(Việc này có thể sẩy ra vì lúc bạn Building Project sẽ không hề nhận được thông báo lỗi 'Resource not found')
  3. Khi bạn dùng DynamicResource bạn sẽ phải tốn nhiều chi phí hơn, giảm hiệu xuất ứng dụng, ý (1) cũng cho thấy điều đó.

Minh chứng:

Sample.XAML

















User Name:



Password:










Sample.CS


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

private void btnBlack_Click(object sender, RoutedEventArgs e)
{
Brush blackBrush = new SolidColorBrush { Color = Colors.Black };
this.Resources["WindowBrush"] = blackBrush;
}

private void btnRed_Click(object sender, RoutedEventArgs e)
{
Brush blackBrush = new SolidColorBrush { Color = Colors.Red };
this.Resources["WindowBrush"] = blackBrush;
}
}


Lời khuyên:

Nên cân nhắc việc dùng  StaticResource và DynamicResource

Các StaticResource luôn được load lên trước và chúng sẽ ready khi ứng dụng đã Start vì thế ứng dụng của bạn sẽ khởi động rất lâu nếu dữ liệu Static là phức tạp và quá lớn.
Nếu bạn dùng DynamicResource thì hiệu xuất ứng dụng sẽ giảm đi đáng kể nhưng bạn lại có thể thay đổi giá trị của Resource lúc Runtime - cái mà StaticResource không làm được.
Hi vọng các bạn sẽ có cái nhìn rõ ràng hơn về StaticResource và DynamicResource, chúc các bạn thành công!
Phạm Tuân

Chủ Nhật, 5 tháng 4, 2015

On 06:24 by Tuan Pham in ,    No comments
Data Binding là kĩ thuật dùng để tạo gắn kết giữa phần giao diện (UI) và dữ liệu thông qua phần business logic hoặc giữa các đối tượng UI với nhau. Nhờ Data Binding, UI có thể tự động cập nhật lại để hiển thị các thay đổi trong dữ liệu mà không cần sự can thiệp từ code behind.Ngoài ra, Data Binding trong WPF còn hỗ trợ các chiều khác nhau, nghĩa là các thay đổi có thể cập nhật từ UI vào dữ liệu hay ngược lại.



I> Giới thiệu

Một Binding bao gồm 4 thành phần chính là: binding target, target property, binding source và path  (đường dẫn) đến giá trị cần thiết trong binding source, thông thường path này là một source property.
Ví dụ: bạn muốn gắn property Name của một đối tượng Person cho property Text của một TextBox. Khi đó:
- Binding target: TextBox
- Target property: property Text của TextBox
- Binding source: đối tượng Person
- Path: đường dẫn đếnproperty Name của đối tượng Person.
Mô hình Data Binding của WPF theo hình minh họa như sau
Cần lưu ý là target property phải là một dependency property. Đa số các property của lớp UIElement  đều là các dependency property. Đối với binding source, bạn có thể sử dụng bất kì đối tượng .NET nào, chẳng hạn như các đối tượng trong ADO.NET, XML hay các control trong WPF.

II> Binding Mode


Binding mode sẽ chỉ ra hướng mà dữ liệu sẽ được cập nhật. Bao gồm 5 giá trị từ enum BindingMode là:


Name Description
OneWay Cập nhật target property theo source property
TwoWay Cập nhật hai chiều giữa target property và source property.
OneTime Khởi tạo target property từ source property. Sau đó việc cập nhật dữ liệu sẽ không được thực hiện.
OneWayToSource Giống OneWay nhưng theo hướng ngược lại: cập nhật từ target property sang source property.
Default Hướng binding dựa trên target property. Với target property mà người dùng có thể thay đổi giá trị (như TextBox.Text) thì nó là TwoWay, còn lại là OneWay

* Nhưng khi nào thì dữ liệu sẽ bắt đầu được cập nhật, chúng ta sẽ nói sau về phần UpdateSourceTrigger

Ví dụ: Giả sử tôi muốn cập nhật nội dung của một TextBox theo giá trị được nhập vào TextBox khác. Cửa sổ minh họa cho ví dụ này cần hai control chính là txtHvà txtS:







Bản chất của việc tạo binding bao gồm 2 bước:
-Tạo một đối tượng System.Windows.Data.Binding và thiết lập các giá trị cần thiết.
-Gọi phương thức instance FrameworkElement.SetBinding() của target binding. FrameworkElement được thừa kế từ UIElement và là lớp cha của các control trong WPF. Phương thức này có tham số đầu tiên là một dependency property.

Bạn có thể tạo trong XAML theo cách thông thường sau:


Red









Tuy nhiên, XAML còn hỗ trợ một dạng cú pháp gọn hơn với cùng chức năng như đoạn mã trên:











Kết quả:







Ví dụ về binding màu:

Thứ Bảy, 4 tháng 4, 2015

On 16:11 by Tuan Pham in ,    No comments
1. WPF là gì?WPF, viết tắt của Windows Presentation Foundation, là hệ thống API mới hỗ trợ việc xây dựng giao diện đồ hoạ trên nền Windows. Được xem như thế hệ kế tiếp của WinForms, WPF tăng cường khả năng lập trình giao diện của lập trình viên bằng cách cung cấp các API cho phép tận dụng những lợi thế về đa phương tiện hiện đại. Là một bộ phận của .NET Framework 3.0, WPF sẵn có trong Windows Vista và Windows Server 2008. Đồng thời, WPF cũng có thể hoạt động trên nền Windows XP Service Pack 2 hoặc mới hơn, và cả Windows Server 2003.
WPF được xây dựng nhằm vào ba mục tiêu cơ bản:

  1. Cung cấp một nền tảng thống nhất để xây dựng giao diện người dùng; 
  2. Cho phép người lập trình và người thiết kế giao diện làm việc cùng nhau một cách dễ dàng;
  3. Cung cấp một công nghệ chung để xây dựng giao diện người dùng trên cả Windows và trình duyệt Web
2. Nền tảng thống nhất để xây dựng giao diện người dùng
Trước khi WPF ra đời, việc tạo giao diện người dùng theo những yêu cầu mô tả ở ví dụ trên đòi hỏi sử dụng rất nhiều công nghệ khác nhau (xem Bảng 2.1). Để tạo form, các control và các tính năng kinh điển khác của một giao diện đồ họa Windows, thông thường lập trình viên sẽ chọn Windows Forms, một phần của .NET Framework. Nếu cần hiển thị văn bản, Windows Forms có một số tính năng hỗ trợ văn bản trực tiếp hoặc có thể sử dụng Adobe’s PDF để hiển thị văn bản có khuôn dạng cố định.

Đối với hình ảnh và đồ họa 2 chiều, lập trình viên sẽ dùng GDI+, một mô hình lập trình riêng biệt có thể truy nhập qua Windows Forms. Để hiển thị video hay phát âm thanh, lập trình viên lại phải sử dụng Windows Media Player, và với đồ họa 3 chiều, anh ta lại phải dùng Direct3D, một thành phần chuẩn khác của Windows. Tóm lại, quá trình phát triển giao diện người dùng theo yêu cầu trở nên phức tạp, đòi hỏi lập trình viên quá nhiều kỹ năng công nghệ.
Bảng 0.1 – Thành phần giao diện theo yêu cầu và những công nghệ chuyên biệt  ần thiết để tạo chúng. (nguồn msdn.vietnam)

WPF là giải pháp hợp nhất nhằm giải quyết tất cả những vấn đề công nghệ nêu trên, hay nói cách khác, WPF cung cấp nhiều tính năng lập trình giao diện trong cùng một công nghệ đơn nhất. Điều này giúp cho quá trình tạo giao diện người dùng trở nên dễ dàng hơn đáng kể.
Hình 0.1  cho thấy một giao diện quản lý và theo dõi bệnh nhân có sự kết hợp của hình ảnh, text, đồ họa 2 chiều/3 chiều và nhiều thông tin trực quan khác. Tất cả đều được tạo ra bằng WPF – lập trình viên không cần viết code để sử dụng các công nghệ  chuyên biệt như GDI+ hay Direct3D.
Hình 0.1 – Một giao diễn người dùng quản lý và theo dõi bệnh nhân sử dụng WPF có thể kết hợp hình ảnh, text, đồ họa 2 chiều/3chiều và nhiều tính năng trực quan khác. (nguồn http://msdn.microsoft.com)
Tuy nhiên, WPF ra đời không có nghĩa là tất cả những công nghệ nêu trên bị thay thế. Windows Forms vẫn có giá trị, thậm trí trong WPF, một số ứng dụng mới vẫn sẽ sử dụng Windows Forms. Windows Media Player vẫn đóng một vai trò công cụ độc lập để chơi nhạc và trình chiếu video. PDF cho văn bản vẫn tiếp tục được sử dụng. Direct3D vẫn là công nghệ quan trọng trong games và các dạng ứng dụng khác (Trong thực tế, bản thân WPF dựa trên Direct3D để thực hiện mọi biểu diễn đồ họa).

Việc tạo ra một giao diện người dùng hiện đại không chỉ là việc hợp nhất các công nghệ sẵn có khác nhau. Nó còn thể hiện ở việc tận dụng lợi điểm của card đồ họa hiện đại. Để giải phóng những hạn chế của đồ họa bitmap, WPF dựa hoàn toàn trên đồ họa vector, cho phép hình ảnh tự động thay đổi kích thước để phù hợp với kích thước và độ phân giải của màn hình mà nó được hiển thị.
Bằng việc hợp nhất tất cả các công nghệ cần thiết để tạo ra một giao diện người dùng vào một nền tảng đơn nhất, WPF đơn giản hóa đáng kể công việc của lập trình viên giao diện. Với việc yêu cầu lập trình viên học một môi trường phát triển duy nhất, WPF góp phần làm giảm chi phí cho việc xây dựng và bảo trì ứng dụng. Và bằng việc cho phép tích hợp đa dạng nhiều cách biểu diễn thông tin trên giao diện người dùng, WPF góp phần nâng cao chất lượng, và theo đó là giá trị công việc, của cách thức người dùng tương tác với ứng dụng trên Windows.

3. Khả năng làm việc chung giữa người thiết kế giao diện và lập trình viên
Trong thực tế, việc xây dựng một giao diện người dùng phức hợp như trong ví dụ về ứng dụng quản lý bệnh nhân trên đòi hỏi những kỹ năng ít thấy ở những lập trình viên đơn thuần, mà chỉ có thể tìm thấy ở những người thiết kế giao diện chuyên nghiệp. Nhưng câu hỏi đặt ra là làm sao để người thiết kế và lập trình viên có thể làm việc cùng nhau?

Thông thường, người thiết kế giao diện sử dụng một công cụ đồ họa để tạo ra những ảnh tĩnh về cách bố trí giao diện trên màn hình. Những hình ảnh này sau đó được chuyển tới lập trình viên với nhiệm vụ tạo ra mã trình để hiện thực hóa giao diện đã thiết kế. Đôi lúc vẽ ra một giao diện thì đơn giản với người thiết kế, nhưng để biến nó thành hiện thực có thể là khó khăn hoặc bất khả thi với lập trình viên.

Hạn chế về công nghệ, sức ép tiến độ, thiếu kỹ năng, hiểu nhầm hoặc đơn giản là bất đồng quan điểm có thể khiến lập trình viên không đáp ứng được đầy đủ yêu cầu từ người thiết kế. Do vậy, điều cần thiết ở đây là một cách thức để hai nhóm công tác độc lập này có thể làm việc với nhau mà không làm thay đổi chất lượng của giao diện đã thiết kế.

Để thực hiện được điều này, WPF đưa ra ngôn ngữ đặc tả eXtensible Application Markup Language (XAML). XAML định ra một tập các phần tử XML như Button, TextBox, Label…, nhằm định nghĩa các đối tượng đồ họa tương ứng như nút bấm, hộp thoại, nhãn…, và nhờ đó cho phép mô tả chính xác diện mạo của giao diện người dùng. Các phần tử XAML cũng chứa các thuộc tính, cho phép thiết lập nhiều tính chất khác nhau của đối tượng đồ họa tương ứng.
Ví dụ, đoạn mã sau sẽ tạo ra một nút bấm màu đỏ có nhan đề “No”.
<Button x:Name="btn" Content="Click me"/> 
Mỗi phần tử XAML lại tương ứng với một lớp WPF, và mỗi thuộc tính của phần tử đó lại tương ứng với thuộc tính hay sự kiện của lớp này. Chẳng hạn, nút bấm màu đỏ trong ví dụ trên có thể tạo bằng C# code như sau: Button btn = new Button(); btn.Background = Brushes.Red; btn.Content = "Click me"; Nếu như mọi thứ có thể biểu diễn bằng XAML thì cũng có thể biểu diễn bằng đoạn mã, thì câu hỏi đặt ra là XAML có ý nghĩa gì? Câu trả lời là việc xây dựng các công cụ sinh và sử dụng các đặc tả bằng XML dễ dàng hơn nhiều so với xây dựng một công cụ tương tự làm việc với đoạn mã. Bởi vậy, XAML mở ra một cách thức tốt hơn để lập trình viên và người thiết kế làm việc với nhau. Hình 0.2 minh họa quá trình này.
Hình 0.2 – XAML hỗ trợ lập trình viên và người thiết kế làm việc chung. (nguồn http://msdn.microsoft.com/en-us/library/aa663364.aspx)
Người thiết kế có thể mô tả giao diện người dùng và tương tác với nó thông qua một công cụ, chẳng hạn như Microsoft Expression Interactive Designer. Chỉ tập trung vào việc định ra diện mạo và cảm quan cho giao diện đồ họa WPF, công cụ này sinh các đoạn mô tả giao diện thể hiện qua ngôn ngữ XAML. Lập trình viên sau đó sẽ nhập đoạn mô tả XAML đó vào môi trường lập trình, chẳng hạn như Microsoft Visual Studio.

Thay vì lập trình viên phải tái tạo lại giao diện từ đầu dựa trên một ảnh tĩnh mà người thiết kế cung cấp, bản thân các đoạn XAML này sẽ được Microsoft Visual Studio biên dịch để tái tạo thành giao diện đồ họa đúng theo mô tả. Lập trình viên chỉ tập trung vào việc viết mã trình cho giao diện được sinh ra, chẳng hạn như xử lý các sự kiện, theo những chức năng đề ra của ứng dụng.
Việc cho phép người thiết kế và lập trình viên làm việc chung như vậy sẽ hạn chế những lỗi phát sinh khi hiện thực hóa giao diện từ thiết kế. Thêm vào đó, nó còn cho phép hai nhóm công tác này làm việc song song, khiến mỗi bước lặp trong quy trình phát triển phần mềm ngắn đi và việc phản hồi được tốt hơn. Vì cả hai môi trường đều có khả năng hiểu và sử dụng XAML, ứng dụng WPF có thể chuyển qua lại giữa hai môi trường phát triển để sửa đổi hay bổ sung giao diện. Với tất cả những lợi điểm này, vai trò của người thiết kế trong việc xây dựng giao diện được đặt lên hàng đầu.

4. Công nghệ chung cho giao diện trên Windows và trên trình duyệt Web
Trong thời đại bùng nổ của Internet, các ứng dụng Web ngày một phát triển. Việc trang bị giao diện người dùng với đầy đủ tính năng như một ứng dụng desktop sẽ thu hút nhiều người sử dụng;, và do đó góp phần làm tăng giá trị doanh nghiệp. Tuy nhiên, như đã nêu trong phần đầu, với những công nghệ truyền thống, để phát triển một giao diện đồ họa vừa hoạt động trên desktop vừa trên trình duyệt Web, đòi hỏi phải sử dụng những công nghệ hoàn toàn khác nhau, giống như việc xây dựng hai giao diện hoàn toàn độc lập. Điều này tạo ra chi phí không cần thiết để phát triển giao diện.
WPF là một giải pháp cho vấn đề này. Lập trình viên có thể tạo ra một ứng dụng trình duyệt XAML (XBAP) sử dụng WPF chạy trên Internet Explore. Trên thực tế, cùng đoạn code này có thể được dùng để sinh ứng dụng WPF chạy độc lập trên Windows. Hình 0.4 minh họa một ứng dụng dịch vụ tài chính hoạt động như một ứng dụng WPF độc lập. Trong khi đó, hình 0.4 minh họa giao diện của cùng ứng dụng chạy trên Internet Explore dưới dạng XBAP.

Hình 0.3. Úng dụng WPF độc lập cung cấp dịch vụ tài chính chạy trong cửa sổ riêng. (nguồn http://msdn.microsoft.com)

Hình 0.4. Giao diện của cùng ứng dụng nêu trên dưới dạng một XBAP chạy trên Internet Explore. (nguồn http://msdn.microsoft.com)

Như đã thấy trong Hình 0.4, phần giao diện của ứng dụng dạng XBAP được trình duyệt chia thành các frame thay vì chạy trên các cửa sổ riêng, ngoài ra, các chức năng đều được bảo toàn. Cùng một đoạn mã được sử dụng chung cho cả hai trường hợp sẽ làm giảm khối lượng công việc cần thiết để phát triển hai dạng giao diện. Thêm vào đó, sử dụng cùng một đoạn mã cũng có nghĩa là sử dụng cùng kỹ năng của lập trình viên.

Do đó, lập trình viên chỉ cần có học một kiến thức chung là có thể sử dụng trong cả hai trường hợp. Một lợi điểm nữa của việc dùng chung công nghệ cho cả giao diện Windows và giao diện Web là người xây dựng ứng dụng không nhất thiết phải quyết định trước loại giao diện nào được sử dụng. Miễn là máy client đáp ứng được những yêu cầu hệ thống để chạy XBAP, một ứng dụng có thể cung cấp cả giao diện Windows và giao diện Web, mà chỉ sử dụng phần lớn những đoạn mã giống nhau.
Mỗi ứng dụng XBAP được download khi cần từ một Web server, nên nó phải tuân theo những yêu cầu về an ninh khắt khe hơn đối với một ứng dụng Windows độc lập. Theo đó, XBAP chạy trong phạm vi sandbox an ninh do hệ thống an ninh truy nhập mã của .NET Framework cung cấp. XBAP chỉ chạy với các hệ thống Windows có cài đặt WPF và chỉ với Internet Explore phiên bản 6 và 7 trở lên.
Bộ công cụ phục vụ thiết kế giao diện WPF đây
Tư liệu Microsoft VN

Chủ Nhật, 15 tháng 3, 2015


Chào các bạn, hôm nay tôi sẽ giới thiệu đến mọi người về một thư viện khác giúp chúng ta xây dựng một ứng dụng WPF với giao diện mang đậm phong cách thiết kế của Window 8 đó là giao diện Metro.






I> Tiếp cận


MahApps.Metro là một dự án được Paul Jenkins bắt đầu phát triển vào năm 2011, nhầm mang một giao diện người dùng Metro-style vào ứng dụng WPF. Kể từ đó nó được phát triển và nhận được nhiều đóng góp từ cộng đồng nguồn mở cho đến bây giờ. Đây là một dự án vì cộng đồng, do cộng đồng - không ai trong số họ được trả tiền để làm điều này, song sản phẩm mà học tạo ra thật sự đã thay đổi được nhiều thứ.


Vậy bạn có thể tải về bộ thư viện này ở đâu? Cách đơn giản nhất là dùng NuGet




[code language="c"]
Install-Package MahApps.Metro
[/code]

Hoặc có thể tải trọn bộ mã nguồn tại Github, mời các bạn xem qua một số hình ảnh






II> Ứng dụng mẫu


Trước tiên chúng ta cần tạo một project WPF và add reference các thư viện cần thiết, hãy bắt đầu với NuGet nếu bạn cảm thấy bối rối về việc tải và add reference


Xong phần add DLLs chúng ta tiến hành tải về phần Resource của thư viện này(bạn có thể tải hoặc không tùy thích nhưng để hiểu nó làm việc thể nào thì đừng bỏ qua bước này) như hình sau.


Tool -> NuGet Package Panager -> Manage NuGet Packages For Solution


Chọn và tải về MahApps.Metro.Resource; đừng quên mục "Include Prerelease:



Hoặc bạn không thể tải resource như cách trên thì có thể dùng NuGet




[code language="c"]
Install-Package MahApps.Metro -Pre
[/code]

Chạy ứng dụng xem điều gì đã sảy ra và tiếp tục sửa nội dung các file XAML, mở file "MainWindow.xaml" thêm namespace của MahApps.Metro vào trong thẻ <Window ...>




[code language="csharp"]
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
// Hoặc
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
[/code]

Tiếp theo thay thẻ Window bằng thẻ của MahApps, file hoàn thiện sẽ là như sau nhưng đừng có coppy hết vào ứng dụng của bạn mà quên đổi namespace "x:Class="WpfApplication1.MainWindow"




[code language="xml"]
<Controls:MetroWindow x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="MainWindow"
Height="350"
Width="525">

<!-- Tiếp tục code tại đây -->

</Controls:MetroWindow>
[/code]

Mở tiếp file MainWindow.xaml.cs và thay đổi lớp kế thừa của MainWindow




[code language="csharp"]
// using statements...
using MahApps.Metro.Controls

public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
}
}
[/code]

Tạm thời đã xong về cơ bản, để đẹp hơn chúng ta dùng Resource của MahApps, mở file App.xaml lên và thêm ResourceDistionary




[code language="xml"]
<Application <!--........--> >
<!--Phần cần thêm-->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="Resources/Icons.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<!--Phần cần thêm-->
</Application>
[/code]

Kết quả,

Để thêm một vài lệnh lên trên Titlebar chúng ta mở file MainWindow.xaml và thêm đoạn code dưới đây vào ngay sau thẻ <Controls:MetroWindow ... > thêm vào đây... </Controls:MetroWindow>




[code language="xml"]
<Controls:MetroWindow ... >
<Controls:MetroWindow.RightWindowCommands>
<Controls:WindowCommands>
<Button Content="Cài đặt" />
<Button>
<StackPanel Orientation="Horizontal">
<Rectangle Width="20" Height="20"
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button},
Path=Foreground}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{StaticResource appbar_windowsphone}" />
</Rectangle.OpacityMask>
</Rectangle>
<TextBlock Margin="4 0 0 0"
VerticalAlignment="Center"
Text="Phạm Tuân" />
</StackPanel>
</Button>
</Controls:WindowCommands>
</Controls:MetroWindow.RightWindowCommands>

<Grid>
</Grid>
</Controls:MetroWindow>
[/code]

Thế là xong, mời các bạn tải về tham khảo tại đây và đọc thêm tại đây
P
hạm Tuân

Thứ Năm, 5 tháng 3, 2015

Như ở bài trước tôi đã giới thiệu về caliburn.mocro, bài tiếp theo này tôi sẽ hướng dẫn các bạn về cách tạo một project WPF hợp chuẩn mô hình MVVM(*) dùng Caliburn framework. Bài này cho ta cái nhìn cơ bản về cấu trúc một Project WPF-Caliburn...


I> Hiểu MVVM Là Gì


Để tổ chức một project theo mô hình MVVM nói chung và Caliburn nói riêng chúng ta cần hiểu rõ MVVM là gì và cơ chế Mapping(View - ViewModel) mà Framework hiện tại hỗ trợ. Hai hình sau minh họa  khã rõ mô hình MVVM





Class diagram




II> Quy Ước Tổ Chức Một Project Của Caliburn




Theo quy ước của Caliburn, phần View-ViewModel-Model phải được đặt tên theo một quy ước để có thể tận dụng tối hiệu năng của framework - mà dễ thấy nhất đó là một View sẽ được tự động kết dính với một ViewModel( bạn sẽ có cơ hội cảm nhận về điều này sau) nếu bạn tuân các thủ quy ước sau.

























View ModelView
Convention<EntityName>ViewModel<EntityName>View
Example 1ShellViewModelShellView
Example 2TabViewModelTabView

Vì View trong thực tế được thể hiện như một Page nên chúng ta hoàn toàn có thể đặt tên như bảng dưới đây

























View ModelView
Convention<EntityName>PageViewModel<EntityName>Page
Example 1MainPageViewModelMainPage
Example 2OrderPageViewModelOrderPage

Nếu chúng ta có một Namespace khá dài cho các ViewModel hoặc View, chúng ta phải tuân thủ cách đặt tên sau

























View ModelView
Convention<RootNS>.<ViewModelTypeName><RootNS>.<ViewTypeName>
Example 1MyProject.ShellViewModelMyProject.ShellView
Example 2MyProject.MainPageViewModelMyProject.MainPage

Hoặc

























View ModelView
Convention<RootNS>.ViewModels.<ChildNS>.<ViewModelTypeName><RootNS>.Views.<ChildNS>.<ViewTypeName>
Example 1MyProject.ViewModels.ShellViewModelMyProject.Views.ShellView
Example 2MyProject.ViewModels.Utilities.SettingsViewModelMyPoject.Views.Utitlities.SettingsView

Dưới đây là một ví dụ về cách tổ chức một project Caliburn.Micro

caliburn project example

Hay



Tất cả những quy ước trên là bạn phải thực hiện chính xác nếu muốn tận dụng nhiều tính năng hay, song bạn hoàn toàn có thể thay đổi theo ý cá nhân vì đây là framework mã nguồn mở.

-----------------------------------------------------------------

  1. Tìm Hiểu Về Caliburn.Micro Framework Phần I

  2. Tìm Hiểu Về Caliburn.Micro Framework Phần II

Thứ Bảy, 10 tháng 1, 2015


Một khía cạnh hết sức quan trọng trong WPF đó là Trigger - Trigger giúp nắm bắt được mọi thay đổi trong một hệ thống GUI của WPF. Nhờ có Trigger một chương trình WPF sẽ trở nên gần gủi và thân thiện với người dùng hơn, trigger sẽ được kích hoạt cho mọi sự thay đổi trong trạng thái của từng Control/Element qua đó chúng ta sẽ chọn cách ứng xử phù hợp. Trigger thường được dùng trong Style và Template.




Trigger trong WPF có 3 loại:




  1. PropertyTrigger

  2. DataTrigger

  3. EventTrigger


Chúng ta sẽ tìm hiểu từng loại Trigger



I> Trigger


Là một dạng Trigger được kích hoạt khi một properry bất kỳ của Element bị thay đổi, ví dụ như trạng thái, màu sắc, kích thước. Chúng ta sẽ thử làm một ví dụ về PropertyTrigger.


Tôi sẽ tạo một TextBox, mặc định TextBox này sẽ có chữ màu Xanh; Tôi muốn màu chữ sẽ chuyển sang Xanh khi thuộc tính IsEnabled = false; Thuộc tính IsEnabled được binding đến một CheckBox.


Tôi áp dụng một Style như sau để có một TextBox mặc định, chú ý với PropertyTrigger chúng ta chỉ dùng thẻ Trigger.

tuanphamdg

[code language="xml"]
<StackPanel x:Name="LayoutRoot" Margin="0,50,0,0">
<TextBox VerticalAlignment="Center" HorizontalAlignment="Left" FontWeight="Bold"
IsEnabled="{Binding IsChecked, ElementName=checkBox, IsAsync=True}"
MinWidth="100" Margin="0" Height="24" Text="ABCD XYZ" VerticalContentAlignment="Center">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Green" />
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
[/code]

Và đây là kết quả khi tôi dùng Trigger

phạm tuân wpf

[code language="xml"]
<WrapPanel VerticalAlignment="Top" HorizontalAlignment="Center">
<TextBox VerticalAlignment="Center" HorizontalAlignment="Left" FontWeight="Bold"
IsEnabled="{Binding IsChecked, ElementName=checkBox, IsAsync=True}"
MinWidth="100" Margin="0" Height="24" Text="ABCD XYZ" VerticalContentAlignment="Center">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Green" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<CheckBox x:Name="checkBox" IsChecked="True" Content="IsEnabled for TextBox" Margin="20,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</WrapPanel>
[/code]

II> Data triggers


Khác với PropertyTrigger ở chỗ, DataTrigger sẽ kiểm soát sự thay đổi về mặt dữ liệu của các Control/Element - tức phạm vi không chỉ giới hạn ở các property mà là tất cả các dạng Data có thể binding.

Ví dụ dưới đây tôi có 2 TextBox(trên và dưới), tôi áp dụng DataTrigger cho textBox trên, TextBox này sẽ có màu chữ mặc định là Xanh, nhưng khi tôi ghi chữ "Checked" vào ô TextBox dưới thì ở TextBox trên sẽ có màu chữ là Xanh.

wpf Phạm Tuân


[code language="xml"]
<StackPanel VerticalAlignment="Top" Margin="10,0">
<TextBox VerticalAlignment="Center"
MinWidth="100" Margin="0" Text="Tiềm Hiểu Về Trigger Trong WPF" VerticalContentAlignment="Center" Height="20">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Green" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=textBox}" Value="Checked">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox x:Name="textBox" VerticalAlignment="Center"
MinWidth="100" Margin="0,10,0,0" Text="Checked" VerticalContentAlignment="Center" Height="20"/>
</StackPanel>
[/code]

III> Event triggers


EventTriggers dùng khi phải làm việc với các sự kiện của chuột, bàn phím... Tôi thường thấy mọi người dùng loại Trigger này kèm theo Storyboard(Sẽ nói về vấn đề này ở bài sau) nhằm thực thi một chuyển động, hiệu ứng màu sắc...

Sau đây tôi sẽ dùng EventTriggers để xử lý sự kiện MouseEnter, khi người dùng di chuyển chuột vào vùng TextBox, lập tức TextBox này sẽ Trigger sự kiện chuột và thực thi một hành động đó là phóng to chữ.

tuanphamdg c#

[code language="xml"]
<TextBox VerticalAlignment="Center"
MinWidth="100" Margin="0" Text="Tiềm Hiểu Về Trigger Trong WPF" VerticalContentAlignment="Center" Padding="0,0,0,1">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Green" />
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard AutoReverse="True">
<DoubleAnimation Duration="0:0:0.800" Storyboard.TargetProperty="FontSize" To="16" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
[/code]

Nếu áp dụng thành thục các Trigger trên vào Styles và Templates chắc chắn chúng ta sẽ có một ứng dụng vô cùng tiện lợi và đẹp mặt người dùng.
Chúc các bạn thành công!
Phạm Tuân

Chủ Nhật, 28 tháng 12, 2014


Đa số các chip xử lý (CPU) hiện còn đang hoạt động mà chúng ta biết đến đều có trên hai nhân(lõi) và tính năng ảo hóa - điều này làm cho các máy tính có được tốc độ xử lý nhanh hơn và khả năng đa nhiệm tốt hơn. Chính vì vậy để tận dụng hết khả năng của CPU chúng ta cần xử lý đồng thời trên nhiều lõi CPU cho nhiều bài toán khác nhau để tiết kiệm thời gian và nâng cao hiệu xuất. Trong vấn đề trên và trong lập trình ứng dụng .NET nói riêng chúng ta có thuật ngữ Thread (luồng xử lý/tiểu trình), chúng ta sẽ tìm hiểu về nó trong bài viết này.




Bạn không nên nhầm lẫn giữa process (tiến trình) và thread (tiểu trình). Process có thể hiểu là một instance của chương trình máy tính được thực thi, dựa trên hệ điều hành, hoàn toàn độc lập với các tiến trình khác. Còn thread là một nhóm lệnh được tạo ra để thực thi một tác vụ trong một process, chúng chia sẻ chung dữ liệu với nhau để xử lý, điều này là cần thiết nhưng cũng là nguyên nhân dễ gây ra lỗi nếu bạn không xử lý đúng cách. Các bạn có thể tham khảo về Class Thread tại đây




[caption id="" align="aligncenter" width="735"]tuanphamdg Mô tả sự tiết kiệm về thời gian[/caption]

Trong nội dung này tôi không nói sâu về Thread trong C#, các bạn có thể tìm hiểm thêm tại đây, tôi sẽ xoay quanh về cách Thread hoạt động với các thành phần của WPF, xong tôi sẽ vẫn nhắc các bạn nhớ về Thread và Cách tạo một Thread như sau




[code language="csharp"]
public void CreateThread()
{
Thread thread = new Thread(new ThreadStart(MethodJob));
//MethodJob có thể là một delegate

//Run thread
thread.Start();

//Buộc hoãn thực thi đến khi được phép
thread.Suspend();

//Gọi thread tiếp tục chạy
thread.Resume();

//Buộc thread phải dừng
thread.Abort();

//Đợi thread xử lý xong
thread.Join();

}

private void MethodJob()
{
for (int i = 0; i < 100; i++)
{
//Do something
}
}
[/code]

Vấn đề Thread trong WPF - Truy cập an toàn một Control
Trong winform, một Thread phụ được tạo ra không thể tự mình truy cặp được các Control của Thread chính một cách an toàn được, chúng ta sẽ phải dùng delegate, trong WPF cũng thế; Nhưng trong WPF, mỗi Control đều chứa một đối tượng là Dispatcher - đối tượng này giúp đồng bộ các truy cập đến Control hiện tại và tránh sảy ra lỗi(tương tực việc lock(object)). Vì vậy, khi muốn thực hiện một truy cập an toàn đến một Control bất kỳ chúng ta cần gọi Dispatcher của control đó và phương thức xử lý Invoke(delegate), phương thức này chứa một delegate để làm việc với các thuộc tính của Control.


Ví dụ dưới đây phục vụ nhu cầu đếm lần lược từ 0->99 và hiện số đó lên TextBox




[code language="csharp"]
private void BtnRun_OnClick(object sender, RoutedEventArgs e)
{
//thread là một biến toàn cục kiểu Thread
if (thread != null)
{
thread.Abort(); //Dừng công viện hiện tại
}

//delegate được tạo theo định nghĩa Lambeda
thread = new Thread(new ThreadStart(() =>
{
//nội dung delegate
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);

//An toàn truy cập với Dispatcher
txtData.Dispatcher.Invoke(() => txtData.Text = i.ToString());

//Vùng giới hạn an toàn rộng hơn
//this.Dispatcher.Invoke(() => txtData.Text = i.ToString());
}
}));

thread.IsBackground = true;
thread.Start();
}
[/code]

Ví dụ dưới đây phục vụ nhu cầu đếm lần lược từ x->99 và hiện số đó lên TextBox, với x là một số được truyền vào, tôi truyền vào số 10. Lưu ý tôi có hai delegate(Counter,Counters) chịu trách nhiệm hiển thị số đếm lên TextBox nhưng chúng có cách hiển thị khác nhau, các bạn hãy chú ý về cách truyền đối số cho Thread và hai delegate. Tôi gọi phương thức Invoke(delegate, object[]).




[code language="csharp"]
private void BtnRun2_OnClick(object sender, RoutedEventArgs e)
{
if (thread2 != null)
{
thread2.Abort();
}

thread2 = new Thread(new ParameterizedThreadStart(Run2));

thread2.IsBackground = true;
thread2.Start(10);
}

private delegate void Counter(int num);
private delegate void Counters(int num,int limit);

private void Run2(object numStart)
{
for (int i = (int)numStart; i < 100; i++)
{
//Cách truyền một tham số
this.Dispatcher.Invoke(new Counter(StartCount), i);

//Cách truyền nhiều tham số
this.Dispatcher.Invoke(new Counters(StartCounts), new object[] {i, 40});

Thread.Sleep(800);
}
}

private void StartCount(int num)
{
if (num < 20)
{
txtData2.Text = num.ToString();
}
else
{
txtData2.Text = "---";
}
}
private void StartCounts(int num,int limit)
{
if (num > 30 && num < limit)
{
txtData2.Text = num.ToString();
}
}
[/code]

Vấn đề Thread trong WPF - Show một Window từ Thread phụ
Nếu nhu cầu là một thread phụ được tạo ra để thực hiện một công việc nào đó và trong đó cần Show một Window thì bạn sẽ làm thế nào. Nếu bạn có thói quen dùng delegate như đoạn code bênh dưới chắc chắn rằng bạn sẽ gặp lỗi "The calling thread must be STA, because many UI components require this."




[code language="csharp"]
private void BtnRun4_OnClick(object sender, RoutedEventArgs e)
{
Thread thread5 = new Thread(new ThreadStart(ShowSubForm));
thread5.IsBackground = true;
thread5.Start();
}

private void ShowSubForm()
{
new MainWindow().Show();
}
[/code]

Vậy ta khắc phục lỗi trên như sau, cần tạo ra một Thread STA




[code language="csharp"]
private void BtnRun4_OnClick(object sender, RoutedEventArgs e)
{
Thread thread5 = new Thread(new ThreadStart(ShowSubForm));

thread5.SetApartmentState(ApartmentState.STA);
thread5.IsBackground = true;
thread5.Start();
}

private void ShowSubForm()
{
new MainWindow().Show();

//Nói rằng đây là một Window độc lập
System.Windows.Threading.Dispatcher.Run();
}
[/code]

Chúng ta còn cách khác đơn giản hơn với Dispathcher, bạn hảy thử




[code language="csharp"]
private void BtnRun4_OnClick(object sender, RoutedEventArgs e)
{
Thread thread5 = new Thread(new ThreadStart(ShowSubForm));

thread5.IsBackground = true;
thread5.Start();
}

private void ShowSubForm()
{
this.Dispatcher.Invoke(() =>
{
new MainWindow().Show();
});
}
[/code]

Vậy là chúng ta đã biết thêm về các vấn đề về Thread trong WPF, các bạn có thể tham khảo SourceCode
Chúc các bạn thành công!
Phạm Tuân

Thứ Bảy, 27 tháng 12, 2014


Ở phần trước tôi đã giới thiệu sơ qua cách hình thành một Modern UI căng bản cho ứng dụng WPF, bài tiếp theo này tôi sẽ hướng dẫn chi tiết để chúng ta có một GUI đẹp mắt và chuyên nghiệp hơn. Đồng thời tôt cũng nói về cách thêm mới và dùng Extensions trong bộ công cụ Visual Studio(VS).




Mục đích cần nắm trong bài này là:



- Cách thêm và dùng một Extensions mà ở đây sẽ là Modern UI For WPF Extension
- Cách tạo một ứng dụng với Modern UI có chức năng cơ bản như thay đổi Theme và các trang hiển thị
- Biết cách Costum theme cho cá nhân custom.

I> Thêm Modern UI For WPF Extension
Extension là một công cụ giúp chúng ta tải về một Eample để tham khảo hoặc một Template cho việc Design hay một Add-On... Chúng ta tiến tải về một số Template của Modern UI như sau: Mở VS và vào menu TOOL -> Extensions and Updates




[caption id="" align="aligncenter" width="399"]Phạm Tuân Chọn Extension and ...[/caption]

Một hợp thoại hiện ra ta chọn mục Online để tiến hành quét, Nhập vào khung Search từ "modern ui", vô số các Extensions hiện ra để chúng ta Download và cài đặt(VS sẽ tự động cài đặt và Add vào bảng New Project) hãy chọn Modern UI For WPF Templates và Download sau đó chọn Install


Phạm Tuân Đến đây chúng ta đã xong phần Template chuẩn bị cho Project Modern UI.


II> Tạo một Project với tùy chọn cơ bản


Tiếp đến chúng te sẽ new một Project, chú ý rằng hãy chọn như hình dưới để thấy được Template vừa mới Install


tuanphamdg
Có hai tùy chọn cho Project Modern UI, bạn có thể chọn một trong hai, tôi sẽ chọn mục thứ hai WPF Navigation Application vì nó đầy đủ nhất. OK bây giờ bạn thử Run ứng dụng của mình xem sao.



Việc tiếp theo là thử thêm một trang hiển thị vào MainWindow, Chọn Project -> Add -> New Item khi hộp thoại mở ra chọn mục Modern UI for WPF từ bảng bênh trái, ở  bảng giữ chọn Tamplate nào bạn muốn, tôi chọn List Page và đặt tên là ListPage1.xaml.



Tiến hành Add trang này vào MainWindow bằng cách trỏ đường dẫn đến trang này tại thẻ Link, chú ý thuộc tính ContentSource, là đường dẫn đến trang mà bạn muốn gọi đầu tiên; LogoData là hình mặt cười trên GUI và nó được Draw bằng Path.




[code language="xml"]
<mui:ModernWindow x:Class="ModernUIs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
Title="mui" IsTitleVisible="True"
LogoData="F1 M 24.9015,43.0378L 25.0963,43.4298C 26.1685,49.5853 31.5377,54.2651 38,54.2651C 44.4623,54.2651 49.8315,49.5854 50.9037,43.4299L 51.0985,43.0379C 51.0985,40.7643 52.6921,39.2955 54.9656,39.2955C 56.9428,39.2955 58.1863,41.1792 58.5833,43.0379C 57.6384,52.7654 47.9756,61.75 38,61.75C 28.0244,61.75 18.3616,52.7654 17.4167,43.0378C 17.8137,41.1792 19.0572,39.2954 21.0344,39.2954C 23.3079,39.2954 24.9015,40.7643 24.9015,43.0378 Z M 26.7727,20.5833C 29.8731,20.5833 32.3864,23.0966 32.3864,26.197C 32.3864,29.2973 29.8731,31.8106 26.7727,31.8106C 23.6724,31.8106 21.1591,29.2973 21.1591,26.197C 21.1591,23.0966 23.6724,20.5833 26.7727,20.5833 Z M 49.2273,20.5833C 52.3276,20.5833 54.8409,23.0966 54.8409,26.197C 54.8409,29.2973 52.3276,31.8106 49.2273,31.8106C 46.127,31.8106 43.6136,29.2973 43.6136,26.197C 43.6136,23.0966 46.127,20.5833 49.2273,20.5833 Z"
ContentSource="/ListPage1.xaml">

<mui:ModernWindow.MenuLinkGroups>
<mui:LinkGroup DisplayName="welcome">
<mui:LinkGroup.Links>
<mui:Link DisplayName="Cuộc sống" Source="/ListPage1.xaml" />
</mui:LinkGroup.Links>
</mui:LinkGroup>
<mui:LinkGroup DisplayName="settings" GroupKey="settings">
<mui:LinkGroup.Links>
<mui:Link DisplayName="software" Source="/Pages/SettingsPage.xaml" />
</mui:LinkGroup.Links>
</mui:LinkGroup>
</mui:ModernWindow.MenuLinkGroups>

<mui:ModernWindow.TitleLinks>
<mui:Link DisplayName="settings" Source="/Pages/SettingsPage.xaml" />
<mui:Link DisplayName="help" Source="http://mui.codeplex.com" />
</mui:ModernWindow.TitleLinks>
</mui:ModernWindow>
[/code]

Kết quả tôi nhận được


III> Costum Theme


Sau đây là bảng Theme resources đang được sử dụng, chúng ta sẽ thay đổi theo ý mình về màu sắc cho các Control nhưng phải tuân thủ theo Key sau đây, chú ý khi modify Resource phải đúng kiểu dữ liệu.























































































































































































































































































































































































































































































































































































































ResourceTypeLight defaultDark default
ButtonBackgroundBrush#FFFFFFFF#FF333333
ButtonBackgroundHoverBrush#FFDDDDDD#FF3E3E42
ButtonBackgroundPressedBrush{AccentColor}{AccentColor}
ButtonBorderBrush#FFCCCCCC#FF333333
ButtonBorderHoverBrush#FFCCCCCC#FF3E3E42
ButtonBorderPressedBrush{AccentColor}{AccentColor}
ButtonTextBrush#FF333333#FFD1D1D1
ButtonTextDisabledBrush#FFA1A1A1#FF515151
ButtonTextHoverBrush#FF333333#FFD1D1D1
ButtonTextPressedBrush#FFFFFFFF#FFD1D1D1
DataGridBackgroundBrush#00FFFFFF#00FFFFFF
DataGridCellBackgroundBrush#00FFFFFF#00FFFFFF
DataGridCellBackgroundHoverBrush#FFDDDDDD#FF3E3E42
DataGridCellBackgroundSelectedBrush{AccentColor}{AccentColor}
DataGridCellForegroundBrush#FF333333#FFD1D1D1
DataGridCellForegroundHoverBrush#FF333333#FFD1D1D1
DataGridCellForegroundSelectedBrush#FFFFFFFF#FFD1D1D1
DataGridDropSeparatorBrush{AccentColor}{AccentColor}
DataGridForegroundBrush#FF333333#FFD1D1D1
DataGridGridLinesBrush#FFCCCCCC#FF333333
DataGridHeaderBackgroundBrush#00FFFFFF#00FFFFFF
DataGridHeaderBackgroundHoverBrush#FFDDDDDD#FF3E3E42
DataGridHeaderBackgroundPressedBrush{AccentColor}{AccentColor}
DataGridHeaderBackgroundSelectedBrush{AccentColor}{AccentColor}
DataGridHeaderForegroundBrush#FF333333#FFD1D1D1
DataGridHeaderForegroundHoverBrush#FF333333#FFD1D1D1
DataGridHeaderForegroundPressedBrush#FFFFFFFF#FFD1D1D1
DataGridHeaderForegroundSelectedBrush#FFFFFFFF#FFD1D1D1
HyperlinkBrush{AccentColor}{AccentColor}
HyperlinkDisabledBrush#FF919191#FF717171
HyperlinkHoverBrush{AccentColor}{AccentColor}
InputBackgroundBrush#FFFFFFFF#FF333333
InputBackgroundHoverBrush#FFFFFFFF#FF3E3E42
InputBorderBrush#FFCCCCCC#FF333333
InputBorderHoverBrush#FFCCCCCC#FF3E3E42
InputTextBrush#FF333333#FFD1D1D1
InputTextDisabledBrush#FF919191#FF717171
InputTextHoverBrush#FF333333#FFD1D1D1
ItemBackgroundHoverBrush#FFDDDDDD#FF3E3E42
ItemBackgroundSelectedBrush{AccentColor}{AccentColor}
ItemBorderBrush#FFCCCCCC#FF3E3E42
ItemTextBrush#FF333333#FFD1D1D1
ItemTextDisabledBrush#FF919191#FF717171
ItemTextHoverBrush#FF333333#FFD1D1D1
ItemTextSelectedBrush#FFFFFFFF#FFD1D1D1
LinkButtonTextBrush#FF717171#FF717171
LinkButtonTextDisabledBrush#FF919191#FF515151
LinkButtonTextHoverBrush#FF333333#FFD1D1D1
LinkButtonTextPressedBrush#FF919191#FF515151
MenuTextBrush#66000000#FF515151
MenuTextHoverBrush#BB000000#FF919191
MenuTextSelectedBrush#FF333333#FFD1D1D1
ModernButtonBorderBrush#FF919191#FF919191
ModernButtonBorderDisabledBrush#FF919191#FF515151
ModernButtonBorderHoverBrush#FF333333#FFD1D1D1
ModernButtonBorderPressedBrush#FF333333#FFD1D1D1
ModernButtonIconBackgroundPressedBrush{AccentColor}{AccentColor}
ModernButtonIconForegroundPressedBrush#FFFFFFFF#FFD1D1D1
ModernButtonTextBrush#FF333333#FFD1D1D1
ModernButtonTextDisabledBrush#FFA1A1A1#FF515151
ModernButtonTextHoverBrush#FF333333#FFD1D1D1
ModernButtonTextPressedBrush#FF333333#FFD1D1D1
PopupBackgroundBrush#FFFFFFFF#FF252526
ProgressBackgroundBrush#FFDDDDDD#FF333333
ScrollBarBackgroundBrush#00FFFFFF#FF333333
ScrollBarThumbBackgroundBrush#00FFFFFF#00FFFFFF
ScrollBarThumbBackgroundDraggingBrush#FFCCCCCC#00FFFFFF
ScrollBarThumbBackgroundHoverBrush#FFDDDDDD#00FFFFFF
ScrollBarThumbBorderBrush#FFCCCCCC#FF333333
ScrollBarThumbForegroundBrush#00FFFFFF#FF717171
ScrollBarThumbForegroundDraggingBrush#00FFFFFF#FFD1D1D1
ScrollBarThumbForegroundHoverBrush#00FFFFFF#FF919191
SeparatorBackgroundBrush#FFCCCCCC#FF3E3E42
SliderSelectionBackgroundBrush{AccentColor}{AccentColor}
SliderSelectionBorderBrush{AccentColor}{AccentColor}
SliderThumbBackgroundBrush#FFFFFFFF#FF717171
SliderThumbBackgroundDisabledBrush#FFCCCCCC#FF333333
SliderThumbBackgroundDraggingBrush{AccentColor}{AccentColor}
SliderThumbBackgroundHoverBrush#FFDDDDDD#FF919191
SliderThumbBorderBrush#FFCCCCCC#FF717171
SliderThumbBorderDisabledBrush#FFCCCCCC#FF333333
SliderThumbBorderDraggingBrush{AccentColor}{AccentColor}
SliderThumbBorderHoverBrush#FFCCCCCC#FF919191
SliderTickBrush#FF919191#FF717171
SliderTickDisabledBrush#FFCCCCCC#FF333333
SliderTrackBackgroundBrush#FFCCCCCC#FF333333
SliderTrackBorderBrush#FFCCCCCC#FF333333
SubMenuTextBrush#99000000#FF717171
SubMenuTextHoverBrush#66000000#FF515151
SubMenuTextSelectedBrush#FF333333#FFD1D1D1
WindowBackgroundBrush#FFFFFFFF#FF252526
WindowBackgroundColorColor#FFFFFFFF#FF252526
WindowBackgroundContentUIElementRectangleRectangle
WindowBorderBrush{AccentColor}{AccentColor}
WindowBorderActiveBrush{AccentColor}{AccentColor}
WindowHeaderGradientBrushLinearGradientBrush#00FFFFFF
WindowTextBrush#FF333333#FFC1C1C1
WindowTextReadOnlyBrush#FF919191#FF717171

Tôi sẽ thử Costum lại màu nền của thẻ Link thành màu Đỏ cho Theme Light với Key như bảng trên là ItemBackgroundSelected. Tôi mở file App.xaml và thêm vào một dòng.




[code language="xml"]
<Application x:Class="ModernUIs.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--Phần tôi thêm vào bênh dưới-->
<SolidColorBrush x:Key="ItemBackgroundSelected" Color="Red" />
<!--Phần tôi thêm vào bênh trên-->
</ResourceDictionary>
</Application.Resources>
</Application>

[/code]

Kết quả tôi có thế này


tuanphamdgChúc các bạn thành công


Phạm Tuân