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 Phạm Tuân. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Phạm Tuân. Hiển thị tất cả bài đăng

Chủ Nhật, 1 tháng 2, 2015


Sau Dependency property, chúng ta sẽ tiếp tục tìm hiểu về Attached property - một loại property không thuộc đối tượng trực tiếp sử dụng nó(khác Dependency property). Dễ hiểu hơn có nghĩa là property này được định nghĩa ở một class/object khác và được dùng như một property của class/object này(na ná cách dùng của Extension methods), bởi thế mới gọi là Attached.




Bạn có thể thấy một số Attached property khi sử dụng các panel: Dock, Grid, Canvas ... Trong ví dụ dưới đây, chúng ta set giá trị DockPanel.Dock cho TextBox là "Top", xong property Dock lại không phải là một property thuộc TextBox - đó là một Attached property được định nghĩa ở DockPanel, attached property này dùng cho tất cả các control đang được chứa bởi DockPanel.




[code language="xml"]
<TextBox DockPanel.Dock=”Top”>Attached property</TextBox>
[/code]

Để tạo một attached property chúng ta phải tuân theo cấu trúc này( hãy liên tưởng đến bài Dependency property trước để có sự so sánh về cách đăng ký một attached property - không khéo là nhầm lẫn đấy)




[code language="csharp"]
public class RotationManager
{
public static propertyType Get[+ propertyName](DependencyObject obj)
{
//TODO: need to implement now
return (propertyType)obj.GetValue([propertyName+]Property);
}
public static void Set[+ propertyName](DependencyObject obj, propertyType value)
{
//TODO: need to implement now
obj.SetValue([propertyName+]Property, value);
}

public static readonly DependencyProperty [propertyName+]Property =
DependencyProperty.RegisterAttached("propertyName",
typeof(propertyType), typeof(ownerType),
new [defaultMetadata](0.5, [propertyChangedCallback]));

private static void propertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
//TODO: need to implement now
}
}
[/code]

Diễn gải:




  • propertyName: Là tên của property bạn muốn tạo ví dụ "Dock"

  • propertyType: Là Type mà attached property này nhận

  • ownerType: là Type của đối tượng chịu trách nhiệm(Owner) property này, ví dụ DockPanel

  • propertyChangedCallback: là hàm sẽ được gọi khi Value của attached property bị thay đổi

  • defaultMetadata: là một object thuộc PropertyMetadata - chỉ loại các đối tượng sẽ được thừa hưởng property này

    • FrameworkPropertyMetadata: tất cả các element trong XAML

    • UIPropertyMetadata: là các UI element/Control trong XAML




Ví dụ trực quan: Tôi có một Path, tôi xoay nó với góc 50 độ và được như hình sau

XAML thế này




[code language="xml"]
<Grid Background="White" Margin="25">
<Path Stretch="Uniform" Fill="#FF08578B" Data="F1 M 28.4583,20.8296C 28.9254,20.4138 29.2514,19.768 29.4364,18.8921C 29.6214,18.0162 29.4554,17.0984 28.9384,16.1389C 29.3694,14.16 29.0234,12.7686 27.9003,11.9646C 26.7772,11.1607 25.4592,10.7729 23.946,10.8015L 23.274,10.8015C 23.614,10.1266 23.882,9.40756 24.0781,8.64438C 24.2741,7.8812 24.3741,7.20871 24.3781,6.62686C 24.3166,4.37555 23.8036,2.70673 22.839,1.62039C 21.8744,0.534058 20.8272,-0.006073 19.6976,0C 17.6181,0.0614014 16.274,1.37863 15.6653,3.95166C 15.6406,4.27734 15.1563,5.48642 14.2124,7.57889C 13.2686,9.67136 11.8711,11.4226 10.02,12.8326L 10.02,11.6045C 9.94067,10.959 9.57507,10.5968 8.92316,10.5181L 1.0975,10.5181C 0.445374,10.5968 0.0795288,10.959 0,11.6045L 0,30.0051C 0.0795288,30.7017 0.445374,31.086 1.0975,31.1581L 8.92316,31.1581C 9.57507,31.086 9.94067,30.7017 10.02,30.0051L 10.02,27.7953C 10.7491,28.3628 12.6754,28.989 15.7988,29.6737C 18.9222,30.3584 21.8093,30.4828 24.46,30.0469C 27.1107,29.6109 28.0914,27.9957 27.4023,25.2011C 28.0253,24.7488 28.4593,24.1283 28.7043,23.3396C 28.9493,22.551 28.8674,21.7143 28.4583,20.8296 Z M 24.2341,20.7815C 25.4911,21.0257 26.0372,21.5341 25.8722,22.3068C 25.7072,23.0794 24.7771,23.5158 23.082,23.6158C 22.812,23.6399 22.68,23.748 22.686,23.9401C 22.692,24.1323 22.872,24.2644 23.226,24.3365C 24.8361,24.7918 25.4081,25.4303 24.9421,26.252C 24.4761,27.0737 23.392,27.5081 21.6899,27.5551C 20.587,27.5541 19.0097,27.388 16.9579,27.0567C 14.9062,26.7254 12.5936,25.9468 10.02,24.7208L 10.02,16.0445C 11.9097,15.114 13.6807,13.4388 15.3332,11.0186C 16.9857,8.59857 17.9977,6.34018 18.3693,4.2435C 18.4157,3.81894 18.5482,3.46432 18.7666,3.17972C 18.985,2.89514 19.2953,2.74728 19.6976,2.73615C 20.1107,2.74524 20.5296,3.09171 20.9543,3.77557C 21.3792,4.45944 21.6083,5.42606 21.6419,6.67551C 21.5589,7.68878 21.227,8.72198 20.646,9.77512C 20.0651,10.8282 19.7331,11.6585 19.6501,12.2657C 19.6491,12.525 19.7697,12.765 20.0118,12.9858C 20.254,13.2066 20.6236,13.324 21.1207,13.3381L 23.946,13.3381C 25.3041,13.4708 26.0562,14.0019 26.2022,14.9312C 26.3482,15.8606 25.8042,16.3916 24.5701,16.5244C 24.3061,16.5867 24.1741,16.7006 24.1741,16.8661C 24.1741,17.0316 24.3061,17.1356 24.5701,17.1781C 25.8192,17.3445 26.4172,17.853 26.3642,18.7038C 26.3112,19.5545 25.6012,20.0389 24.2341,20.157C 23.9221,20.177 23.7661,20.2691 23.7661,20.4332C 23.7661,20.5974 23.9221,20.7135 24.2341,20.7815 Z "
RenderTransformOrigin="0.5,0.5">

