ستونهای Dynamic در کنترل DataGrid درWPF
پنجشنبه 5 آذر 1394در این مقاله متدی را برای ارتباط چند به چند جداول در WPF توضیح خواهیم داد که می توانند در کنترل DataGrid نمایش داده شوند و اصلاح شوند. ردیف ها و ستون ها می توانند اضافه ، حذف یا اصلاح شوند.
کنترل DataGrid ابزار خوبی برای نمایش داده های ذخیره شده در یک جدول است. یک ردیف در جدول دیتابیس برابر با یک ردیف در DataGrid است . ممکن است داده ها در جدول های چندگانه ذخیره شوند ، (جدول a و جدول b) و جدول ها رابطه یک به چند داشته باشند. این نوع از داده ها می توانند در یک master-detail نشان داده شوند. نوع دیگری از ارتباطات می تواند ارتباط چند به چند باشد.
در این مقاله متدی را برای ارتباط چند به چند جداول در WPF توضیح خواهیم داد که می توانند در کنترل DataGrid نمایش داده شوند و اصلاح شوند. ردیف ها و ستون ها می توانند اضافه ، حذف یا اصلاح شوند.
در این قسمت بر روی راه حلی برای ستونهای پویا یا Dynamic تمرکز داریم . به منظور ساده سازی آن محدودیت های معماری را شکسته ایم ، و آن این است که اشیاء لایه های بالایی نمی توانند در لایه های پایین تر استفاده شوند.
مثال زیر پیاده سازی یک فرم مدیریت کاربران است که در آن کاربران ، نقش ها و انتساب ها می توانند اداره شوند. نقش ها و کاربران در دو DataGrid نمایش داده می شوند. انتساب کاربران در datagrid کاربران تخصیص داده می شود. اگرچه این grid محتوای Dynamic دارد، نمایش هر نقش به صورت ستون chechbox جداگانه ای است. انتساب نقش کاربران توسط چک کردن checkbox مربوطه انجام میشود.
مدل داده ای این مثال شامل یک جدول User ، یک جدول Role و یک جدول UserRoleکه جدول همبستگی بین دو جدول دیگر است می باشد (یک کاربر بتواند چند نقش داشته باشد).
مدل داده ای با استفاده از .NET DataSet پیاده سازی می شود که یک پایگاه داده درون حافظه ای یکپارچه است، و شامل اشاره گرهای اطلاع رسانی built-in میشود که درج ، حذف و اصلاح ردیف های داده را منتشر میکند. محتوا را می توان به یک فایل XML ذخیره کرد که به عنوان یک مکانیزم در این مثال استفاده می شود.
اجزاء و نمودار کلاس
نمودار اجزاء ، لایه بندی نرم افزار را نمایش می دهد:
Application . شامل عناصر GUI
ViewModel . شامل منطق کاری
DataModel . شامل تعریف داده ها و تداوم آنها
Application
MainWindow . : تعاریف GUI در XAML نوشته می شوند
DataGridColumnsBehavior . : یک رفتار متصل که اجازه اصلاح ستونهای متصل کنترل DataGrid را می دهد.
UserRoleValueConverter . : مقدار مبدل است که تعریف میکند زمانی که کاربر در checkbox تیک میخورد یا نه چه اتفاقی افتاده است .
ViewModel
MainViewModel . : شامل خواص جدول داده ها برای نمایش و منطق داده ای برای اداره ستون پویا
ColumnTag . : ویژگی های پیوست شده برای برچسب زدن اشیاء به موارد مشتق شده از DependencyObject ، در این نمونه DataGridColumn
DataModel
DatabaseContext . : نمونه singleton که شامل UserRoleDataSet است
UserRoleDataSet . : پیاده سازی پایگاه داده بر اساس DataSet
اتصال داده ها
این برنامه را با استفاده از الگوی MVVMمی نویسیم .
1. جدول Role به کنترل Role DataGrid متصل می شود.
2. جدول User به کنترل User DataGrid متصل می شود.
3. مجموعه ستونهای قابل مشاهده به ویژگی ستونهای کنترل User Grid متصل می شود. رفتار ستون پویا با استفاده از این ویزگی به دست می آید، زیرا منطق در ViewModel ستونها را از این مجموعه کم یا به آن اضافه میکند.
خاصیت ستونهای کنترل data grid به صورت read-only تعریف می شود، پس نمی تواند به یک ویژگی ViewModel محدود شود. DataGridColumnsBehavior یک رفتار متصل شده است که این محدودیت را پوشش می دهد.
<Window x:Class="Application.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:attachedBehaviors="clr-namespace:Application.AttachedBehaviors" xmlns:viewModel="clr-namespace:ViewModel;assembly=ViewModel" Title="User Administration" Height="350" Width="525"> <Window.DataContext> <viewModel:MainViewModel/> </Window.DataContext> <DockPanel LastChildFill="True"> <ToolBar DockPanel.Dock="Top"> <Button Content="Save" Command="{Binding SaveCommand}"/> </ToolBar> <Grid> <Grid.RowDefinitions> <RowDefinition Height="146*"/> <RowDefinition Height="147*"/> </Grid.RowDefinitions> <GroupBox x:Name="UsersGroupBox" Grid.Column="0" Header="انتساب نقش کاربر"> <DataGrid x:Name="DataGridUsers" ItemsSource="{Binding Users}" attachedBehaviors:DataGridColumnsBehavior.BindableColumns= "{Binding UserRoleColumns}" AutoGenerateColumns="False" EnableRowVirtualization="False"/> </GroupBox> <GroupBox x:Name="RolesGroupBox" Grid.Row="1" Grid.Column="0" Header="نقش ها "> <DataGrid x:Name="DataGridRoles" ItemsSource="{Binding Roles}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="نام" Binding="{Binding Name}"/> </DataGrid.Columns> </DataGrid> </GroupBox> </Grid> </DockPanel> </Window>
داده ها در سه جدول در UserRoleDataSet نگهداری می شوند. جدول های role و User توسط DataView به کنترل data grid محدود می شود. فیلترینگ و مرتب سازی می توانند در DataView تنظیم شوند. کنترل DataGrid می تواند دستکاری داده ها را با استفاده از DataView اداره کند. ردیف ها در کنترل data grid می توانند درج ، اصلاح و یا حذف شوند و جدول داده ها مستقیما از طریق DataView بروزرسانی شوند.
public class MainViewModel { public MainViewModel() { --- Code omitted --- this.UserRoleColumns = new ObservableCollection<DataGridColumn>(); --- Code omitted --- } public DataView Users { get { return this.dataContext.DataSet.User.DefaultView; } } public DataView Roles { get { return this.dataContext.DataSet.Role.DefaultView; } } public ObservableCollection<DataGridColumn> UserRoleColumns { get; private set; } }
DataSet می تواند در کنار رشته اتصال پایگاه داده برای ذخیره کردن و بازیابی داده ها از sql server و ... استفاده شود. هر جدول DataSet مجموعه ای از رویداد ها دارد که می تواند برای مطلع شدن از تغییرات داده ها استفاده شود . این مکانیزم برای اضافه کردن و حذف و ویرایش ستونهای داینامیک هنگامی که جدول role اصلاح شود استفاده می شود.
public class MainViewModel { public MainViewModel() { --- Code omitted --- this.dataContext = DatabaseContext.Instance; this.dataContext.DataSet.Role.RoleRowChanged += this.RoleOnRowChanged; this.dataContext.DataSet.Role.RoleRowDeleted += this.RoleOnRoleRowDeleted; --- Code omitted --- } private void RoleOnRowChanged(object sender, UserRoleDataSet.RoleRowChangeEvent roleRowChangeEvent) { switch (roleRowChangeEvent.Action) { case DataRowAction.Change: this.UpdateRoleColumn(roleRowChangeEvent.Row); break; case DataRowAction.Add: this.AddRoleColumn(roleRowChangeEvent.Row); break; } } private void RoleOnRoleRowDeleted(object sender, UserRoleDataSet.RoleRowChangeEvent roleRowChangeEvent) { if (roleRowChangeEvent.Action == DataRowAction.Delete) { this.DeleteRoleColumn(roleRowChangeEvent.Row); } } }
منطق کار ی
تعریف ستونهای User data grid در مجموعه UserRolesColumn ذخیره می شود. به این معنی است که ستون پیش فرض نیز (نام و نام خانوادگی کاربران ) در آن مجموعه قرار خواهد داشت . برای نام و نام خانوادگی از دو DataGridTextColumns استفاده کرده ایم.
public class MainViewModel { public MainViewModel() { this.GenerateDefaultColumns(); --- Code omitted --- } private void GenerateDefaultColumns() { this.UserRoleColumns.Add(new DataGridTextColumn { Header = "نام", Binding = new Binding("FirstName") }); this.UserRoleColumns.Add(new DataGridTextColumn { Header = "نام خانوادگی", Binding = new Binding("LastName") }); } }
تعریف ستونهای داینامیک
دسترسی به ستونهای داینامیک به سه نوع عملیات تقسیم می شود :
AddRoleColumn . : زمانی فراخوانی می شود که یک نقش به جدول Role اضافه شود. یک نمونه DataGridCheckBoxColumn . جدید ساخته می شود ، و CheckBoxColumnStyle و UserRoleValueConverter (پیاده سازی انتساب کاربر به نقش ) اختصاص می یابند.
UpdateRoleColumn . : زمانی که محتوای ردیف Role تغییر کند فراخوانی می شود.
DeleteRole . : زمانی که یک نقش از جدول Role حذف شود فراخوانی می شود.
public class MainViewModel { private void AddRoleColumn(UserRoleDataSet.RoleRow role) { var resourceDictionary = ResourceDictionaryResolver.GetResourceDictionary("Styles.xaml"); var userRoleValueConverter = resourceDictionary["UserRoleValueConverter"] as IValueConverter; var checkBoxColumnStyle = resourceDictionary["CheckBoxColumnStyle"] as Style; var binding = new Binding { Converter = userRoleValueConverter, RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridCell), 1), Path = new PropertyPath("."), Mode = BindingMode.TwoWay }; var dataGridCheckBoxColumn = new DataGridCheckBoxColumn { Header = role.Name, Binding = binding, IsThreeState = false, CanUserSort = false, ElementStyle = checkBoxColumnStyle, }; ObjectTag.SetTag(dataGridCheckBoxColumn, role); this.UserRoleColumns.Add(dataGridCheckBoxColumn); } private void UpdateRoleColumn(UserRoleDataSet.RoleRow role) { if (role != null) { foreach (var userRoleColumn in this.UserRoleColumns) { var roleScan = ColumnTag.GetTag(userRoleColumn) as UserRoleDataSet.RoleRow; if (roleScan == role) { userRoleColumn.Header = role.Name; break; } } } } private void DeleteRoleColumn(UserRoleDataSet.RoleRow role) { if (role != null) { foreach (var userRoleColumn in this.UserRoleColumns) { var roleScan = ColumnTag.GetTag(userRoleColumn) as UserRoleDataSet.RoleRow; if (roleScan == role) { this.UserRoleColumns.Remove(userRoleColumn); break; } } } } }
انتساب نقش کاربران
DataGridCheckBoxColumn چک باکس کنترل را به یک ویژگی boolean از داده ها در ردیفی که نمایش داده می شود اتصال می دهد . در مثال ما یک خاصیت boolean در ردیف داده های User خواهد بود که کاربر را به نقش انتساب می دهد . بجای اتصال به کنترل چک باکس یک مبدل مقدار نمونه سازی می شود و به DataGridCell که شامل کنترل checkbox است داده می شود. تعریف binding در متد AddRoleColumn که در بالا نشان داده شد شامل یک انتساب به این مبدل مقدار است.
مبدل مقدار ، متد Convert را هر بار که DataGrid تغییر کند فراخوانی میکند ، کاربر و نقش بازیابی می شوند و نتیجه تبدیل را باز می گردانند.
public class UserRoleValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { bool result = false; var dataGridCell = value as DataGridCell; if (dataGridCell != null) { var dataRowView = dataGridCell.DataContext as DataRowView; if (dataRowView != null) { var user = dataRowView.Row as UserRoleDataSet.UserRow; var role = ColumnTag.GetTag(dataGridCell.Column) as UserRoleDataSet.RoleRow; if (user != null && role != null) { var checkBox = dataGridCell.Content as CheckBox; if (checkBox != null) { if (dataGridCell.IsEditing) { checkBox.Checked += this.CheckBoxOnChecked; } else { checkBox.Checked -= this.CheckBoxOnChecked; } } result = DatabaseContext.Instance.DataSet.UserRole.Any( x => x.UserRow == user && x.RoleRow == role); } } } return result; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
متد CheckedBoxOnChecked زمانی فراخوانی می شود که وضعیت CheckBox تغییر کند . منطق جستجو برای چک باکس DataGridCell است و نمونه های کاربر و نقش کاربری که به آن تعلق دارد را می دهد. می تواند ورودی مربوطه User-role را بسته به وضعیت CheckBox.IsCheckedاضافه یا حذف کند .
rivate void CheckBoxOnChecked(object sender, RoutedEventArgs routedEventArgs) { var checkBox = sender as CheckBox; var dataGridCell = ControlHelper.FindVisualParent<DataGridCell>(checkBox); if (dataGridCell != null) { var dataRowView = dataGridCell.DataContext as DataRowView; if (checkBox != null && dataRowView != null) { var user = dataRowView.Row as UserRoleDataSet.UserRow; var role = ObjectTag.GetTag(dataGridCell.Column) as UserRoleDataSet.RoleRow; if (user != null && role != null) { if (checkBox.IsChecked == true && DatabaseContext.Instance.DataSet.UserRole.Any( x => x.UserRow == user && x.RoleRow == role) == false) { DatabaseContext.Instance.DataSet.UserRole.AddUserRoleRow(user, role); } else { var userRole = DatabaseContext.Instance.DataSet.UserRole.FirstOrDefault( x => x.UserRow == user && x.RoleRow == role); if (userRole != null) { userRole.Delete(); } } } } } } }
سبک CheckBox
قابلیتی اضافه کردیم که کنترل CheckBox در ردیف آیتم های جدید نشان داده نمی شود. سبک DataGridCheckBoxColumn باید تغییر کند ، و Visibility آن باید بسته به محتویات DataGridCell تنظیم شود ، اگر ردیف داده ، ردیف آیتم جدید باشد آنگاه یک NewItemPlaceHolder دارد. مبدل برای گرفتن این اطلاعات استفاده می شود. سبک Checkbox در style.xaml تعریف می شود.
پیوست کردن اشیاء Object tagging
DataColumn استاندارد در WPF اجازه برچسب زدن به اشیاء ندارد. Object tagging قابلیتی است که به اشیاء اجازه می دهد به یک کنترل پیوست داده شوند. در جایی استفاده می شود که کنترل در دسترس است اما نمی توان به شیء با استفاده از منطق استاندارد دسترسی داشت . در این مثال کنترل در دسترس CheckBox در DataGridCell خواهد بود و شیء مورد نظر Role مربوط به ستون خواهد بود . Role به ستون پیوست داده شده است و می تواند بعدا بازیابی شود. ObjectTag خودش یک DependencyProperty است که می تواند به هر نوعی از کنترل که از DependencyObject مشتق شده است پیوست داده شود .
ذخیره در دیتابیس
ذخیره در پایگاه داده را زمانی که داده ها تغییر میکنند پیاده سازی کردیم . فرمان یا Command به دکمه ذخیره در Toolbar اشاره میکند و پایگاه داده را برای تغییرات چک میکند. DataSet قابلیت built-inدارد که محتوا را برای ویرایش ها تست میکند. اگر DataSet تغییر کرده باشد دکمه فعال است در غیر اینصورت غیر فعال است . دستور CanExecuteChanged با CommandMaanager.RequerySuggested در ارتباط است . وضعیت دکمه هنگامی که برنامه در وضعیت idle است به صورت خودکار توسط متد CanExecute چک می شود.
- WPF
- 2k بازدید
- 2 تشکر