<!-- Xoay -->
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="50"/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
[/code]

Chú ý rằng tôi xoay Path trên nhờ đoạn XAML sau




[code language="xml"]
<Path Stretch="Uniform" Fill="#FF08578B" Data="............."
RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="50"/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path></pre>
<pre>[/code]

Nhưng tôi thấy nó quá dài - đến vài thẻ XAML cho một giá trị Angle="50", nếu tôi dùng phép xoay này nhiều lần trong một file XAML hoặc nhiều file XAML khác thì thật rối mắt, vậy nên tôi tạo ra một attached property thay thế đoạn XAML kia như sau




[code language="csharp"]
public class RotationManager
{
/// <summary>
/// Get giá trị của attached property
/// </summary>
/// <param name="obj">Đối tượng Dependency</param>
/// <returns>Giá trị của attached property</returns>
public static double GetAngle(DependencyObject obj)
{
return (double)obj.GetValue(AngleProperty);
}

/// <summary>
/// Set giá trị góc cho đối tượng
/// </summary>
/// <param name="obj">Đối tượng Dependency</param>
/// <param name="value">Giá trị do user input</param>
public static void SetAngle(DependencyObject obj, double value)
{
obj.SetValue(AngleProperty, value);
throw new NoNullAllowedException();
}

public static readonly DependencyProperty AngleProperty =
DependencyProperty.RegisterAttached("Angle",
typeof(double), typeof(RotationManager),
new UIPropertyMetadata(0.5, OnAngleChanged));

/// <summary>
/// CallBack khi value của property thay đổi
/// </summary>
/// <param name="obj">Đối tượng Dependency</param>
/// <param name="e"></param>
private static void OnAngleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var element = obj as UIElement;
if (element != null)
{
element.RenderTransformOrigin = new Point(.5, .5);
element.RenderTransform = new RotateTransform((double)e.NewValue);
}
}
}
[/code]

Và dùng đơn giản thế này, tôi tiến hành xoay góc 180 độ




[code language="xml"]
<Grid Background="White" Margin="25">
<Path Stretch="Uniform" Fill="#FF08578B" Data="Như củ" RenderTransformOrigin="0.5,0.5"
local:RotationManager.Angle="180"/>
</Grid>
[/code]

Hãy so sánh với đoạn code trước

[code language="xml"]
<!-- khi không dùng attached property -->
<Path Stretch="Uniform" Fill="#FF08578B" Data="............."
RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="50"/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path>

<!-- khi dùng attached property thì code chỉ thế này -->
<Path Stretch="Uniform" Fill="#FF08578B" Data="............."
RenderTransformOrigin="0.5,0.5"
local:RotationManager.Angle="180"/>
[/code]

Kết quả


Chúc mọi người 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

Thứ Năm, 25 tháng 12, 2014


Bài viết này cho ta thấy làm thế nào để có thể sử dụng một ErrorTemplate và kích hoạt một Style để cung cấp thông tin phản hồi trực quan nhằm thông báo cho người dùng khi một giá trị không hợp lệ được nhập vào, dựa trên một quy tắc xác nhận nào đó.




Chúng ta sẽ làm một ví dụ như sau: Tôi có một lớp Persional chứa thông tin một người nào đó với hai thuộc tính là Name và YearOld, tôi sẽ Binding dữ liệu vào hai TextBox. Yêu cầu khi người dùng nhập tên và tuổi vào hai TextBox kia là tên không nhỏ hơn 2 kí tự hoặc rỗng, tuổi phải lớn hơn >= 18. Nếu nhập sai sẽ có ToolTip thông báo lỗi và chuyển màu TexBox.


Tôi tiến hành tạo Style cho TextBox với những yêu cầu sau:


- Có phần Header với dữ liệu được BinDing từ Tag ( là một property) của TextBox.


- Màu nền của Header trùng với màu viền của TextBox (mặc định là Blue)


- Trigger: Nếu sảy ra một Validation.HasError (=True) thì màu nền Header và viền TextBox chuyển sang Red và có sẽ có ToolTip thông báo lỗi cụ thể.


Tôi đặc style này vào phần Window.Resources như sau.




[code language="xml"]

<Window.Resources>
<SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
<SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>
<Style x:Key="TxtStyleValidation" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid MinWidth="100" x:Name="grdHeader" Background="{TemplateBinding BorderBrush}">
<Label x:Name="lbHeader" Content="{TemplateBinding Tag}" Padding="5,0,5,2" VerticalContentAlignment="Center" Foreground="White" ></Label>
</Grid>
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" Grid.Column="1" VerticalContentAlignment="Center" Padding="5,2,2,1"/>

</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>

<!-- Phần chính-->
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Background" TargetName="grdHeader" Value="Red"/>
<Setter Property="BorderBrush" TargetName="border" Value="Red"/>
</Trigger>
<!-- Phần chính-->

</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>

[/code]

Và Phần thể hiện nội dung chính được thiết kế như sau:




[code language="xml"]
<Window x:Class="Validation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">

<!--Chèn phần style trên vào đây -->

<StackPanel x:Name="Stack" Margin="10">
<TextBox x:Name="txtName" Height="23" TextWrapping="Wrap"
Text="{Binding Name,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Tag="My Name"
VerticalAlignment="Top" Padding="5,1,2,1" Style="{DynamicResource TxtStyleValidation}"
BorderBrush="#FF0630AE" Margin="0,3,0,0"/>
<TextBox x:Name="txtYear" Height="23" TextWrapping="Wrap"
Text="{Binding YearOld, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Tag="Year-Old"
VerticalAlignment="Top" Padding="5,1,2,1" Style="{DynamicResource TxtStyleValidation}"
BorderBrush="#FF0630AE" Margin="0,3,0,0"/>
</StackPanel>
</Window>
[/code]

Kết quả cho style




[caption id="" align="aligncenter" width="525"]tuanphamdg Phạm Tuân[/caption]

Chúng ta đến phần quan trọng nhất là khởi tạo lớp Persional


Chúng ta tạo lớp Persional và phải kế thừa từ hai interface sau IDataErrorInfo và INotifyPropertyChanged. Ta có hình hài lớp này như sau (Tham khảo INotifyPropertyChanged để hiểu hơn về method OnPropertyChanged)




[code language="csharp"]
class Personal : IDataErrorInfo,INotifyPropertyChanged
{
private string _name;
private int _yearold;

public int YearOld
{
set
{
if (value != _yearold)
{
_yearold = value;
}
}
get { return _yearold; }
}
public string Name
{
set
{
value = value.Trim();
if (value !=_name)
{
_name = value;
}
}
get { return _name; }
}

// IDataErrorInfo
public string Error
{
get { throw new NotImplementedException(); }
}

public string this[string columnName]
{
get { throw new NotImplementedException(); }
}

//INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property){}
}
[/code]

Hoàn thành lớp Persional như sau




[code language="csharp"]
class Personal:IDataErrorInfo,INotifyPropertyChanged
{
private string _name;
private int _yearold;

public int YearOld
{
set
{
if (value != _yearold)
{
_yearold = value;
OnPropertyChanged("YearOld");
}
}
get { return _yearold; }
}
public string Name
{
set
{
value = value.Trim();
if (value !=_name)
{
_name = value;
OnPropertyChanged("Name");
}
}
get { return _name; }
}

//IDataErrorInfo
public string Error
{
get { throw new NotImplementedException(); }
}

public string this[string columnName]
{
get
{
string result = String.Empty;
switch (columnName)
{
case "Name":
if (String.IsNullOrEmpty(Name) || Name.Length < 2)
{
result = "Lỗi nhập liệu: tên không chính xác";
}
break;
case "YearOld":
if (YearOld < 18)
{
result = "Lỗi nhập liệu: Tuổi quá nhỏ";
}
break;
}

return result;
}
}

// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this,new PropertyChangedEventArgs(property));
}
}
}
[/code]

Hoàn thành phần View và ViewModel xong ta tiến hành một bước đơn giản sau trước khi chạy thử ứng dụng

[code language="csharp"]
public MainWindow()
{
InitializeComponent();

Stack.DataContext = new Personal() { Name = "Phạm Tuân", YearOld = 21 };
}
[/code]

Kết quả như sau




[caption id="" align="aligncenter" width="525"]tuanphamdg Phạm Tuân[/caption]

Các bạn có thể tham khảo SourceCode tại đây, Phần tiếp theo nói về ValidationRule.


Chúc các bạn thành công!
Tuân Phạm